Merge "Bring the latest Caliper (r102) into vogar." into dalvik-dev
diff --git a/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java b/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
index d239a8a..8cd8cf2 100644
--- a/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
@@ -279,8 +279,7 @@
      * @return 0 if no further bytes are available. Otherwise returns 1,
      *         which suggests (but does not guarantee) that additional bytes are
      *         available.
-     * @throws IOException
-     *             If an error occurs.
+     * @throws IOException if this stream is closed or an error occurs
      */
     @Override
     public int available() throws IOException {
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java b/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
index c2af30c..0d55a10 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
@@ -383,18 +383,12 @@
         return skipped;
     }
 
-    /**
-     * Returns 0 if the {@code EOF} has been reached, otherwise returns 1.
-     *
-     * @return 0 after {@code EOF} of current entry, 1 otherwise.
-     * @throws IOException
-     *             if an IOException occurs.
-     */
     @Override
     public int available() throws IOException {
         if (closed) {
             throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
         }
+        // The InflaterInputStream contract says we must only return 0 or 1.
         return (currentEntry == null || inRead < currentEntry.size) ? 1 : 0;
     }
 
diff --git a/libcore/crypto/src/main/java/javax/crypto/CipherInputStream.java b/libcore/crypto/src/main/java/javax/crypto/CipherInputStream.java
index b2c626d..fb1ef27 100644
--- a/libcore/crypto/src/main/java/javax/crypto/CipherInputStream.java
+++ b/libcore/crypto/src/main/java/javax/crypto/CipherInputStream.java
@@ -185,13 +185,6 @@
         return i;
     }
 
-    /**
-     * Returns the number of bytes available without blocking.
-     *
-     * @return the number of bytes available, currently zero.
-     * @throws IOException
-     *             if an error occurs
-     */
     @Override
     public int available() throws IOException {
         return 0;
@@ -227,4 +220,3 @@
         return false;
     }
 }
-
diff --git a/libcore/luni/src/main/java/java/io/BufferedInputStream.java b/libcore/luni/src/main/java/java/io/BufferedInputStream.java
index 1ecae30..369e0d0 100644
--- a/libcore/luni/src/main/java/java/io/BufferedInputStream.java
+++ b/libcore/luni/src/main/java/java/io/BufferedInputStream.java
@@ -94,13 +94,13 @@
     }
 
     /**
-     * Returns the number of bytes that are available before this stream will
-     * block. This method returns the number of bytes available in the buffer
-     * plus those available in the source stream.
+     * Returns an estimated number of bytes that can be read or skipped without blocking for more
+     * input. This method returns the number of bytes available in the buffer
+     * plus those available in the source stream, but see {@link InputStream#available} for
+     * important caveats.
      *
-     * @return the number of bytes available before blocking.
-     * @throws IOException
-     *             if this stream is closed.
+     * @return the estimated number of bytes available
+     * @throws IOException if this stream is closed or an error occurs
      */
     @Override
     public synchronized int available() throws IOException {
diff --git a/libcore/luni/src/main/java/java/io/ByteArrayInputStream.java b/libcore/luni/src/main/java/java/io/ByteArrayInputStream.java
index f0b7219..9b4b32f 100644
--- a/libcore/luni/src/main/java/java/io/ByteArrayInputStream.java
+++ b/libcore/luni/src/main/java/java/io/ByteArrayInputStream.java
@@ -85,11 +85,9 @@
     }
 
     /**
-     * Returns the number of bytes that are available before this stream will
-     * block. This method returns the number of bytes yet to be read from the
-     * source byte array.
+     * Returns the number of remaining bytes.
      *
-     * @return the number of bytes available before blocking.
+     * @return {@code count - pos}
      */
     @Override
     public synchronized int available() {
diff --git a/libcore/luni/src/main/java/java/io/FileInputStream.java b/libcore/luni/src/main/java/java/io/FileInputStream.java
index 6a65395..e8aed58 100644
--- a/libcore/luni/src/main/java/java/io/FileInputStream.java
+++ b/libcore/luni/src/main/java/java/io/FileInputStream.java
@@ -134,15 +134,6 @@
         this(null == fileName ? (File) null : new File(fileName));
     }
 
-    /**
-     * Returns the number of bytes that are available before this stream will
-     * block. This method always returns the size of the file minus the current
-     * position.
-     *
-     * @return the number of bytes available before blocking.
-     * @throws IOException
-     *             if an error occurs in this stream.
-     */
     @Override
     public int available() throws IOException {
         openCheck();
diff --git a/libcore/luni/src/main/java/java/io/FilterInputStream.java b/libcore/luni/src/main/java/java/io/FilterInputStream.java
index 0baa409..9891cdb 100644
--- a/libcore/luni/src/main/java/java/io/FilterInputStream.java
+++ b/libcore/luni/src/main/java/java/io/FilterInputStream.java
@@ -46,14 +46,6 @@
         this.in = in;
     }
 
-    /**
-     * Returns the number of bytes that are available before this stream will
-     * block.
-     *
-     * @return the number of bytes available before blocking.
-     * @throws IOException
-     *             if an error occurs in this stream.
-     */
     @Override
     public int available() throws IOException {
         return in.available();
diff --git a/libcore/luni/src/main/java/java/io/InputStream.java b/libcore/luni/src/main/java/java/io/InputStream.java
index 33a5cfd..b16c5a2 100644
--- a/libcore/luni/src/main/java/java/io/InputStream.java
+++ b/libcore/luni/src/main/java/java/io/InputStream.java
@@ -48,13 +48,41 @@
     }
 
     /**
-     * Returns the number of bytes that are available before this stream will
-     * block. This implementation always returns 0. Subclasses should override
-     * and indicate the correct number of bytes available.
+     * Returns an estimated number of bytes that can be read or skipped without blocking for more
+     * input.
      *
-     * @return the number of bytes available before blocking.
-     * @throws IOException
-     *             if an error occurs in this stream.
+     * <p>Note that this method provides such a weak guarantee that it is not very useful in
+     * practice.
+     *
+     * <p>Firstly, the guarantee is "without blocking for more input" rather than "without
+     * blocking": a read may still block waiting for I/O to complete&nbsp;&mdash; the guarantee is
+     * merely that it won't have to wait indefinitely for data to be written. The result of this
+     * method should not be used as a license to do I/O on a thread that shouldn't be blocked.
+     *
+     * <p>Secondly, the result is a
+     * conservative estimate and may be significantly smaller than the actual number of bytes
+     * available. In particular, an implementation that always returns 0 would be correct.
+     * In general, callers should only use this method if they'd be satisfied with
+     * treating the result as a boolean yes or no answer to the question "is there definitely
+     * data ready?".
+     *
+     * <p>Thirdly, the fact that a given number of bytes is "available" does not guarantee that a
+     * read or skip will actually read or skip that many bytes: they may read or skip fewer.
+     *
+     * <p>It is particularly important to realize that you <i>must not</i> use this method to
+     * size a container and assume that you can read the entirety of the stream without needing
+     * to resize the container. Such callers should probably write everything they read to a
+     * {@link ByteArrayOutputStream} and convert that to a byte array. Alternatively, if you're
+     * reading from a file, {@link File#length} returns the current length of the file (though
+     * assuming the file's length can't change may be incorrect, reading a file is inherently
+     * racy).
+     *
+     * <p>The default implementation of this method in {@code InputStream} always returns 0.
+     * Subclasses should override this method if they are able to indicate the number of bytes
+     * available.
+     *
+     * @return the estimated number of bytes available
+     * @throws IOException if this stream is closed or an error occurs
      */
     public int available() throws IOException {
         return 0;
@@ -198,15 +226,16 @@
     }
 
     /**
-     * Skips at most {@code n} bytes in this stream. It does nothing and returns
-     * 0 if {@code n} is negative. Less than {@code n} characters are skipped if
-     * the end of this stream is reached before the operation completes.
-     * <p>
-     * This default implementation reads {@code n} bytes into a temporary
+     * Skips at most {@code n} bytes in this stream. This method does nothing and returns
+     * 0 if {@code n} is negative.
+     *
+     * <p>Note the "at most" in the description of this method: this method may choose to skip
+     * fewer bytes than requested. Callers should <i>always</i> check the return value.
+     *
+     * <p>This default implementation reads bytes into a temporary
      * buffer. Concrete subclasses should provide their own implementation.
      *
-     * @param n
-     *            the number of bytes to skip.
+     * @param n the number of bytes to skip.
      * @return the number of bytes actually skipped.
      * @throws IOException
      *             if this stream is closed or another IOException occurs.
diff --git a/libcore/luni/src/main/java/java/io/LineNumberInputStream.java b/libcore/luni/src/main/java/java/io/LineNumberInputStream.java
index 3df3a05..b8290cf 100644
--- a/libcore/luni/src/main/java/java/io/LineNumberInputStream.java
+++ b/libcore/luni/src/main/java/java/io/LineNumberInputStream.java
@@ -51,17 +51,12 @@
     }
 
     /**
-     * Returns the number of bytes that are available before this stream will
-     * block.
-     * <p>
-     * Note: The source stream may just be a sequence of {@code "\r\n"} bytes
+     * {@inheritDoc}
+     *
+     * <p>Note that the source stream may just be a sequence of {@code "\r\n"} bytes
      * which are converted into {@code '\n'} by this stream. Therefore,
      * {@code available} returns only {@code in.available() / 2} bytes as
      * result.
-     *
-     * @return the guaranteed number of bytes available before blocking.
-     * @throws IOException
-     *             if an error occurs in this stream.
      */
     @Override
     public int available() throws IOException {
diff --git a/libcore/luni/src/main/java/java/io/ObjectInputStream.java b/libcore/luni/src/main/java/java/io/ObjectInputStream.java
index df6d9a2..9d79641a 100644
--- a/libcore/luni/src/main/java/java/io/ObjectInputStream.java
+++ b/libcore/luni/src/main/java/java/io/ObjectInputStream.java
@@ -446,16 +446,6 @@
         primitiveData = emptyStream;
     }
 
-    /**
-     * Returns the number of bytes of primitive data that can be read from this
-     * stream without blocking. This method should not be used at any arbitrary
-     * position; just when reading primitive data types (int, char etc).
-     *
-     * @return the number of available primitive data bytes.
-     * @throws IOException
-     *             if any I/O problem occurs while computing the available
-     *             bytes.
-     */
     @Override
     public int available() throws IOException {
         // returns 0 if next data is an object, or N if reading primitive types
diff --git a/libcore/luni/src/main/java/java/io/PipedInputStream.java b/libcore/luni/src/main/java/java/io/PipedInputStream.java
index 507371b..0f4ed0a 100644
--- a/libcore/luni/src/main/java/java/io/PipedInputStream.java
+++ b/libcore/luni/src/main/java/java/io/PipedInputStream.java
@@ -137,13 +137,13 @@
     }
 
     /**
-     * Returns the number of bytes that are available before this stream will
-     * block. This implementation returns the number of bytes written to this
-     * pipe that have not been read yet.
+     * {@inheritDoc}
      *
-     * @return the number of bytes available before blocking.
-     * @throws IOException
-     *             if an error occurs in this stream.
+     * <p>Unlike most streams, {@code PipedInputStream} returns 0 rather than throwing
+     * {@code IOException} if the stream has been closed. Unconnected and broken pipes also
+     * return 0.
+     *
+     * @throws IOException if an I/O error occurs
      */
     @Override
     public synchronized int available() throws IOException {
diff --git a/libcore/luni/src/main/java/java/io/PushbackInputStream.java b/libcore/luni/src/main/java/java/io/PushbackInputStream.java
index 932a896..5a78eb9 100644
--- a/libcore/luni/src/main/java/java/io/PushbackInputStream.java
+++ b/libcore/luni/src/main/java/java/io/PushbackInputStream.java
@@ -73,16 +73,6 @@
         pos = size;
     }
 
-    /**
-     * Returns the number of bytes that are available before this stream will
-     * block. This is the sum of the bytes available in the pushback buffer and
-     * those available from the source stream.
-     *
-     * @return the number of bytes available before blocking.
-     * @throws IOException
-     *             if this stream is closed or an I/O error occurs in the source
-     *             stream.
-     */
     @Override
     public int available() throws IOException {
         if (buf == null) {
diff --git a/libcore/luni/src/main/java/java/io/SequenceInputStream.java b/libcore/luni/src/main/java/java/io/SequenceInputStream.java
index 3719021..c72e521 100644
--- a/libcore/luni/src/main/java/java/io/SequenceInputStream.java
+++ b/libcore/luni/src/main/java/java/io/SequenceInputStream.java
@@ -81,14 +81,6 @@
         }
     }
 
-    /**
-     * Returns the number of bytes that are available before the current input stream will
-     * block.
-     *
-     * @return the number of bytes available in the current input stream before blocking.
-     * @throws IOException
-     *             if an I/O error occurs in the current input stream.
-     */
     @Override
     public int available() throws IOException {
         if (e != null && in != null) {
diff --git a/libcore/luni/src/main/java/java/io/StringBufferInputStream.java b/libcore/luni/src/main/java/java/io/StringBufferInputStream.java
index 037cc60..32e89d7 100644
--- a/libcore/luni/src/main/java/java/io/StringBufferInputStream.java
+++ b/libcore/luni/src/main/java/java/io/StringBufferInputStream.java
@@ -60,12 +60,6 @@
         count = str.length();
     }
 
-    /**
-     * Returns the number of bytes that are available before this stream will
-     * block.
-     *
-     * @return the number of bytes available before blocking.
-     */
     @Override
     public synchronized int available() {
         return count - pos;
diff --git a/libcore/luni/src/main/java/java/util/ServiceLoader.java b/libcore/luni/src/main/java/java/util/ServiceLoader.java
index 2f28dff..a8e16d5 100644
--- a/libcore/luni/src/main/java/java/util/ServiceLoader.java
+++ b/libcore/luni/src/main/java/java/util/ServiceLoader.java
@@ -20,6 +20,8 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 
 /**
  * A service-provider loader.
@@ -62,6 +64,8 @@
  * 
  * <p>Note that each iteration creates new instances of the various service implementations, so
  * any heavily-used code will likely want to cache the known implementations itself and reuse them.
+ * Note also that the candidate classes are instantiated lazily as you call {@code next} on the
+ * iterator: construction of the iterator itself does not instantiate any of the providers.
  * 
  * @param <S> the service class or interface
  * @since 1.6
@@ -106,13 +110,17 @@
     }
 
     /**
-     * Constructs a service loader.
+     * Constructs a service loader. If {@code classLoader} is null, the system class loader
+     * is used.
      * 
      * @param service the service class or interface
-     * @param loader the class loader
+     * @param classLoader the class loader
      * @return a new ServiceLoader
      */
     public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader classLoader) {
+        if (classLoader == null) {
+            classLoader = ClassLoader.getSystemClassLoader();
+        }
         return new ServiceLoader<S>(service, classLoader);
     }
 
@@ -120,16 +128,12 @@
         services.clear();
         try {
             String name = "META-INF/services/" + service.getName();
-            services.addAll(Collections.list(classLoader().getResources(name)));
+            services.addAll(Collections.list(classLoader.getResources(name)));
         } catch (IOException e) {
             return;
         }
     }
 
-    private ClassLoader classLoader() {
-        return (classLoader != null) ? classLoader : ClassLoader.getSystemClassLoader();
-    }
-
     /**
      * Constructs a service loader, using the current thread's context class loader.
      * 
@@ -156,6 +160,29 @@
         return ServiceLoader.load(service, cl);
     }
 
+    /**
+     * Internal API to support built-in SPIs that check a system property first.
+     * Returns an instance specified by a property with the class' binary name, or null if
+     * no such property is set.
+     * @hide
+     */
+    public static <S> S loadFromSystemProperty(final Class<S> service) {
+        return AccessController.doPrivileged(new PrivilegedAction<S>() {
+            public S run() {
+                try {
+                    final String className = System.getProperty(service.getName());
+                    if (className != null) {
+                        Class<?> c = ClassLoader.getSystemClassLoader().loadClass(className);
+                        return (S) c.newInstance();
+                    }
+                    return null;
+                } catch (Exception e) {
+                    throw new Error(e);
+                }
+            }
+        });
+    }
+
     @Override
     public String toString() {
         return "ServiceLoader for " + service.getName();
@@ -190,11 +217,7 @@
             }
             String className = queue.remove();
             try {
-                if (classLoader == null) {
-                    return service.cast(Class.forName(className).newInstance());
-                } else {
-                    return service.cast(classLoader.loadClass(className).newInstance());
-                }
+                return service.cast(classLoader.loadClass(className).newInstance());
             } catch (Exception e) {
                 throw new ServiceConfigurationError("Couldn't instantiate class " + className, e);
             }
diff --git a/libcore/luni/src/test/java/javax/net/ssl/SSLSocketTest.java b/libcore/luni/src/test/java/javax/net/ssl/SSLSocketTest.java
index c6026d9..0e39d47 100644
--- a/libcore/luni/src/test/java/javax/net/ssl/SSLSocketTest.java
+++ b/libcore/luni/src/test/java/javax/net/ssl/SSLSocketTest.java
@@ -275,6 +275,7 @@
         });
         thread.start();
         final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+        final boolean[] handshakeCompletedListenerCalled = new boolean[1];
         client.addHandshakeCompletedListener(new HandshakeCompletedListener() {
             public void handshakeCompleted(HandshakeCompletedEvent event) {
                 try {
@@ -320,8 +321,7 @@
                         } else if (c.keyStore.isKeyEntry(alias)) {
                             assertNull(key);
                             key = c.keyStore.getKey(alias, c.keyStorePassword);
-                        }
-                        else {
+                        } else {
                             fail();
                         }
                     }
@@ -353,6 +353,7 @@
                     assertNotNull(socket);
                     assertSame(client, socket);
 
+                    handshakeCompletedListenerCalled[0] = true;
                 } catch (RuntimeException e) {
                     throw e;
                 } catch (Exception e) {
@@ -361,6 +362,7 @@
             }
         });
         client.startHandshake();
+        assertTrue(handshakeCompletedListenerCalled[0]);
         thread.join();
     }
 
diff --git a/libcore/nio/src/main/java/java/nio/channels/spi/SelectorProvider.java b/libcore/nio/src/main/java/java/nio/channels/spi/SelectorProvider.java
index 7692f2f..ab898af 100644
--- a/libcore/nio/src/main/java/java/nio/channels/spi/SelectorProvider.java
+++ b/libcore/nio/src/main/java/java/nio/channels/spi/SelectorProvider.java
@@ -17,10 +17,7 @@
 
 package java.nio.channels.spi;
 
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.URL;
 import java.nio.channels.Channel;
 import java.nio.channels.DatagramChannel;
 import java.nio.channels.Pipe;
@@ -28,8 +25,7 @@
 import java.nio.channels.SocketChannel;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.Enumeration;
-
+import java.util.ServiceLoader;
 import org.apache.harmony.luni.platform.Platform;
 import org.apache.harmony.nio.internal.SelectorProviderImpl;
 
@@ -38,19 +34,12 @@
  * providing instances of {@link DatagramChannel}, {@link Pipe},
  * {@link java.nio.channels.Selector} , {@link ServerSocketChannel}, and
  * {@link SocketChannel}. All the methods of this class are thread-safe.
- * <p>
- * A provider instance can be retrieved through a system property or the
- * configuration file in a jar file; if no provide is available that way then
+ *
+ * <p>A provider instance can be retrieved through a system property or the
+ * configuration file in a jar file; if no provider is available that way then
  * the system default provider is returned.
  */
-public abstract class SelectorProvider extends Object {
-
-    private static final String SYMBOL_COMMENT = "#"; //$NON-NLS-1$
-
-    private static final String PROVIDER_IN_SYSTEM_PROPERTY = "java.nio.channels.spi.SelectorProvider"; //$NON-NLS-1$
-
-    private static final String PROVIDER_IN_JAR_RESOURCE = "META-INF/services/java.nio.channels.spi.SelectorProvider"; //$NON-NLS-1$
-
+public abstract class SelectorProvider {
     private static SelectorProvider provider = null;
 
     private static Channel inheritedChannel;
@@ -87,103 +76,29 @@
      * @return the provider.
      */
     synchronized public static SelectorProvider provider() {
-        if (null == provider) {
-            provider = loadProviderByProperty();
-            if (null == provider) {
+        if (provider == null) {
+            provider = ServiceLoader.loadFromSystemProperty(SelectorProvider.class);
+            if (provider == null) {
                 provider = loadProviderByJar();
             }
-            if (null == provider) {
-                provider = AccessController
-                        .doPrivileged(new PrivilegedAction<SelectorProvider>() {
-                            public SelectorProvider run() {
-                                return new SelectorProviderImpl();
-                            }
-                        });
+            if (provider == null) {
+                provider = AccessController.doPrivileged(new PrivilegedAction<SelectorProvider>() {
+                    public SelectorProvider run() {
+                        return new SelectorProviderImpl();
+                    }
+                });
             }
         }
         return provider;
     }
 
-    /*
-     * load the provider in the jar file of class path.
-     */
-    static SelectorProvider loadProviderByJar() {
-        Enumeration<URL> enumeration = null;
-
-        ClassLoader classLoader = AccessController
-                .doPrivileged(new PrivilegedAction<ClassLoader>() {
-                    public ClassLoader run() {
-                        return ClassLoader.getSystemClassLoader();
-                    }
-                });
-        try {
-            enumeration = classLoader.getResources(PROVIDER_IN_JAR_RESOURCE);
-        } catch (IOException e) {
-            throw new Error(e);
-        }
-        if (null == enumeration) {
-            return null;
-        }
-        // for every jar, read until we find the provider name.
-        while (enumeration.hasMoreElements()) {
-            BufferedReader br = null;
-            String className = null;
-            try {
-                br = new BufferedReader(
-                        new InputStreamReader(enumeration.nextElement().openStream()));
-            } catch (Exception e) {
-                continue;
-            }
-            try {
-                // only the first class is loaded ,as spec says, not the same as
-                // we do before.
-                while ((className = br.readLine()) != null) {
-                    className = className.trim();
-                    int siteComment = className.indexOf(SYMBOL_COMMENT);
-                    className = (-1 == siteComment) ? className : className
-                            .substring(0, siteComment);
-                    if (0 < className.length()) {
-                        return (SelectorProvider) classLoader.loadClass(
-                                className).newInstance();
-                    }
-                }
-            } catch (Exception e) {
-                throw new Error(e);
-            } finally {
-                try {
-                    br.close();
-                } catch (IOException ioe) {
-                    // Ignore
-                }
-            }
+    private static SelectorProvider loadProviderByJar() {
+        for (SelectorProvider provider : ServiceLoader.load(SelectorProvider.class, null)) {
+            return provider;
         }
         return null;
     }
 
-    /*
-     * Load by system property.
-     */
-    static SelectorProvider loadProviderByProperty() {
-        return AccessController
-                .doPrivileged(new PrivilegedAction<SelectorProvider>() {
-                    public SelectorProvider run() {
-                        try {
-                            final String className = System
-                                    .getProperty(PROVIDER_IN_SYSTEM_PROPERTY);
-                            if (null != className) {
-                                Class<?> spClass = ClassLoader
-                                        .getSystemClassLoader().loadClass(
-                                                className);
-                                return (SelectorProvider) spClass.newInstance();
-                            }
-                            return null;
-                        } catch (Exception e) {
-                            throw new Error(e);
-                        }
-                    }
-                });
-    }
-
     /**
      * Creates a new open {@code DatagramChannel}.
      * 
diff --git a/libcore/prefs/src/main/java/java/util/prefs/Preferences.java b/libcore/prefs/src/main/java/java/util/prefs/Preferences.java
index b128858..e79d9eb 100644
--- a/libcore/prefs/src/main/java/java/util/prefs/Preferences.java
+++ b/libcore/prefs/src/main/java/java/util/prefs/Preferences.java
@@ -16,14 +16,6 @@
 
 package java.util.prefs;
 
-// BEGIN android-added
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.util.Enumeration;
-// END android-added
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -31,7 +23,7 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Locale;
-
+import java.util.ServiceLoader;
 import org.apache.harmony.prefs.internal.nls.Messages;
 
 /**
@@ -110,123 +102,24 @@
      */
     public static final int MAX_VALUE_LENGTH = 8192;
 
-    // BEGIN android-added
-    /**
-     * The name of the configuration file where preferences factory class names
-     * can be specified.
-     */
-    private static final String FACTORY_CONFIGURATION_FILE_NAME = "META-INF/services/java.util.prefs.PreferencesFactory"; //$NON-NLS-1$
-
-    /**
-     * The encoding of configuration files
-     */
-    private static final String CONFIGURATION_FILE_ENCODING = "UTF-8"; //$NON-NLS-1$
-
-    /**
-     * The comment string used in configuration files
-     */
-    private static final String CONFIGURATION_FILE_COMMENT = "#"; //$NON-NLS-1$
-
-    // END android-added
-
     //permission
-    private static final RuntimePermission PREFS_PERM = new RuntimePermission("preferences"); //$NON-NLS-1$
+    private static final RuntimePermission PREFS_PERM = new RuntimePermission("preferences");
 
     //factory used to get user/system prefs root
-    private static final PreferencesFactory factory;
+    private static final PreferencesFactory factory = findPreferencesFactory();
 
-    // BEGIN android-removed
-    // // default provider factory name for Windows
-    // private static final String DEFAULT_FACTORY_NAME_WIN = "java.util.prefs.RegistryPreferencesFactoryImpl"; //$NON-NLS-1$
-    //
-    // // default provider factory name for Unix
-    // private static final String DEFAULT_FACTORY_NAME_UNIX = "java.util.prefs.FilePreferencesFactoryImpl"; //$NON-NLS-1$
-    // END android-removed
-
-    static {
-        String factoryClassName = AccessController.doPrivileged(new PrivilegedAction<String>() {
-            public String run() {
-                return System.getProperty("java.util.prefs.PreferencesFactory"); //$NON-NLS-1$
-            }
-        });
-        // BEGIN android-removed
-        // // set default provider
-        // if (factoryClassName == null) {
-        //     String osName = AccessController.doPrivileged(new PrivilegedAction<String>() {
-        //         public String run() {
-        //             return System.getProperty("os.name"); //$NON-NLS-1$
-        //         }
-        //     });
-        //
-        //     // only comparing ASCII, so assume english locale
-        //     osName = (osName == null ? null : osName.toLowerCase(Locale.ENGLISH));
-        //
-        //     if (osName != null && osName.startsWith("windows")) {
-        //         factoryClassName = DEFAULT_FACTORY_NAME_WIN;
-        //     } else {
-        //         factoryClassName = DEFAULT_FACTORY_NAME_UNIX;
-        //     }
-        // }
-        // try {
-        //     ClassLoader loader = Thread.currentThread().getContextClassLoader();
-        //     if(loader == null){
-        //         loader = ClassLoader.getSystemClassLoader();
-        //     }
-        //     Class<?> factoryClass = loader.loadClass(factoryClassName);
-        //     factory = (PreferencesFactory) factoryClass.newInstance();
-        // } catch (Exception e) {
-        //     // prefs.10=Cannot initiate PreferencesFactory: {0}. Caused by {1}
-        //     throw new InternalError(Messages.getString("prefs.10", factoryClassName, e));   //$NON-NLS-1$
-        // }
-        // BEGIN android-added
-        ClassLoader loader = Thread.currentThread().getContextClassLoader();
-        if (loader == null) {
-            loader = ClassLoader.getSystemClassLoader();
+    private static PreferencesFactory findPreferencesFactory() {
+        // Try the system property first...
+        PreferencesFactory result = ServiceLoader.loadFromSystemProperty(PreferencesFactory.class);
+        if (result != null) {
+            return result;
         }
-        if (factoryClassName == null) {
-            Enumeration<URL> en = null;
-            try {
-                en = loader.getResources(FACTORY_CONFIGURATION_FILE_NAME);
-                BufferedReader reader = null;
-                int commentIndex = 0;
-                while (en.hasMoreElements()) {
-                    try {
-                        InputStream is = en.nextElement().openStream();
-                        // Read each line for charset provider class names
-                        reader = new BufferedReader(new InputStreamReader(is,
-                                CONFIGURATION_FILE_ENCODING));
-                        factoryClassName = reader.readLine();
-                        commentIndex = factoryClassName.indexOf(CONFIGURATION_FILE_COMMENT);
-                        if (commentIndex > 0) {
-                            factoryClassName = factoryClassName.substring(0, commentIndex).trim();
-                        }
-                        if (factoryClassName.length() > 0) {
-                            break;
-                        }
-                    } catch (IOException ex) {
-                        // ignore if a resource couldn't be read
-                    }
-                }
-            } catch (Exception e) {
-                // prefs.10=Cannot initiate PreferencesFactory: {0}. Caused by
-                // {1}
-                throw new InternalError(Messages.getString("prefs.10",
-                        FACTORY_CONFIGURATION_FILE_NAME, e)); //$NON-NLS-1$
-            }
+        // Then use ServiceLoader for META-INF/services/...
+        for (PreferencesFactory impl : ServiceLoader.load(PreferencesFactory.class, null)) {
+            return impl;
         }
-
-        if (factoryClassName == null) {
-            factoryClassName = "java.util.prefs.FilePreferencesFactoryImpl";
-        }
-
-        try {
-            Class<?> c = loader.loadClass(factoryClassName);
-            factory = (PreferencesFactory)c.newInstance();
-        } catch (Exception e) {
-            // prefs.10=Cannot initiate PreferencesFactory: {0}. Caused by {1}
-            throw new InternalError(Messages.getString("prefs.10", factoryClassName, e)); //$NON-NLS-1$
-        }
-        // END android-added
+        // Finally return a default...
+        return new FilePreferencesFactoryImpl();
     }
 
     /**
diff --git a/libcore/prefs/src/main/resources/META-INF/services/java.util.prefs.PreferencesFactory b/libcore/prefs/src/main/resources/META-INF/services/java.util.prefs.PreferencesFactory
deleted file mode 100644
index ebb514c..0000000
--- a/libcore/prefs/src/main/resources/META-INF/services/java.util.prefs.PreferencesFactory
+++ /dev/null
@@ -1 +0,0 @@
-java.util.prefs.FilePreferencesFactoryImpl
diff --git a/libcore/security/src/main/java/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java b/libcore/security/src/main/java/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java
index f0e47d9..cc175df 100644
--- a/libcore/security/src/main/java/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java
+++ b/libcore/security/src/main/java/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java
@@ -800,26 +800,17 @@
             this.inStream = inStream;
         }
 
-        /**
-         * @see java.io.InputStream#available()
-         * method documentation for more info
-         */
+        @Override
         public int available() throws IOException {
             return (bar - pos) + inStream.available();
         }
 
-        /**
-         * @see java.io.InputStream#close()
-         * method documentation for more info
-         */
+        @Override
         public void close() throws IOException {
             inStream.close();
         }
 
-        /**
-         * @see java.io.InputStream#mark(int readlimit)
-         * method documentation for more info
-         */
+        @Override
         public void mark(int readlimit) {
             if (pos < 0) {
                 pos = 0;
@@ -830,10 +821,7 @@
             }
         }
 
-        /**
-         * @see java.io.InputStream#markSupported()
-         * method documentation for more info
-         */
+        @Override
         public boolean markSupported() {
             return true;
         }
@@ -881,18 +869,12 @@
             return inStream.read();
         }
 
-        /**
-         * @see java.io.InputStream#read(byte[] b)
-         * method documentation for more info
-         */
+        @Override
         public int read(byte[] b) throws IOException {
             return read(b, 0, b.length);
         }
 
-        /**
-         * @see java.io.InputStream#read(byte[] b, int off, int len)
-         * method documentation for more info
-         */
+        @Override
         public int read(byte[] b, int off, int len) throws IOException {
             int read_b;
             int i;
@@ -905,10 +887,7 @@
             return i;
         }
 
-        /**
-         * @see java.io.InputStream#reset()
-         * method documentation for more info
-         */
+        @Override
         public void reset() throws IOException {
             if (pos >= 0) {
                 pos = (end + 1) % BUFF_SIZE;
@@ -918,10 +897,7 @@
             }
         }
 
-        /**
-         * @see java.io.InputStream#skip(long n)
-         * method documentation for more info
-         */
+        @Override
         public long skip(long n) throws IOException {
             if (pos >= 0) {
                 long i = 0;
@@ -939,6 +915,3 @@
         }
     }
 }
-
-
-
diff --git a/libcore/sqlite-jdbc/src/main/java/SQLite/Blob.java b/libcore/sqlite-jdbc/src/main/java/SQLite/Blob.java
index 3e28225..2724670 100644
--- a/libcore/sqlite-jdbc/src/main/java/SQLite/Blob.java
+++ b/libcore/sqlite-jdbc/src/main/java/SQLite/Blob.java
@@ -30,11 +30,7 @@
 	this.pos = 0;
     }
 
-    /**
-     * Return number of available bytes for reading.
-     * @return available input bytes
-     */
-
+    @Override
     public int available() throws IOException {
 	int ret = blob.size - pos;
 	return (ret < 0) ? 0 : ret;
diff --git a/libcore/tools/runner/java/vogar/DeviceDalvikVm.java b/libcore/tools/runner/java/vogar/DeviceDalvikVm.java
index fa96e82..4b44f02 100644
--- a/libcore/tools/runner/java/vogar/DeviceDalvikVm.java
+++ b/libcore/tools/runner/java/vogar/DeviceDalvikVm.java
@@ -29,9 +29,10 @@
 
     DeviceDalvikVm(Integer debugPort, File sdkJar, List<String> javacArgs,
             int monitorPort, File localTemp, List<String> additionalVmArgs,
-            boolean cleanBefore, boolean cleanAfter, File runnerDir) {
+            List<String> targetArgs, boolean cleanBefore, boolean cleanAfter,
+            File runnerDir) {
         super(new EnvironmentDevice(cleanBefore, cleanAfter, debugPort, monitorPort, localTemp,
-                runnerDir), sdkJar, javacArgs, additionalVmArgs, monitorPort);
+                runnerDir), sdkJar, javacArgs, additionalVmArgs, targetArgs, monitorPort);
     }
 
     private EnvironmentDevice getEnvironmentDevice() {
diff --git a/libcore/tools/runner/java/vogar/HostMonitor.java b/libcore/tools/runner/java/vogar/HostMonitor.java
index 9732b66..c5e84bf 100644
--- a/libcore/tools/runner/java/vogar/HostMonitor.java
+++ b/libcore/tools/runner/java/vogar/HostMonitor.java
@@ -39,8 +39,11 @@
 
     private static final Logger logger = Logger.getLogger(HostMonitor.class.getName());
 
-    private final int MAX_CONNECT_ATTEMPTS = 10;
-    private final int CONNECTION_ATTEMPT_DELAY_MILLIS = 1000;
+    private final long monitorTimeoutSeconds;
+
+    HostMonitor(long monitorTimeoutSeconds) {
+        this.monitorTimeoutSeconds = monitorTimeoutSeconds;
+    }
 
     /**
      * Connect to the target process on the given port, read all of its
@@ -65,16 +68,16 @@
                 return false;
             }
 
-            if (attempt++ == MAX_CONNECT_ATTEMPTS) {
-                logger.warning("Exceeded " + MAX_CONNECT_ATTEMPTS
+            if (attempt++ == monitorTimeoutSeconds) {
+                logger.warning("Exceeded " + monitorTimeoutSeconds
                         + " attempts to connect to localhost:" + port);
                 return false;
             }
 
             logger.fine("connection " + attempt + " to localhost:" + port
-                    + " failed; retrying in " + CONNECTION_ATTEMPT_DELAY_MILLIS + "ms");
+                    + " failed; retrying in 1s");
             try {
-                Thread.sleep(CONNECTION_ATTEMPT_DELAY_MILLIS);
+                Thread.sleep(1000);
             } catch (InterruptedException e) {
             }
         } while (true);
diff --git a/libcore/tools/runner/java/vogar/JavaVm.java b/libcore/tools/runner/java/vogar/JavaVm.java
index dcb0969..f728744 100644
--- a/libcore/tools/runner/java/vogar/JavaVm.java
+++ b/libcore/tools/runner/java/vogar/JavaVm.java
@@ -28,9 +28,9 @@
 
     JavaVm(Integer debugPort, File sdkJar, List<String> javacArgs, int monitorPort,
             File localTemp, File javaHome, List<String> additionalVmArgs,
-            boolean cleanBefore, boolean cleanAfter) {
+            List<String> targetArgs, boolean cleanBefore, boolean cleanAfter) {
         super(new EnvironmentHost(cleanBefore, cleanAfter, debugPort, localTemp),
-                sdkJar, javacArgs, additionalVmArgs, monitorPort);
+                sdkJar, javacArgs, additionalVmArgs, targetArgs, monitorPort);
         this.javaHome = javaHome;
     }
 
diff --git a/libcore/tools/runner/java/vogar/Vm.java b/libcore/tools/runner/java/vogar/Vm.java
index c69d7a2..4edb0bc 100644
--- a/libcore/tools/runner/java/vogar/Vm.java
+++ b/libcore/tools/runner/java/vogar/Vm.java
@@ -32,11 +32,13 @@
 public abstract class Vm extends Mode {
 
     protected final List<String> additionalVmArgs;
+    protected final List<String> targetArgs;
 
     Vm(Environment environment, File sdkJar, List<String> javacArgs,
-            List<String> additionalVmArgs, int monitorPort) {
+            List<String> additionalVmArgs, List<String> targetArgs, int monitorPort) {
         super(environment, sdkJar, javacArgs, monitorPort);
         this.additionalVmArgs = additionalVmArgs;
+        this.targetArgs = targetArgs;
     }
 
     /**
@@ -49,6 +51,7 @@
                 .debugPort(environment.debugPort)
                 .vmArgs(additionalVmArgs)
                 .mainClass(TestRunner.class.getName())
+                .args(targetArgs)
                 .build();
     }
 
@@ -76,6 +79,7 @@
         private PrintStream output;
         private List<String> vmCommand = Collections.singletonList("java");
         private List<String> vmArgs = new ArrayList<String>();
+        private List<String> args = new ArrayList<String>();
 
         public VmCommandBuilder vmCommand(String... vmCommand) {
             this.vmCommand = Arrays.asList(vmCommand.clone());
@@ -126,6 +130,15 @@
             return this;
         }
 
+        public VmCommandBuilder args(String... args) {
+            return args(Arrays.asList(args));
+        }
+
+        public VmCommandBuilder args(Collection<String> args) {
+            this.args.addAll(args);
+            return this;
+        }
+
         public Command build() {
             Command.Builder builder = new Command.Builder();
             builder.args(vmCommand);
@@ -146,6 +159,7 @@
 
             builder.args(vmArgs);
             builder.args(mainClass);
+            builder.args(args);
 
             builder.tee(output);
 
diff --git a/libcore/tools/runner/java/vogar/Vogar.java b/libcore/tools/runner/java/vogar/Vogar.java
index bca0a3d..2e9e3a9 100644
--- a/libcore/tools/runner/java/vogar/Vogar.java
+++ b/libcore/tools/runner/java/vogar/Vogar.java
@@ -36,6 +36,7 @@
     private static class Options {
 
         private final List<File> actionFiles = new ArrayList<File>();
+        private final List<String> targetArgs = new ArrayList<String>();
 
         @Option(names = { "--expectations" })
         private Set<File> expectationFiles = new LinkedHashSet<File>();
@@ -52,6 +53,9 @@
         @Option(names = { "--timeout" })
         private long timeoutSeconds = 10 * 60; // default is ten minutes;
 
+        @Option(names = { "--monitor-timeout" })
+        private long monitorTimeout = 10;
+
         @Option(names = { "--clean-before" })
         private boolean cleanBefore = true;
 
@@ -95,53 +99,43 @@
         private File sdkJar = new File("/home/dalvik-prebuild/android-sdk-linux/platforms/android-2.0/android.jar");
 
         private void printUsage() {
-            System.out.println("Usage: Vogar [options]... <actions>...");
+            System.out.println("Usage: Vogar [options]... <actions>... [target args]...");
             System.out.println();
-            System.out.println("  <actions>: .java files containing a jtreg tests, JUnit tests,");
-            System.out.println("      Caliper benchmarks, or a directory of such tests.");
+            System.out.println("  <actions>: .java files or directories containing jtreg tests, JUnit");
+            System.out.println("      tests, Caliper benchmarks, or executable Java classes.");
+            System.out.println();
+            System.out.println("  [args]: arguments passed to the target process. This is only useful when");
+            System.out.println("      the target process is a Caliper benchmark or main method.");
             System.out.println();
             System.out.println("GENERAL OPTIONS");
             System.out.println();
-            System.out.println("  --expectations <file>: include the specified file when looking for");
-            System.out.println("      action expectations. The file should include qualified action names");
-            System.out.println("      and the corresponding expected output.");
-            System.out.println("      Default is: " + expectationFiles);
-            System.out.println();
             System.out.println("  --mode <device|host|activity>: specify which environment to run the");
             System.out.println("      actions in. Options are on the device VM, on the host VM, and on");
             System.out.println("      device within an android.app.Activity.");
             System.out.println("      Default is: " + mode);
             System.out.println();
-            System.out.println("  --clean-before: remove working directories before building and");
-            System.out.println("      running (default). Disable with --no-clean-before if you are");
-            System.out.println("      using interactively with your own temporary input files.");
-            System.out.println();
-            System.out.println("  --clean-after: remove temporary files after running (default).");
-            System.out.println("      Disable with --no-clean-after and use with --verbose if");
-            System.out.println("      you'd like to manually re-run commands afterwards.");
-            System.out.println();
             System.out.println("  --clean: synonym for --clean-before and --clean-after (default).");
             System.out.println("      Disable with --no-clean if you want no files removed.");
             System.out.println();
-            System.out.println("  --color: format output in technicolor.");
-            System.out.println();
             System.out.println("  --stream: stream output as it is emitted.");
             System.out.println();
-            System.out.println("  --timeout-seconds <seconds>: maximum execution time of each");
-            System.out.println("      action before the runner aborts it. Specifying zero seconds");
-            System.out.println("      or using --debug will disable the execution timeout");
+            System.out.println("  --timeout <seconds>: maximum execution time of each action before the");
+            System.out.println("      runner aborts it. Specifying zero seconds or using --debug will");
+            System.out.println("      disable the execution timeout.");
             System.out.println("      Default is: " + timeoutSeconds);
             System.out.println();
             System.out.println("  --xml-reports-directory <path>: directory to emit JUnit-style");
             System.out.println("      XML test results.");
             System.out.println();
-            System.out.println("  --ident: amount to indent action result output. Can be set to ''");
-            System.out.println("      (aka empty string) to simplify output parsing.");
-            System.out.println("      Default is: '" + indent + "'");
+            System.out.println("  --sdk <android jar>: the API jar file to compile against.");
+            System.out.println("      Usually this is <SDK>/platforms/android-<X.X>/android.jar");
+            System.out.println("      where <SDK> is the path to an Android SDK path and <X.X> is");
+            System.out.println("      a release version like 1.5.");
+            System.out.println("      Default is: " + sdkJar);
             System.out.println();
             System.out.println("  --verbose: turn on verbose output");
             System.out.println();
-            System.out.println("DEVICE OPTIONS");
+            System.out.println("TARGET OPTIONS");
             System.out.println();
             System.out.println("  --debug <port>: enable Java debugging on the specified port.");
             System.out.println("      This port must be free both on the device and on the local");
@@ -151,34 +145,47 @@
             System.out.println("      on-device temporary files and code.");
             System.out.println("      Default is: " + deviceRunnerDir);
             System.out.println();
-            System.out.println("GENERAL VM OPTIONS");
-            System.out.println();
             System.out.println("  --vm-arg <argument>: include the specified argument when spawning a");
             System.out.println("      virtual machine. Examples: -Xint:fast, -ea, -Xmx16M");
             System.out.println();
-            System.out.println("HOST VM OPTIONS");
-            System.out.println();
             System.out.println("  --java-home <java_home>: execute the actions on the local workstation");
             System.out.println("      using the specified java home directory. This does not impact");
             System.out.println("      which javac gets used. When unset, java is used from the PATH.");
             System.out.println();
-            System.out.println("COMPILE OPTIONS");
+            System.out.println("EXOTIC OPTIONS");
             System.out.println();
-            System.out.println("  --sdk <android jar>: the API jar file to compile against.");
-            System.out.println("      Usually this is <SDK>/platforms/android-<X.X>/android.jar");
-            System.out.println("      where <SDK> is the path to an Android SDK path and <X.X> is");
-            System.out.println("      a release version like 1.5.");
-            System.out.println("      Default is: " + sdkJar);
+            System.out.println("  --clean-before: remove working directories before building and");
+            System.out.println("      running (default). Disable with --no-clean-before if you are");
+            System.out.println("      using interactively with your own temporary input files.");
+            System.out.println();
+            System.out.println("  --clean-after: remove temporary files after running (default).");
+            System.out.println("      Disable with --no-clean-after and use with --verbose if");
+            System.out.println("      you'd like to manually re-run commands afterwards.");
+            System.out.println();
+            System.out.println("  --color: format output in technicolor.");
+            System.out.println();
+            System.out.println("  --expectations <file>: include the specified file when looking for");
+            System.out.println("      action expectations. The file should include qualified action names");
+            System.out.println("      and the corresponding expected output.");
+            System.out.println("      Default is: " + expectationFiles);
+            System.out.println();
+            System.out.println("  --ident: amount to indent action result output. Can be set to ''");
+            System.out.println("      (aka empty string) to simplify output parsing.");
+            System.out.println("      Default is: '" + indent + "'");
             System.out.println();
             System.out.println("  --javac-arg <argument>: include the specified argument when invoking");
             System.out.println("      javac. Examples: --javac-arg -Xmaxerrs --javac-arg 1");
             System.out.println();
+            System.out.println("  --monitor-timeout <seconds>: number of seconds to wait for the target");
+            System.out.println("      process to launch. This can be used to prevent connection failures");
+            System.out.println("      when dexopt is slow.");
+            System.out.println();
         }
 
         private boolean parseArgs(String[] args) {
-            final List<String> actionFilenames;
+            List<String> actionsAndTargetArgs;
             try {
-                actionFilenames = new OptionParser(this).parse(args);
+                actionsAndTargetArgs = new OptionParser(this).parse(args);
             } catch (RuntimeException e) {
                 System.out.println(e.getMessage());
                 return false;
@@ -236,11 +243,6 @@
                 return false;
             }
 
-            if (actionFilenames.isEmpty()) {
-                System.out.println("No actions provided.");
-                return false;
-            }
-
             if (!clean) {
                 cleanBefore = false;
                 cleanAfter = false;
@@ -255,13 +257,35 @@
                 timeoutSeconds = 0;
             }
 
-            for (String actionFilename : actionFilenames) {
-                actionFiles.add(new File(actionFilename));
+            // separate the actions and the target args
+            boolean action = true;
+            for (String arg : actionsAndTargetArgs) {
+                if (arg.equals("--")) {
+                    action = false;
+                    continue;
+                }
+
+                File actionFile = new File(arg);
+                if (action && actionFile.exists()) {
+                    actionFiles.add(actionFile);
+                } else {
+                    targetArgs.add(arg);
+                    action = false;
+                }
+            }
+
+            if (actionFiles.isEmpty()) {
+                System.out.println("No actions provided.");
+                return false;
+            }
+
+            if (!targetArgs.isEmpty() && mode.equals(Options.MODE_ACTIVITY)) {
+                System.out.println("Target args not supported with --mode activity");
+                return false;
             }
 
             return true;
         }
-
     }
 
     private final Options options = new Options();
@@ -284,6 +308,7 @@
                     monitorPort,
                     localTemp,
                     options.vmArgs,
+                    options.targetArgs,
                     options.cleanBefore,
                     options.cleanAfter,
                     options.deviceRunnerDir);
@@ -297,6 +322,7 @@
                     localTemp,
                     options.javaHome,
                     options.vmArgs,
+                    options.targetArgs,
                     options.cleanBefore,
                     options.cleanAfter
             );
@@ -316,7 +342,7 @@
             return;
         }
 
-        HostMonitor monitor = new HostMonitor();
+        HostMonitor monitor = new HostMonitor(options.monitorTimeout);
 
         List<CodeFinder> codeFinders = Arrays.asList(
                 new JtregFinder(localTemp),
diff --git a/libcore/tools/runner/java/vogar/target/CaliperRunner.java b/libcore/tools/runner/java/vogar/target/CaliperRunner.java
index 5b424c8..3336716 100644
--- a/libcore/tools/runner/java/vogar/target/CaliperRunner.java
+++ b/libcore/tools/runner/java/vogar/target/CaliperRunner.java
@@ -32,10 +32,10 @@
         this.monitor = monitor;
     }
 
-    public void run(String actionName, Class<?> testClass) {
+    public void run(String actionName, Class<?> testClass, String[] args) {
         monitor.outcomeStarted(actionName, actionName);
         try {
-            Runner.main(testClass.asSubclass(Benchmark.class), new String[0]);
+            Runner.main(testClass.asSubclass(Benchmark.class), args);
         } catch (Exception ex) {
             ex.printStackTrace();
         }
diff --git a/libcore/tools/runner/java/vogar/target/JUnitRunner.java b/libcore/tools/runner/java/vogar/target/JUnitRunner.java
index 00c61f8..f15cad3 100644
--- a/libcore/tools/runner/java/vogar/target/JUnitRunner.java
+++ b/libcore/tools/runner/java/vogar/target/JUnitRunner.java
@@ -57,7 +57,7 @@
         this.junitTest = testRunner.getTest(testClass.getName());
     }
 
-    public void run(String actionName, Class<?> testClass) {
+    public void run(String actionName, Class<?> testClass, String[] args) {
         testRunner.doRun(junitTest);
     }
 
diff --git a/libcore/tools/runner/java/vogar/target/JtregRunner.java b/libcore/tools/runner/java/vogar/target/JtregRunner.java
index 6deeef9..47090c5 100644
--- a/libcore/tools/runner/java/vogar/target/JtregRunner.java
+++ b/libcore/tools/runner/java/vogar/target/JtregRunner.java
@@ -37,10 +37,10 @@
         }
     }
 
-    public void run(String actionName, Class<?> testClass) {
+    public void run(String actionName, Class<?> testClass, String[] args) {
         monitor.outcomeStarted(actionName, actionName);
         try {
-            main.invoke(null, new Object[] { new String[0] });
+            main.invoke(null, new Object[] { args });
             monitor.outcomeFinished(Result.SUCCESS);
         } catch (Throwable failure) {
             failure.printStackTrace();
diff --git a/libcore/tools/runner/java/vogar/target/MainRunner.java b/libcore/tools/runner/java/vogar/target/MainRunner.java
index cc7dccd..d4e5dec 100644
--- a/libcore/tools/runner/java/vogar/target/MainRunner.java
+++ b/libcore/tools/runner/java/vogar/target/MainRunner.java
@@ -37,10 +37,10 @@
         }
     }
 
-    public void run(String actionName, Class<?> testClass) {
+    public void run(String actionName, Class<?> testClass, String[] args) {
         monitor.outcomeStarted(actionName, actionName);
         try {
-            main.invoke(null, new Object[] { new String[0] });
+            main.invoke(null, new Object[] { args });
         } catch (Throwable ex) {
             ex.printStackTrace();
         }
diff --git a/libcore/tools/runner/java/vogar/target/Runner.java b/libcore/tools/runner/java/vogar/target/Runner.java
index af98a00..a06ba1a 100644
--- a/libcore/tools/runner/java/vogar/target/Runner.java
+++ b/libcore/tools/runner/java/vogar/target/Runner.java
@@ -25,5 +25,5 @@
     public void init(TargetMonitor monitor, String actionName,
             Class<?> testClass);
 
-    public void run(String actionName, Class<?> testClass);
+    public void run(String actionName, Class<?> testClass, String[] args);
 }
diff --git a/libcore/tools/runner/java/vogar/target/TestRunner.java b/libcore/tools/runner/java/vogar/target/TestRunner.java
index 1e1f479..739cbc5 100644
--- a/libcore/tools/runner/java/vogar/target/TestRunner.java
+++ b/libcore/tools/runner/java/vogar/target/TestRunner.java
@@ -75,7 +75,7 @@
         }
     }
 
-    public void run() {
+    public void run(String[] args) {
         final TargetMonitor monitor = new TargetMonitor();
         monitor.await(monitorPort);
 
@@ -97,7 +97,7 @@
             throw new RuntimeException(e);
         }
         runner.init(monitor, qualifiedName, testClass);
-        runner.run(qualifiedName, testClass);
+        runner.run(qualifiedName, testClass, args);
 
         monitor.close();
     }
@@ -105,9 +105,6 @@
 
 
     public static void main(String[] args) {
-        if (args.length != 0) {
-            throw new RuntimeException("TestRunner doesn't take arguments");
-        }
-        new TestRunner().run();
+        new TestRunner().run(args);
     }
 }
diff --git a/libcore/tools/runner/lib/TestActivity.java b/libcore/tools/runner/lib/TestActivity.java
index 6e6d09f..39a6575 100644
--- a/libcore/tools/runner/lib/TestActivity.java
+++ b/libcore/tools/runner/lib/TestActivity.java
@@ -48,7 +48,7 @@
                 1, Threads.daemonThreadFactory());
         executor.submit(new Runnable() {
             public void run() {
-                new TestRunner().run();
+                new TestRunner().run(args);
             }
         });
         executor.shutdown();
diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java
index d9db099..b75c218 100644
--- a/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java
+++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java
@@ -23,6 +23,7 @@
 import java.security.PrivilegedAction;
 import java.security.Security;
 // BEGIN android-added
+import java.util.logging.Level;
 import java.util.logging.Logger;
 // END android-added
 
@@ -48,7 +49,7 @@
     public static synchronized SocketFactory getDefault() {
         if (defaultSocketFactory != null) {
             // BEGIN android-added
-            log("SSLSocketFactory", "Using factory " + defaultSocketFactory);
+            // log("SSLSocketFactory", "Using factory " + defaultSocketFactory, null);
             // END android-added
             return defaultSocketFactory;
         }
@@ -65,6 +66,9 @@
                             final Class<?> sfc = Class.forName(defaultName, true, cl);
                             defaultSocketFactory = (SocketFactory) sfc.newInstance();
                         } catch (Exception e) {
+                            // BEGIN android-added
+                            log("SSLSocketFactory", "Problem creating " + defaultName, e);
+                            // END android-added
                         }
                     }
                     return null;
@@ -83,16 +87,16 @@
             // Use internal implementation
             defaultSocketFactory = new DefaultSSLSocketFactory("No SSLSocketFactory installed");
         }
-            // BEGIN android-added
-            log("SSLSocketFactory", "Using factory " + defaultSocketFactory);
-            // END android-added
+        // BEGIN android-added
+        // log("SSLSocketFactory", "Using factory " + defaultSocketFactory, null);
+        // END android-added
         return defaultSocketFactory;
     }
 
     // BEGIN android-added
     @SuppressWarnings("unchecked")
-    private static void log(String tag, String msg) {
-        Logger.getLogger(tag).info(msg);
+    private static void log(String tag, String msg, Throwable throwable) {
+        Logger.getLogger(tag).log(Level.INFO, msg, throwable);
     }
     // END android-added
 
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java
index a95d38f..b780943 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java
@@ -35,6 +35,7 @@
     volatile int timeout;
 
     final SSLParameters parameters;
+    final int sslCtxNativePointer;
 
     /** Identifies OpenSSL sessions. */
     static final int OPEN_SSL = 1;
@@ -46,9 +47,10 @@
      * @param maximumSize of cache
      * @param timeout for cache entries
      */
-    AbstractSessionContext(SSLParameters parameters, int maximumSize,
-            int timeout) {
+    AbstractSessionContext(SSLParameters parameters, int sslCtxNativePointer,
+            int maximumSize, int timeout) {
         this.parameters = parameters;
+        this.sslCtxNativePointer = sslCtxNativePointer;
         this.maximumSize = maximumSize;
         this.timeout = timeout;
     }
@@ -181,11 +183,20 @@
         }
     }
 
+    /**
+     * Puts an SSLSession in the AbstractSessionContext cache
+     */
+    abstract void putSession(SSLSession session);
+
     static void log(Throwable t) {
         java.util.logging.Logger.global.log(Level.WARNING,
                 "Error converting session.", t);
     }
 
+    protected void finalize() throws IOException {
+        NativeCrypto.SSL_CTX_free(sslCtxNativePointer);
+    }
+
     /**
      * Byte array wrapper. Implements equals() and hashCode().
      */
@@ -209,4 +220,4 @@
             return Arrays.equals(bytes, other.bytes);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java
index 7b27787..7246c4d 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java
@@ -55,7 +55,7 @@
      */
     X500Principal[] certificate_authorities;
 
-    //Requested certificate types as Strings
+    // Requested certificate types as Strings
     // ("RSA", "DSA", "DH_RSA" or "DH_DSA")
     private String[] types;
 
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java
index 2c8738f..338fc39 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java
@@ -64,8 +64,9 @@
     final SSLClientSessionCache persistentCache;
 
     public ClientSessionContext(SSLParameters parameters,
+            int sslCtxNativePointer,
             SSLClientSessionCache persistentCache) {
-        super(parameters, 10, 0);
+        super(parameters, sslCtxNativePointer, 10, 0);
         this.persistentCache = persistentCache;
     }
 
@@ -144,9 +145,9 @@
      * Adds the given session to the ID-based index if the index has already
      * been initialized.
      */
-    private void indexById(SSLSession session) {
+    private void indexById(byte[] id, SSLSession session) {
         if (sessionsById != null) {
-            sessionsById.put(new ByteArray(session.getId()), session);
+            sessionsById.put(new ByteArray(id), session);
         }
     }
 
@@ -173,7 +174,7 @@
                 if (session != null) {
                     synchronized (sessions) {
                         sessions.put(new HostAndPort(host, port), session);
-                        indexById(session);
+                        indexById(session.getId(), session);
                     }
                     return session;
                 }
@@ -183,12 +184,17 @@
         return null;
     }
 
+    @Override
     void putSession(SSLSession session) {
+        byte[] id = session.getId();
+        if (id.length == 0) {
+            return;
+        }
         HostAndPort key = new HostAndPort(session.getPeerHost(),
                 session.getPeerPort());
         synchronized (sessions) {
             sessions.put(key, session);
-            indexById(session);
+            indexById(id, session);
         }
 
         // TODO: This in a background thread.
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java
index 33b0a45..083a342 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java
@@ -115,6 +115,8 @@
                 put("KeyManagerFactory.X509", KeyManagerFactoryImpl.class.getName());
                 put("TrustManagerFactory.X509", TrustManagerFactoryImpl.class.getName());
                 // BEGIN android-added
+                put("SSLContext.SSL", SSLContextImpl.class.getName());
+                put("Alg.Alias.SSLContext.SSLv3", "SSL");
                 put("MessageDigest.SHA-1", "org.apache.harmony.xnet.provider.jsse.OpenSSLMessageDigestJDK$SHA1");
                 put("Alg.Alias.MessageDigest.SHA1", "SHA-1");
                 put("Alg.Alias.MessageDigest.SHA", "SHA-1");
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index 7698035..ad6ae15 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -16,6 +16,15 @@
 
 package org.apache.harmony.xnet.provider.jsse;
 
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.openssl.PEMWriter;
+
 /**
  * Provides the Java side of our JNI glue for OpenSSL. Currently only hashing
  * and verifying are covered. Is expected to grow over time. Also needs to move
@@ -23,31 +32,31 @@
  */
 public class NativeCrypto {
 
+    // --- OpenSSL library initialization --------------------------------------
     static {
-        // Need to ensure that OpenSSL initialization is done exactly once.
-        // This can be cleaned up later, when all OpenSSL glue moves into its
-        // own libcore module. Make it run, make it nice.
-        OpenSSLSocketImpl.class.getClass();
+        clinit();
     }
 
+    private native static void clinit();
+
     // --- DSA/RSA public/private key handling functions -----------------------
-    
+
     public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] priv_key, byte[] pub_key);
 
     public static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
-    
+
     public static native void EVP_PKEY_free(int pkey);
-  
+
     // --- General context handling functions (despite the names) --------------
-    
+
     public static native int EVP_new();
-    
+
     public static native void EVP_free(int ctx);
-    
+
     // --- Digest handling functions -------------------------------------------
-    
+
     public static native void EVP_DigestInit(int ctx, String algorithm);
-    
+
     public static native void EVP_DigestUpdate(int ctx, byte[] buffer, int offset, int length);
 
     public static native int EVP_DigestFinal(int ctx, byte[] hash, int offset);
@@ -55,13 +64,186 @@
     public static native int EVP_DigestSize(int ctx);
 
     public static native int EVP_DigestBlockSize(int ctx);
-    
+
     // --- Signature handling functions ----------------------------------------
-    
+
     public static native void EVP_VerifyInit(int ctx, String algorithm);
-    
+
     public static native void EVP_VerifyUpdate(int ctx, byte[] buffer, int offset, int length);
-    
+
     public static native int EVP_VerifyFinal(int ctx, byte[] signature, int offset, int length, int key);
-    
+
+    // --- SSL handling --------------------------------------------------------
+
+    private static final String SUPPORTED_PROTOCOL_SSLV3 = "SSLv3";
+    private static final String SUPPORTED_PROTOCOL_TLSV1 = "TLSv1";
+
+    public static long SSL_OP_NO_SSLv3 = 0x02000000L;
+    public static long SSL_OP_NO_TLSv1 = 0x04000000L;
+
+    public static native int SSL_CTX_new();
+
+    public static native String[] SSL_CTX_get_ciphers(int ssl_ctx);
+
+    public static String[] getDefaultCipherSuites() {
+        int ssl_ctx = SSL_CTX_new();
+        String[] supportedCiphers = SSL_CTX_get_ciphers(ssl_ctx);
+        SSL_CTX_free(ssl_ctx);
+        return supportedCiphers;
+    }
+
+    public static String[] getSupportedCipherSuites() {
+        // TODO really return full cipher list
+        return getDefaultCipherSuites();
+    }
+
+    public static native void SSL_CTX_free(int ssl_ctx);
+
+    public static native int SSL_new(int ssl_ctx, String privatekey, String certificate, byte[] seed) throws IOException;
+
+    /**
+     * Initialize the SSL socket and set the certificates for the
+     * future handshaking.
+     */
+    public static int SSL_new(SSLParameters sslParameters) throws IOException {
+        boolean client = sslParameters.getUseClientMode();
+
+        final int ssl_ctx = (client) ?
+            sslParameters.getClientSessionContext().sslCtxNativePointer :
+            sslParameters.getServerSessionContext().sslCtxNativePointer;
+
+        // TODO support more than RSA certificates?  non-openssl
+        // SSLEngine implementation did these callbacks during
+        // handshake after selecting cipher suite, not before
+        // handshake.
+        final String alias = (client) ?
+            sslParameters.getKeyManager().chooseClientAlias(new String[] { "RSA" }, null, null) :
+            sslParameters.getKeyManager().chooseServerAlias("RSA", null, null);
+
+        final String privateKeyString;
+        final String certificateString;
+        if (alias == null) {
+            privateKeyString = null;
+            certificateString = null;
+        } else {
+            PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
+            X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
+
+            ByteArrayOutputStream privateKeyOS = new ByteArrayOutputStream();
+            PEMWriter privateKeyPEMWriter = new PEMWriter(new OutputStreamWriter(privateKeyOS));
+            privateKeyPEMWriter.writeObject(privateKey);
+            privateKeyPEMWriter.close();
+            privateKeyString = privateKeyOS.toString();
+
+            ByteArrayOutputStream certificateOS = new ByteArrayOutputStream();
+            PEMWriter certificateWriter = new PEMWriter(new OutputStreamWriter(certificateOS));
+
+            for (X509Certificate certificate : certificates) {
+                certificateWriter.writeObject(certificate);
+            }
+            certificateWriter.close();
+            certificateString = certificateOS.toString();
+        }
+
+        final byte[] seed = (sslParameters.getSecureRandomMember() != null) ?
+            sslParameters.getSecureRandomMember().generateSeed(1024) :
+            null;
+
+        return SSL_new(ssl_ctx,
+                       privateKeyString,
+                       certificateString,
+                       seed);
+    }
+
+
+    public static native long SSL_get_options(int ssl);
+
+    public static native long SSL_set_options(int ssl, long options);
+
+    public static String[] getSupportedProtocols() {
+        return new String[] { SUPPORTED_PROTOCOL_SSLV3, SUPPORTED_PROTOCOL_TLSV1 };
+    }
+
+    public static String[] getEnabledProtocols(int ssl) {
+        long options = SSL_get_options(ssl);
+        ArrayList<String> array = new ArrayList<String>();
+        if ((options & NativeCrypto.SSL_OP_NO_SSLv3) == 0) {
+            array.add(SUPPORTED_PROTOCOL_SSLV3);
+        }
+        if ((options & NativeCrypto.SSL_OP_NO_TLSv1) == 0) {
+            array.add(SUPPORTED_PROTOCOL_TLSV1);
+        }
+        return array.toArray(new String[array.size()]);
+    }
+
+    public static void setEnabledProtocols(int ssl, String[] protocols) {
+        if (protocols == null) {
+            throw new IllegalArgumentException("Provided parameter is null");
+        }
+        // openssl uses negative logic letting you disable protocols.
+        // so first, lets turn them all off, and in the loop selectively enable
+        long options = SSL_get_options(ssl);
+        options |= (SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1);
+        for (int i = 0; i < protocols.length; i++) {
+            if (protocols[i].equals(SUPPORTED_PROTOCOL_SSLV3)) {
+                options ^= SSL_OP_NO_SSLv3;
+            } else if (protocols[i].equals(SUPPORTED_PROTOCOL_TLSV1)) {
+                options ^= SSL_OP_NO_TLSv1;
+            } else {
+                throw new IllegalArgumentException("Protocol " + protocols[i] +
+                                                   " is not supported");
+            }
+        }
+        SSL_set_options(ssl, options);
+    }
+
+    public static native String[] SSL_get_ciphers(int ssl);
+
+    public static native void SSL_set_cipher_list(int ssl, String ciphers);
+
+    public static void setEnabledCipherSuites(int ssl, String[] suites) {
+        if (suites == null) {
+            throw new IllegalArgumentException("Provided parameter is null");
+        }
+
+        // makes sure all suites are valid, throwing on error
+        String[] supportedCipherSuites = getSupportedCipherSuites();
+        for (String suite : suites) {
+            findSuite(supportedCipherSuites, suite);
+        }
+
+        String controlString = "";
+        for (int i = 0; i < suites.length; i++) {
+            if (i == 0) {
+                controlString = suites[i];
+            } else {
+                controlString += ":" + suites[i];
+            }
+        }
+        SSL_set_cipher_list(ssl, controlString);
+    }
+
+    private static void findSuite(String[] supportedCipherSuites, String suite) {
+        for(int i = 0; i < supportedCipherSuites.length; i++) {
+            if (supportedCipherSuites[i].equals(suite)) {
+                return;
+            }
+        }
+        throw new IllegalArgumentException("Protocol " + suite + " is not supported.");
+    }
+
+    public static native void SSL_free(int ssl);
+
+    public interface CertificateChainVerifier {
+        /**
+         * Verify that we trust the certificate chain is trusted.
+         *
+         * @param bytes An array of certficates in byte form
+         *
+         * @throws AlertException if the certificate is untrusted
+         * @return false if there are other problems verifying the certificate chain
+         */
+        // TODO throw on error in all cases instead of returning false
+        public boolean verifyCertificateChain(byte[][] bytes);
+    }
 }
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java
index 970f5dc..f342457 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java
@@ -39,16 +39,16 @@
     }
 
     public OpenSSLServerSocketFactoryImpl(SSLParameters sslParameters) {
-        this.sslParameters = sslParameters;
+        this.sslParameters = (SSLParameters) sslParameters.clone();
+        this.sslParameters.setUseClientMode(false);
     }
 
     public String[] getDefaultCipherSuites() {
-        // TODO There might be a better way to implement this...
-        return OpenSSLServerSocketImpl.nativegetsupportedciphersuites();
+        return NativeCrypto.getDefaultCipherSuites();
     }
 
     public String[] getSupportedCipherSuites() {
-        return OpenSSLServerSocketImpl.nativegetsupportedciphersuites();
+        return NativeCrypto.getSupportedCipherSuites();
     }
 
     public ServerSocket createServerSocket() throws IOException {
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
index d52f08f..c79dccf 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
@@ -16,98 +16,46 @@
 
 package org.apache.harmony.xnet.provider.jsse;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.OutputStreamWriter;
 import java.net.InetAddress;
 import java.net.Socket;
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-
-import org.bouncycastle.openssl.PEMWriter;
 
 /**
  * OpenSSL-based implementation of server sockets.
- *  
+ *
  * This class only supports SSLv3 and TLSv1. This should be documented elsewhere
  * later, for example in the package.html or a separate reference document.
- */ 
+ */
 public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
-    private int ssl_ctx;
-    private boolean client_mode = true;
-    private long ssl_op_no = 0x00000000L;
-    private SSLParameters sslParameters;
-    private static final String[] supportedProtocols = new String[] {
-        "SSLv3",
-        "TLSv1"
-        };
-
-    private native static void nativeinitstatic();
-
-    static {
-        nativeinitstatic();
-    }
-
-    private native void nativeinit(String privatekey, String certificate, byte[] seed);
-
-    /**
-     * Initialize the SSL server socket and set the certificates for the
-     * future handshaking.
-     */
-    private void init() throws IOException {
-        String alias = sslParameters.getKeyManager().chooseServerAlias("RSA", null, null);
-        if (alias == null) {
-            throw new IOException("No suitable certificates found");
-        }
-
-        PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
-        X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
-
-        ByteArrayOutputStream privateKeyOS = new ByteArrayOutputStream();
-        PEMWriter privateKeyPEMWriter = new PEMWriter(new OutputStreamWriter(privateKeyOS));
-        privateKeyPEMWriter.writeObject(privateKey);
-        privateKeyPEMWriter.close();
-
-        ByteArrayOutputStream certificateOS = new ByteArrayOutputStream();
-        PEMWriter certificateWriter = new PEMWriter(new OutputStreamWriter(certificateOS));
-
-        for (int i = 0; i < certificates.length; i++) {
-            certificateWriter.writeObject(certificates[i]);
-        }
-        certificateWriter.close();
-
-        nativeinit(privateKeyOS.toString(), certificateOS.toString(),
-                sslParameters.getSecureRandomMember() != null ?
-                sslParameters.getSecureRandomMember().generateSeed(1024) : null);
-    }
+    private final SSLParameters sslParameters;
+    private int sslNativePointer;
 
     protected OpenSSLServerSocketImpl(SSLParameters sslParameters)
         throws IOException {
         super();
         this.sslParameters = sslParameters;
-        init();
+        this.sslNativePointer = NativeCrypto.SSL_new(sslParameters);
     }
 
     protected OpenSSLServerSocketImpl(int port, SSLParameters sslParameters)
         throws IOException {
         super(port);
         this.sslParameters = sslParameters;
-        init();
+        this.sslNativePointer = NativeCrypto.SSL_new(sslParameters);
     }
 
     protected OpenSSLServerSocketImpl(int port, int backlog, SSLParameters sslParameters)
         throws IOException {
         super(port, backlog);
         this.sslParameters = sslParameters;
-        init();
+        this.sslNativePointer = NativeCrypto.SSL_new(sslParameters);
     }
 
     protected OpenSSLServerSocketImpl(int port, int backlog, InetAddress iAddress, SSLParameters sslParameters)
         throws IOException {
         super(port, backlog, iAddress);
         this.sslParameters = sslParameters;
-        init();
+        this.sslNativePointer = NativeCrypto.SSL_new(sslParameters);
     }
 
     @Override
@@ -127,78 +75,41 @@
      */
     @Override
     public String[] getSupportedProtocols() {
-        return supportedProtocols.clone();
+        return NativeCrypto.getSupportedProtocols();
     }
 
     /**
-     * See the OpenSSL ssl.h header file for more information.
-     */
-    static private long SSL_OP_NO_SSLv3 = 0x02000000L;
-    static private long SSL_OP_NO_TLSv1 = 0x04000000L;
-
-    /**
      * The names of the protocols' versions that in use on this SSL connection.
-     * 
+     *
      * @return an array of protocols names
      */
     @Override
     public String[] getEnabledProtocols() {
-        ArrayList<String> array = new ArrayList<String>();
-
-        if ((ssl_op_no & SSL_OP_NO_SSLv3) == 0x00000000L) {
-            array.add(supportedProtocols[0]);
-        }
-        if ((ssl_op_no & SSL_OP_NO_TLSv1) == 0x00000000L) {
-            array.add(supportedProtocols[1]);
-        }
-        return array.toArray(new String[array.size()]);
+        return NativeCrypto.getEnabledProtocols(sslNativePointer);
     }
 
-    private native void nativesetenabledprotocols(long l);
-
     /**
      * This method enables the protocols' versions listed by
      * getSupportedProtocols().
-     * 
+     *
      * @param protocols names of all the protocols to enable.
-     * 
+     *
      * @throws IllegalArgumentException when one or more of the names in the
      *             array are not supported, or when the array is null.
      */
     @Override
     public void setEnabledProtocols(String[] protocols) {
-        if (protocols == null) {
-            throw new IllegalArgumentException("Provided parameter is null");
-        }
-
-        ssl_op_no  = SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
-
-        for (int i = 0; i < protocols.length; i++) {
-            if (protocols[i].equals("SSLv3"))
-                ssl_op_no ^= SSL_OP_NO_SSLv3;
-            else if (protocols[i].equals("TLSv1"))
-                ssl_op_no ^= SSL_OP_NO_TLSv1;
-            else throw new IllegalArgumentException("Protocol " + protocols[i] +
-            " is not supported.");
-        }
-
-        nativesetenabledprotocols(ssl_op_no);
+        NativeCrypto.setEnabledProtocols(sslNativePointer, protocols);
     }
 
-    /**
-     * Gets all available ciphers from the current OpenSSL library.
-     * Needed by OpenSSLServerSocketFactory too.
-     */
-    static native String[] nativegetsupportedciphersuites();
-
     @Override
     public String[] getSupportedCipherSuites() {
-        return nativegetsupportedciphersuites();
+        return NativeCrypto.getSupportedCipherSuites();
     }
 
     @Override
     public String[] getEnabledCipherSuites() {
-        return OpenSSLSocketImpl.nativeGetEnabledCipherSuites(ssl_ctx);
+        return NativeCrypto.SSL_get_ciphers(sslNativePointer);
     }
 
     /**
@@ -211,7 +122,7 @@
      */
     @Override
     public void setEnabledCipherSuites(String[] suites) {
-        OpenSSLSocketImpl.setEnabledCipherSuites(ssl_ctx, suites);
+        NativeCrypto.setEnabledCipherSuites(sslNativePointer, suites);
     }
 
     /**
@@ -223,10 +134,10 @@
     static private int SSL_VERIFY_CLIENT_ONCE =          0x04;
 
     /**
-     * Calls the SSL_CTX_set_verify(...) OpenSSL function with the passed int
+     * Calls the SSL_set_verify(...) OpenSSL function with the passed int
      * value.
      */
-    private native void nativesetclientauth(int value);
+    private static native void nativesetclientauth(int sslNativePointer, int value);
 
     private void setClientAuth() {
         int value = SSL_VERIFY_NONE;
@@ -237,7 +148,7 @@
             value |= SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE;
         }
 
-        nativesetclientauth(value);
+        nativesetclientauth(sslNativePointer, value);
     }
 
     @Override
@@ -274,20 +185,13 @@
 
     @Override
     public Socket accept() throws IOException {
-        OpenSSLSocketImpl socket
-                = new OpenSSLSocketImpl(sslParameters, ssl_op_no);
+        OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters, null);
         implAccept(socket);
-        socket.accept(ssl_ctx, client_mode);
-
+        socket.accept(sslNativePointer);
         return socket;
     }
 
     /**
-     * Removes OpenSSL objects from memory.
-     */
-    private native void nativefree();
-
-    /**
      * Unbinds the port if the socket is open.
      */
     @Override
@@ -297,7 +201,10 @@
 
     @Override
     public synchronized void close() throws IOException {
-        nativefree();
+        if (sslNativePointer != 0) {
+            NativeCrypto.SSL_free(sslNativePointer);
+            sslNativePointer = 0;
+        }
         super.close();
     }
 }
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
index 17cd088..2a3908c 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
@@ -41,7 +41,7 @@
  * Implementation of the class OpenSSLSessionImpl
  * based on OpenSSL. The JNI native interface for some methods
  * of this this class are defined in the file:
- * org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp
+ * org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
  */
 public class OpenSSLSessionImpl implements SSLSession {
 
@@ -52,11 +52,11 @@
     private boolean isValid = true;
     private TwoKeyHashMap values = new TwoKeyHashMap();
     private javax.security.cert.X509Certificate[] peerCertificateChain;
-    protected int session;
+    protected int sslSessionNativePointer;
     private SSLParameters sslParameters;
     private String peerHost;
     private int peerPort;
-    private final SSLSessionContext sessionContext;
+    private final AbstractSessionContext sessionContext;
 
     /**
      * Class constructor creates an SSL session context given the appropriate
@@ -65,9 +65,9 @@
      * @param session the Identifier for SSL session
      * @param sslParameters the SSL parameters like ciphers' suites etc.
      */
-    protected OpenSSLSessionImpl(int session, SSLParameters sslParameters,
-            String peerHost, int peerPort, SSLSessionContext sessionContext) {
-        this.session = session;
+    protected OpenSSLSessionImpl(int sslSessionNativePointer, SSLParameters sslParameters,
+            String peerHost, int peerPort, AbstractSessionContext sessionContext) {
+        this.sslSessionNativePointer = sslSessionNativePointer;
         this.sslParameters = sslParameters;
         this.peerHost = peerHost;
         this.peerPort = peerPort;
@@ -75,51 +75,58 @@
     }
 
     /**
-     * Constructs a session from a byte[].
+     * Constructs a session from a byte[] containing DER data. This
+     * allows loading the saved session.
+     * @throws IOException
      */
     OpenSSLSessionImpl(byte[] derData, SSLParameters sslParameters,
             String peerHost, int peerPort,
             javax.security.cert.X509Certificate[] peerCertificateChain,
-            SSLSessionContext sessionContext)
+            AbstractSessionContext sessionContext)
             throws IOException {
-        this.sslParameters = sslParameters;
-        this.peerHost = peerHost;
-        this.peerPort = peerPort;
+        this(initializeNativeImpl(derData, derData.length),
+             sslParameters,
+             peerHost,
+             peerPort,
+             sessionContext);
         this.peerCertificateChain = peerCertificateChain;
-        this.sessionContext = sessionContext;
-        initializeNative(derData);
+        // TODO move this check into native code so we can throw an error with more information
+        if (this.sslSessionNativePointer == 0) {
+            throw new IOException("Invalid session data");
+        }
     }
 
+    private static native int initializeNativeImpl(byte[] data, int size);
+
     /**
      * Gets the identifier of the actual SSL session
      * @return array of sessions' identifiers.
      */
-    public native byte[] getId();
+    public byte[] getId() {
+        return getId(sslSessionNativePointer);
+    }
+
+    private static native byte[] getId(int sslSessionNativePointer);
 
     /**
      * Get the session object in DER format. This allows saving the session
      * data or sharing it with other processes.  
      */
-    native byte[] getEncoded();
-
-    /**
-     * Init the underlying native object from DER data. This 
-     * allows loading the saved session.
-     * @throws IOException 
-     */
-    private void initializeNative(byte[] derData) throws IOException {
-        this.session = initializeNativeImpl(derData, derData.length);
-        if (this.session == 0) {
-            throw new IOException("Invalid session data");
-        }
+    byte[] getEncoded() {
+        return getEncoded(sslSessionNativePointer);
     }
-    private native int initializeNativeImpl(byte[] data, int size);
-    
+
+    private native static byte[] getEncoded(int sslSessionNativePointer);
+
     /**
      * Gets the creation time of the SSL session.
      * @return the session's creation time in milliseconds since the epoch
      */
-    public native long getCreationTime();
+    public long getCreationTime() {
+        return getCreationTime(sslSessionNativePointer);
+    }
+
+    private static native long getCreationTime(int sslSessionNativePointer);
 
     /**
      * Gives the last time this concrete SSL session was accessed. Accessing
@@ -184,7 +191,8 @@
     /**
      * Returns the X509 certificates of the peer in the PEM format.
      */
-    private native byte[][] getPeerCertificatesImpl();
+    private static native byte[][] getPeerCertificatesImpl(int sslCtxNativePointer,
+                                                           int sslSessionNativePointer);
 
     /**
      * Gives the certificate(s) of the peer in this SSL session
@@ -201,7 +209,7 @@
     public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
         if (peerCertificateChain == null) {
             try {
-                byte[][] bytes = getPeerCertificatesImpl();
+                byte[][] bytes = getPeerCertificatesImpl(sessionContext.sslCtxNativePointer, sslSessionNativePointer);
                 if (bytes == null) throw new SSLPeerUnverifiedException("No certificate available");
 
                 peerCertificateChain = new javax.security.cert.X509Certificate[bytes.length];
@@ -303,7 +311,11 @@
      * @return an identifier for all the cryptographic algorithms used in the
      *         actual SSL session.
      */
-    public native String getCipherSuite();
+    public String getCipherSuite() {
+        return getCipherSuite(sslSessionNativePointer);
+    }
+
+    private static native String getCipherSuite(int sslSessionNativePointer);
 
     /**
      * Gives back the standard version name of the SSL protocol used in all
@@ -313,7 +325,11 @@
      *         connections pertaining to this SSL session.
      *
      */
-    public native String getProtocol();
+    public String getProtocol() {
+        return getProtocol(sslSessionNativePointer);
+    }
+
+    private static native String getProtocol(int sslSessionNativePointer);
 
     /**
      * Gives back the context to which the actual SSL session is bound. A SSL
@@ -457,10 +473,8 @@
     }
 
     protected void finalize() {
-        synchronized (OpenSSLSocketImpl.class) {
-            freeImpl(session);
-        }
+        freeImpl(sslSessionNativePointer);
     }
 
-    private native void freeImpl(int session);
+    private static native void freeImpl(int session);
 }
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java
index aeb23b6..7b6d7c8 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java
@@ -46,12 +46,11 @@
     }
 
     public String[] getDefaultCipherSuites() {
-        // TODO There might be a better implementation for this...
-        return OpenSSLSocketImpl.nativegetsupportedciphersuites();
+        return NativeCrypto.getDefaultCipherSuites();
     }
 
     public String[] getSupportedCipherSuites() {
-        return OpenSSLSocketImpl.nativegetsupportedciphersuites();
+        return NativeCrypto.getSupportedCipherSuites();
     }
 
     public Socket createSocket() throws IOException {
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
index 8006757..58d2110 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
@@ -16,7 +16,6 @@
 
 package org.apache.harmony.xnet.provider.jsse;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -25,7 +24,6 @@
 import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.SocketException;
-import java.security.PrivateKey;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.security.interfaces.RSAPublicKey;
@@ -41,43 +39,37 @@
 import javax.net.ssl.SSLSession;
 
 import org.apache.harmony.security.provider.cert.X509CertImpl;
-import org.bouncycastle.openssl.PEMWriter;
 
 /**
  * Implementation of the class OpenSSLSocketImpl
  * based on OpenSSL. The JNI native interface for some methods
  * of this this class are defined in the file:
- * org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
- * 
+ * org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
+ *
  * This class only supports SSLv3 and TLSv1. This should be documented elsewhere
- * later, for example in the package.html or a separate reference document. 
+ * later, for example in the package.html or a separate reference document.
  */
-public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket {
-    private int ssl_ctx;
-    private int ssl;
+public class OpenSSLSocketImpl
+        extends javax.net.ssl.SSLSocket
+        implements NativeCrypto.CertificateChainVerifier {
+    private int sslNativePointer;
     private InputStream is;
     private OutputStream os;
     private final Object handshakeLock = new Object();
-    private Object readLock = new Object();
-    private Object writeLock = new Object();
+    private final Object readLock = new Object();
+    private final Object writeLock = new Object();
     private SSLParameters sslParameters;
     private OpenSSLSessionImpl sslSession;
     private Socket socket;
     private boolean autoClose;
     private boolean handshakeStarted = false;
     private ArrayList<HandshakeCompletedListener> listeners;
-    private long ssl_op_no = 0x00000000L;
     private int timeout = 0;
     // BEGIN android-added
     private int handshakeTimeout = -1;  // -1 = same as timeout; 0 = infinite
     // END android-added
     private InetSocketAddress address;
 
-    private static final String[] supportedProtocols = new String[] {
-        "SSLv3",
-        "TLSv1"
-        };
-
     private static final AtomicInteger instanceCount = new AtomicInteger(0);
 
     public static int getInstanceCount() {
@@ -89,66 +81,6 @@
     }
 
     /**
-     * Initialize OpenSSL library.
-     */
-    private native static void nativeinitstatic();
-
-    static {
-        nativeinitstatic();
-    }
-
-    private native void nativeinit(String privatekey, String certificate, byte[] seed);
-
-    /**
-     * Initialize the SSL socket and set the certificates for the
-     * future handshaking.
-     */
-    private void init() throws IOException {
-        String alias = sslParameters.getKeyManager().chooseClientAlias(new String[] { "RSA" }, null, null);
-        if (alias != null) {
-            PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
-            X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
-
-            ByteArrayOutputStream privateKeyOS = new ByteArrayOutputStream();
-            PEMWriter privateKeyPEMWriter = new PEMWriter(new OutputStreamWriter(privateKeyOS));
-            privateKeyPEMWriter.writeObject(privateKey);
-            privateKeyPEMWriter.close();
-
-            ByteArrayOutputStream certificateOS = new ByteArrayOutputStream();
-            PEMWriter certificateWriter = new PEMWriter(new OutputStreamWriter(certificateOS));
-
-            for (int i = 0; i < certificates.length; i++) {
-                certificateWriter.writeObject(certificates[i]);
-            }
-            certificateWriter.close();
-
-            nativeinit(privateKeyOS.toString(), certificateOS.toString(),
-                    sslParameters.getSecureRandomMember() != null ?
-                    sslParameters.getSecureRandomMember().generateSeed(1024) : null);
-        } else {
-            nativeinit(null, null,
-                    sslParameters.getSecureRandomMember() != null ?
-                    sslParameters.getSecureRandomMember().generateSeed(1024) : null);
-        }
-    }
-
-    /**
-     * Class constructor with 2 parameters
-     *
-     * @param sslParameters Parameters for the SSL
-     *            context
-     * @param ssl_op_no Parameter to set the enabled
-     *            protocols
-     * @throws IOException if network fails
-     */
-    protected OpenSSLSocketImpl(SSLParameters sslParameters, long ssl_op_no) throws IOException {
-        super();
-        this.sslParameters = sslParameters;
-        this.ssl_op_no = ssl_op_no;
-        updateInstanceCount(1);
-    }
-
-    /**
      * Class constructor with 1 parameter
      *
      * @param sslParameters Parameters for the SSL
@@ -157,9 +89,7 @@
      */
     protected OpenSSLSocketImpl(SSLParameters sslParameters) throws IOException {
         super();
-        this.sslParameters = sslParameters;
-        init();
-        updateInstanceCount(1);
+        init(sslParameters);
     }
 
     /**
@@ -172,12 +102,9 @@
             SSLParameters sslParameters)
         throws IOException {
         super(host, port);
-        this.sslParameters = sslParameters;
-        init();
-        updateInstanceCount(1);
+        init(sslParameters);
     }
 
-
     /**
      * Class constructor with 3 parameters: 1st is InetAddress
      *
@@ -188,9 +115,7 @@
             SSLParameters sslParameters)
         throws IOException {
         super(address, port);
-        this.sslParameters = sslParameters;
-        init();
-        updateInstanceCount(1);
+        init(sslParameters);
     }
 
 
@@ -204,9 +129,7 @@
             int clientPort, SSLParameters sslParameters)
         throws IOException {
         super(host, port, clientAddress, clientPort);
-        this.sslParameters = sslParameters;
-        init();
-        updateInstanceCount(1);
+        init(sslParameters);
     }
 
     /**
@@ -219,9 +142,7 @@
             InetAddress clientAddress, int clientPort, SSLParameters sslParameters)
         throws IOException {
         super(address, port, clientAddress, clientPort);
-        this.sslParameters = sslParameters;
-        init();
-        updateInstanceCount(1);
+        init(sslParameters);
     }
 
     /**
@@ -237,8 +158,28 @@
         this.timeout = socket.getSoTimeout();
         this.address = new InetSocketAddress(host, port);
         this.autoClose = autoClose;
+        init(sslParameters);
+    }
+
+    /**
+     * Initialize the SSL socket and set the certificates for the
+     * future handshaking.
+     */
+    private void init(SSLParameters sslParameters) throws IOException {
         this.sslParameters = sslParameters;
-        init();
+        this.sslNativePointer = NativeCrypto.SSL_new(sslParameters);
+        updateInstanceCount(1);
+    }
+
+    /**
+     * Construct a OpenSSLSocketImpl from an SSLParameters and an
+     * existing SSL native pointer. Used for transitioning accepting
+     * the OpenSSLSocketImpl within OpenSSLServerSocketImpl.
+     */
+    protected OpenSSLSocketImpl(SSLParameters sslParameters,
+                                OpenSSLServerSocketImpl dummy) {
+        super();
+        this.sslParameters = (SSLParameters) sslParameters.clone();
         updateInstanceCount(1);
     }
 
@@ -246,9 +187,9 @@
      * Adds OpenSSL functionality to the existing socket and starts the SSL
      * handshaking.
      */
-    private native boolean nativeconnect(int ctx, Socket sock, boolean client_mode, int sslsession) throws IOException;
-    private native int nativegetsslsession(int ssl);
-    private native String nativecipherauthenticationmethod();
+    private native boolean nativeconnect(int sslNativePointer, Socket sock, int timeout, boolean client_mode, int sslSessionNativePointer) throws IOException;
+    private native int nativegetsslsession(int sslNativePointer);
+    private native String nativecipherauthenticationmethod(int sslNativePointer);
 
     /**
      * Gets the suitable session reference from the session cache container.
@@ -256,6 +197,9 @@
      * @return OpenSSLSessionImpl
      */
     private OpenSSLSessionImpl getCachedClientSession() {
+        if (!sslParameters.getUseClientMode()) {
+            return null;
+        }
         if (super.getInetAddress() == null ||
                 super.getInetAddress().getHostAddress() == null ||
                 super.getInetAddress().getHostName() == null) {
@@ -273,8 +217,7 @@
      * before logging is ready.
      */
     static class LoggerHolder {
-        static final Logger logger = Logger.getLogger(
-                OpenSSLSocketImpl.class.getName());
+        static final Logger logger = Logger.getLogger(OpenSSLSocketImpl.class.getName());
     }
 
     /**
@@ -282,7 +225,7 @@
      * from the OpenSSL library. It can negotiate new encryption keys, change
      * cipher suites, or initiate a new session. The certificate chain is
      * verified if the correspondent property in java.Security is set. All
-     * listensers are notified at the end of the TLS/SSL handshake.
+     * listeners are notified at the end of the TLS/SSL handshake.
      *
      * @throws <code>IOException</code> if network fails
      */
@@ -300,86 +243,77 @@
         // Check if it's allowed to create a new session (default is true)
         if (session == null && !sslParameters.getEnableSessionCreation()) {
             throw new SSLHandshakeException("SSL Session may not be created");
-        } else {
-            // BEGIN android-added
-            // Temporarily use a different timeout for the handshake process
-            int savedTimeout = timeout;
-            if (handshakeTimeout >= 0) {
-                setSoTimeout(handshakeTimeout);
-            }
-            // END android-added
-
-            Socket socket = this.socket != null ? this.socket : this;
-            int sessionId = session != null ? session.session : 0;
-            boolean reusedSession;
-            synchronized (OpenSSLSocketImpl.class) {
-                reusedSession = nativeconnect(ssl_ctx, socket,
-                        sslParameters.getUseClientMode(), sessionId);
-            }
-            if (reusedSession) {
-                // nativeconnect shouldn't return true if the session is not
-                // done
-                session.lastAccessedTime = System.currentTimeMillis();
-                sslSession = session;
-
-                LoggerHolder.logger.fine("Reused cached session for "
-                        + getInetAddress().getHostName() + ".");
-            } else {
-                if (session != null) {
-                    LoggerHolder.logger.fine("Reuse of cached session for "
-                            + getInetAddress().getHostName() + " failed.");
-                } else {
-                    LoggerHolder.logger.fine("Created new session for "
-                            + getInetAddress().getHostName() + ".");
-                }
-
-                ClientSessionContext sessionContext
-                        = sslParameters.getClientSessionContext();
-                synchronized (OpenSSLSocketImpl.class) {
-                    sessionId = nativegetsslsession(ssl);
-                }
-                if (address == null) {
-                    sslSession = new OpenSSLSessionImpl(
-                            sessionId, sslParameters,
-                            super.getInetAddress().getHostName(),
-                            super.getPort(), sessionContext);
-                } else  {
-                    sslSession = new OpenSSLSessionImpl(
-                            sessionId, sslParameters,
-                            address.getHostName(), address.getPort(),
-                            sessionContext);
-                }
-
-                try {
-                    X509Certificate[] peerCertificates = (X509Certificate[])
-                            sslSession.getPeerCertificates();
-
-                    if (peerCertificates == null
-                            || peerCertificates.length == 0) {
-                        throw new SSLException("Server sends no certificate");
-                    }
-
-                    String authMethod;
-                    synchronized (OpenSSLSocketImpl.class) {
-                        authMethod = nativecipherauthenticationmethod();
-                    }
-                    sslParameters.getTrustManager().checkServerTrusted(
-                            peerCertificates,
-                            authMethod);
-                    sessionContext.putSession(sslSession);
-                } catch (CertificateException e) {
-                    throw new SSLException("Not trusted server certificate", e);
-                }
-            }
-
-            // BEGIN android-added
-            // Restore the original timeout now that the handshake is complete
-            if (handshakeTimeout >= 0) {
-                setSoTimeout(savedTimeout);
-            }
-            // END android-added
         }
 
+        // BEGIN android-added
+        // Temporarily use a different timeout for the handshake process
+        int savedTimeout = timeout;
+        if (handshakeTimeout >= 0) {
+            setSoTimeout(handshakeTimeout);
+        }
+        // END android-added
+
+        Socket socket = this.socket != null ? this.socket : this;
+        int sslSessionNativePointer = session != null ? session.sslSessionNativePointer : 0;
+        boolean reusedSession = nativeconnect(sslNativePointer, socket, timeout,
+                                              sslParameters.getUseClientMode(), sslSessionNativePointer);
+        if (reusedSession) {
+            // nativeconnect shouldn't return true if the session is not
+            // done
+            session.lastAccessedTime = System.currentTimeMillis();
+            sslSession = session;
+
+            LoggerHolder.logger.fine("Reused cached session for "
+                                     + getInetAddress().getHostName() + ".");
+        } else {
+            if (session != null) {
+                LoggerHolder.logger.fine("Reuse of cached session for "
+                                         + getInetAddress().getHostName() + " failed.");
+            } else {
+                LoggerHolder.logger.fine("Created new session for "
+                                         + getInetAddress().getHostName() + ".");
+            }
+
+            AbstractSessionContext sessionContext =
+                (sslParameters.getUseClientMode()) ?
+                sslParameters.getClientSessionContext() :
+                sslParameters.getServerSessionContext();
+            sslSessionNativePointer = nativegetsslsession(sslNativePointer);
+            if (address == null) {
+                sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, sslParameters,
+                                                    super.getInetAddress().getHostName(),
+                                                    super.getPort(), sessionContext);
+            } else  {
+                sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, sslParameters,
+                                                    address.getHostName(), address.getPort(),
+                                                    sessionContext);
+            }
+
+            try {
+                X509Certificate[] peerCertificates = (X509Certificate[])
+                    sslSession.getPeerCertificates();
+
+                if (peerCertificates == null
+                    || peerCertificates.length == 0) {
+                    throw new SSLException("Server sends no certificate");
+                }
+
+                String authMethod = nativecipherauthenticationmethod(sslNativePointer);
+                sslParameters.getTrustManager().checkServerTrusted(peerCertificates,
+                                                                   authMethod);
+                sessionContext.putSession(sslSession);
+            } catch (CertificateException e) {
+                throw new SSLException("Not trusted server certificate", e);
+            }
+        }
+
+        // BEGIN android-added
+        // Restore the original timeout now that the handshake is complete
+        if (handshakeTimeout >= 0) {
+            setSoTimeout(savedTimeout);
+        }
+        // END android-added
+
         if (listeners != null) {
             // notify the listeners
             HandshakeCompletedEvent event =
@@ -392,22 +326,22 @@
     }
 
     // To be synchronized because of the verify_callback
-    native synchronized void nativeaccept(Socket socketObject, int m_ctx, boolean client_mode);
+    native synchronized int nativeaccept(int sslNativePointer, Socket socketObject);
 
     /**
      * Performs the first part of a SSL/TLS handshaking process with a given
      * 'host' connection and initializes the SSLSession.
      */
-    protected void accept(int m_ctx, boolean client_mode) throws IOException {
+    protected void accept(int serverSslNativePointer) throws IOException {
         // Must be set because no handshaking is necessary
         // in this situation
         handshakeStarted = true;
 
-        nativeaccept(this, m_ctx, client_mode);
+        sslNativePointer = nativeaccept(serverSslNativePointer, this);
 
         ServerSessionContext sessionContext
                 = sslParameters.getServerSessionContext();
-        sslSession = new OpenSSLSessionImpl(nativegetsslsession(ssl),
+        sslSession = new OpenSSLSessionImpl(nativegetsslsession(sslNativePointer),
                 sslParameters, super.getInetAddress().getHostName(),
                 super.getPort(), sessionContext);
         sslSession.lastAccessedTime = System.currentTimeMillis();
@@ -415,14 +349,16 @@
     }
 
     /**
-     * Callback methode for the OpenSSL native certificate verification process.
+     * Implementation of NativeCrypto.CertificateChainVerifier.
+     *
+     * Callback method for the OpenSSL native certificate verification process.
      *
      * @param bytes Byte array containing the cert's
      *            information.
-     * @return 0 if the certificate verification fails or 1 if OK
+     * @return false if the certificate verification fails or true if OK
      */
     @SuppressWarnings("unused")
-    private int verify_callback(byte[][] bytes) {
+    public boolean verifyCertificateChain(byte[][] bytes) {
         try {
             X509Certificate[] peerCertificateChain
                     = new X509Certificate[bytes.length];
@@ -439,11 +375,13 @@
                         new SSLException("Not trusted server certificate", e));
             }
         } catch (javax.security.cert.CertificateException e) {
-            return 0;
+            // TODO throw in all cases for consistency
+            return false;
         } catch (IOException e) {
-            return 0;
+            // TODO throw in all cases for consistency
+            return false;
         }
-        return 1;
+        return true;
     }
 
     /**
@@ -483,28 +421,28 @@
         }
     }
 
-    /**
-     * This method is not supported for this SSLSocket implementation.
-     */
     public void shutdownInput() throws IOException {
-        throw new UnsupportedOperationException(
-        "Method shutdownInput() is not supported.");
+        if (socket == null) {
+            super.shutdownInput();
+            return;
+        }
+        socket.shutdownInput();
     }
 
-    /**
-     * This method is not supported for this SSLSocket implementation.
-     */
     public void shutdownOutput() throws IOException {
-        throw new UnsupportedOperationException(
-        "Method shutdownOutput() is not supported.");
+        if (socket == null) {
+            super.shutdownOutput();
+            return;
+        }
+        socket.shutdownOutput();
     }
 
     /**
      * Reads with the native SSL_read function from the encrypted data stream
      * @return -1 if error or the end of the stream is reached.
      */
-    private native int nativeread(int timeout) throws IOException;
-    private native int nativeread(byte[] b, int off, int len, int timeout) throws IOException;
+    private native int nativeread(int sslNativePointer, int timeout) throws IOException;
+    private native int nativeread(int sslNativePointer, byte[] b, int off, int len, int timeout) throws IOException;
 
     /**
      * This inner class provides input data stream functionality
@@ -529,7 +467,7 @@
          */
         public int read() throws IOException {
             synchronized(readLock) {
-                return OpenSSLSocketImpl.this.nativeread(timeout);
+                return OpenSSLSocketImpl.this.nativeread(sslNativePointer, timeout);
             }
         }
 
@@ -539,7 +477,7 @@
          */
         public int read(byte[] b, int off, int len) throws IOException {
             synchronized(readLock) {
-                return OpenSSLSocketImpl.this.nativeread(b, off, len, timeout);
+                return OpenSSLSocketImpl.this.nativeread(sslNativePointer, b, off, len, timeout);
             }
         }
     }
@@ -547,8 +485,8 @@
     /**
      * Writes with the native SSL_write function to the encrypted data stream.
      */
-    private native void nativewrite(int b) throws IOException;
-    private native void nativewrite(byte[] b, int off, int len) throws IOException;
+    private native void nativewrite(int sslNativePointer, int b) throws IOException;
+    private native void nativewrite(int sslNativePointer, byte[] b, int off, int len) throws IOException;
 
     /**
      * This inner class provides output data stream functionality
@@ -559,7 +497,7 @@
         SSLOutputStream() throws IOException {
             /**
             /* Note: When startHandshake() throws an exception, no
-             * SSLInputStream object will be created.
+             * SSLOutputStream object will be created.
              */
             OpenSSLSocketImpl.this.startHandshake();
         }
@@ -570,7 +508,7 @@
          */
         public void write(int b) throws IOException {
             synchronized(writeLock) {
-                OpenSSLSocketImpl.this.nativewrite(b);
+                OpenSSLSocketImpl.this.nativewrite(sslNativePointer, b);
             }
         }
 
@@ -580,7 +518,7 @@
          */
         public void write(byte[] b, int start, int len) throws IOException {
             synchronized(writeLock) {
-                OpenSSLSocketImpl.this.nativewrite(b, start, len);
+                OpenSSLSocketImpl.this.nativewrite(sslNativePointer, b, start, len);
             }
         }
     }
@@ -598,9 +536,6 @@
         try {
             startHandshake();
         } catch (IOException e) {
-            Logger.getLogger(getClass().getName()).log(Level.WARNING,
-                    "Error negotiating SSL connection.", e);
-
             // return an invalid session with
             // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
             return SSLSessionImpl.NULL_SESSION;
@@ -666,22 +601,14 @@
     }
 
     /**
-     * Gets all available ciphers from the current OpenSSL library.
-     * Needed by OpenSSLSocketFactory too.
-     */
-    static native String[] nativegetsupportedciphersuites();
-
-    /**
      * The names of the cipher suites which could be used by the SSL connection
      * are returned.
      * @return an array of cipher suite names
      */
     public String[] getSupportedCipherSuites() {
-        return nativegetsupportedciphersuites();
+        return NativeCrypto.getSupportedCipherSuites();
     }
 
-    static native String[] nativeGetEnabledCipherSuites(int ssl_ctx);
-
     /**
      * The names of the cipher suites that are in use in the actual the SSL
      * connection are returned.
@@ -689,23 +616,7 @@
      * @return an array of cipher suite names
      */
     public String[] getEnabledCipherSuites() {
-        return nativeGetEnabledCipherSuites(ssl_ctx);
-    }
-
-    /**
-     * Calls the SSL_CTX_set_cipher_list(...) OpenSSL function with the passed
-     * char array.
-     */
-    static native void nativeSetEnabledCipherSuites(int ssl_ctx, String controlString);
-
-    private static boolean findSuite(String suite) {
-        String[] supportedCipherSuites = nativegetsupportedciphersuites();
-        for(int i = 0; i < supportedCipherSuites.length; i++) {
-            if (supportedCipherSuites[i].equals(suite)) {
-                return true;
-            }
-        }
-        throw new IllegalArgumentException("Protocol " + suite + " is not supported.");
+        return NativeCrypto.SSL_get_ciphers(sslNativePointer);
     }
 
     /**
@@ -719,20 +630,7 @@
      *             is null.
      */
     public void setEnabledCipherSuites(String[] suites) {
-        setEnabledCipherSuites(ssl_ctx, suites);
-    }
-    
-    static void setEnabledCipherSuites(int ssl_ctx, String[] suites) {
-        if (suites == null) {
-            throw new IllegalArgumentException("Provided parameter is null");
-        }
-        String controlString = "";
-        for (int i = 0; i < suites.length; i++) {
-            findSuite(suites[i]);
-            if (i == 0) controlString = suites[i];
-            else controlString += ":" + suites[i];
-        }
-        nativeSetEnabledCipherSuites(ssl_ctx, controlString);
+        NativeCrypto.setEnabledCipherSuites(sslNativePointer, suites);
     }
 
     /**
@@ -741,65 +639,32 @@
      * @return an array of protocols names
      */
     public String[] getSupportedProtocols() {
-        return supportedProtocols.clone();
+        return NativeCrypto.getSupportedProtocols();
     }
 
     /**
-     * SSL mode of operation with or without back compatibility. See the OpenSSL
-     * ssl.h header file for more information.
-     */
-    static private long SSL_OP_NO_SSLv3 = 0x02000000L;
-    static private long SSL_OP_NO_TLSv1 = 0x04000000L;
-
-    /**
      * The names of the protocols' versions that are in use on this SSL
      * connection.
-     * 
+     *
      * @return an array of protocols names
      */
     @Override
     public String[] getEnabledProtocols() {
-        ArrayList<String> array = new ArrayList<String>();
-
-        if ((ssl_op_no & SSL_OP_NO_SSLv3) == 0x00000000L) {
-            array.add(supportedProtocols[1]);
-        }
-        if ((ssl_op_no & SSL_OP_NO_TLSv1) == 0x00000000L) {
-            array.add(supportedProtocols[2]);
-        }
-        return array.toArray(new String[array.size()]);
+        return NativeCrypto.getEnabledProtocols(sslNativePointer);
     }
 
-    private native void nativesetenabledprotocols(long l);
-
     /**
      * This method enables the protocols' versions listed by
      * getSupportedProtocols().
-     * 
+     *
      * @param protocols The names of all the protocols to put on use
-     * 
+     *
      * @throws IllegalArgumentException when one or more of the names in the
      *             array are not supported, or when the array is null.
      */
     @Override
     public synchronized void setEnabledProtocols(String[] protocols) {
-
-        if (protocols == null) {
-            throw new IllegalArgumentException("Provided parameter is null");
-        }
-
-        ssl_op_no  = SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
-
-        for(int i = 0; i < protocols.length; i++) {
-            if (protocols[i].equals("SSLv3"))
-                ssl_op_no ^= SSL_OP_NO_SSLv3;
-            else if (protocols[i].equals("TLSv1"))
-                ssl_op_no ^= SSL_OP_NO_TLSv1;
-            else throw new IllegalArgumentException("Protocol " + protocols[i] +
-            " is not supported.");
-        }
-
-        nativesetenabledprotocols(ssl_op_no);
+        NativeCrypto.setEnabledProtocols(sslNativePointer, protocols);
     }
 
     /**
@@ -912,8 +777,8 @@
     }
     // END android-added
 
-    private native void nativeinterrupt() throws IOException;
-    private native void nativeclose() throws IOException;
+    private native void nativeinterrupt(int sslNativePointer) throws IOException;
+    private native void nativeclose(int sslNativePointer) throws IOException;
 
     /**
      * Closes the SSL socket. Once closed, a socket is not available for further
@@ -929,9 +794,9 @@
         synchronized (handshakeLock) {
             if (!handshakeStarted) {
                 handshakeStarted = true;
-                
+
                 synchronized (this) {
-                    nativefree();
+                    free();
 
                     if (socket != null) {
                         if (autoClose && !socket.isClosed()) socket.close();
@@ -939,12 +804,12 @@
                         if (!super.isClosed()) super.close();
                     }
                 }
-                
+
                 return;
             }
         }
 
-        nativeinterrupt();
+        nativeinterrupt(sslNativePointer);
 
         synchronized (this) {
             synchronized (writeLock) {
@@ -955,7 +820,7 @@
                     // Shut down the SSL connection, per se.
                     try {
                         if (handshakeStarted) {
-                            nativeclose();
+                            nativeclose(sslNativePointer);
                         }
                     } catch (IOException ex) {
                         /*
@@ -970,7 +835,7 @@
                      * the native structs, and we need to do so lest we leak
                      * memory.
                      */
-                    nativefree();
+                    free();
 
                     if (socket != null) {
                         if (autoClose && !socket.isClosed())
@@ -988,12 +853,18 @@
         }
     }
 
-    private native void nativefree();
+    private void free() {
+        if (sslNativePointer == 0) {
+            return;
+        }
+        NativeCrypto.SSL_free(sslNativePointer);
+        sslNativePointer = 0;
+    }
 
     protected void finalize() throws IOException {
         updateInstanceCount(-1);
 
-        if (ssl == 0) {
+        if (sslNativePointer == 0) {
             /*
              * It's already been closed, so there's no need to do anything
              * more at this point.
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java
index a150470..31bb681 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java
@@ -49,9 +49,6 @@
         this.in = in;
     }
 
-    /**
-     * Returns the number of bytes available for reading.
-     */
     @Override
     public int available() throws IOException {
         // in assumption that the buffer has been set
@@ -78,4 +75,3 @@
         return bytik;
     }
 }
-
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java
index 507e14f..b2501a7 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java
@@ -29,9 +29,6 @@
  */
 public abstract class SSLInputStream extends InputStream {
 
-    /**
-     * @see java.io.InputStream#available()
-     */
     @Override
     public abstract int available() throws IOException;
 
@@ -48,9 +45,6 @@
     @Override
     public abstract int read() throws IOException;
 
-    /**
-     * @see java.io.InputStream#skip(long)
-     */
     @Override
     public long skip(long n) throws IOException {
         long skept = n;
@@ -115,9 +109,6 @@
         return res;
     }
 
-    /**
-     * @see java.io.InputStream#read(byte[],int,int)
-     */
     @Override
     public int read(byte[] b, int off, int len) throws IOException {
         int read_b;
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java
index 89916de..525756d 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java
@@ -93,29 +93,11 @@
         if (enabledCipherSuites == null) this.enabledCipherSuites = CipherSuite.defaultCipherSuites;
         return enabledCipherSuites;
     }
-
-    /**
-     * Holds a pointer to our native SSL context.
-     */
-    private int ssl_ctx = 0;
-    
-    /**
-     * Initializes our native SSL context.
-     */
-    private native int nativeinitsslctx();
-
-    /**
-     * Returns the native SSL context, creating it on-the-fly, if necessary.
-     */
-    protected synchronized int getSSLCTX() {
-        if (ssl_ctx == 0) ssl_ctx = nativeinitsslctx();
-        return ssl_ctx;
-    }
 // END android-changed
 
     /**
      * Initializes the parameters. Naturally this constructor is used
-     * in SSLContextImpl.engineInit method which dirrectly passes its 
+     * in SSLContextImpl.engineInit method which dirrectly passes its
      * parameters. In other words this constructor holds all
      * the functionality provided by SSLContext.init method.
      * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],
@@ -127,21 +109,21 @@
             SSLServerSessionCache serverCache)
             throws KeyManagementException {
         this.serverSessionContext
-                = new ServerSessionContext(this, serverCache);
+                = new ServerSessionContext(this, NativeCrypto.SSL_CTX_new(), serverCache);
         this.clientSessionContext
-                = new ClientSessionContext(this, clientCache);
+                = new ClientSessionContext(this, NativeCrypto.SSL_CTX_new(), clientCache);
 // END android-changed
         try {
             // initialize key manager
             boolean initialize_default = false;
-            // It's not described by the spec of SSLContext what should happen 
+            // It's not described by the spec of SSLContext what should happen
             // if the arrays of length 0 are specified. This implementation
             // behave as for null arrays (i.e. use installed security providers)
             if ((kms == null) || (kms.length == 0)) {
                 if (defaultKeyManager == null) {
                     KeyManagerFactory kmf = KeyManagerFactory.getInstance(
                             KeyManagerFactory.getDefaultAlgorithm());
-                    kmf.init(null, null);                
+                    kmf.init(null, null);
                     kms = kmf.getKeyManagers();
                     // tell that we are trying to initialize defaultKeyManager
                     initialize_default = true;
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java
index a3c2c6d..c379fee 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java
@@ -46,8 +46,9 @@
     private final SSLServerSessionCache persistentCache;
 
     public ServerSessionContext(SSLParameters parameters,
+            int sslCtxNativePointer,
             SSLServerSessionCache persistentCache) {
-        super(parameters, 100, 0);
+        super(parameters, sslCtxNativePointer, 100, 0);
         this.persistentCache = persistentCache;
     }
 
@@ -107,8 +108,13 @@
         return null;
     }
 
+    @Override
     void putSession(SSLSession session) {
-        ByteArray key = new ByteArray(session.getId());
+        byte[] id = session.getId();
+        if (id.length == 0) {
+            return;
+        }
+        ByteArray key = new ByteArray(id);
         synchronized (sessions) {
             sessions.put(key, session);
         }
diff --git a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
index 3bfe061..cb275b7 100644
--- a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
+++ b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
@@ -46,7 +46,7 @@
 
 /**
  * Frees the SSL error state.
- * 
+ *
  * OpenSSL keeps an "error stack" per thread, and given that this code
  * can be called from arbitrary threads that we don't keep track of,
  * we err on the side of freeing the error state promptly (instead of,
@@ -60,7 +60,7 @@
 /*
  * Checks this thread's OpenSSL error queue and throws a RuntimeException if
  * necessary.
- * 
+ *
  * @return 1 if an exception was thrown, 0 if not.
  */
 static int throwExceptionIfNecessary(JNIEnv* env) {
@@ -79,328 +79,6 @@
     return result;
 }
 
-/**
- * Converts a Java byte[] to an OpenSSL BIGNUM, allocating the BIGNUM on the
- * fly.
- */
-static BIGNUM* arrayToBignum(JNIEnv* env, jbyteArray source) {
-    // LOGD("Entering arrayToBignum()");
-  
-    jbyte* sourceBytes = env->GetByteArrayElements(source, NULL);
-    int sourceLength = env->GetArrayLength(source);
-    BIGNUM* bignum = BN_bin2bn((unsigned char*) sourceBytes, sourceLength, NULL);
-    env->ReleaseByteArrayElements(source, sourceBytes, JNI_ABORT);
-    return bignum;
-}
-
-/**
- * private static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] pub_key, byte[] priv_key);
- */
-static EVP_PKEY* NativeCrypto_EVP_PKEY_new_DSA(JNIEnv* env, jclass clazz, jbyteArray p, jbyteArray q, jbyteArray g, jbyteArray pub_key, jbyteArray priv_key) {
-    // LOGD("Entering EVP_PKEY_new_DSA()");
-  
-    DSA* dsa = DSA_new();
-  
-    dsa->p = arrayToBignum(env, p);
-    dsa->q = arrayToBignum(env, q);
-    dsa->g = arrayToBignum(env, g);
-    dsa->pub_key = arrayToBignum(env, pub_key);
-    
-    if (priv_key != NULL) {
-        dsa->priv_key = arrayToBignum(env, priv_key);
-    }
-
-    if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL || dsa->pub_key == NULL) {
-        DSA_free(dsa);
-        jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
-        return NULL;
-    }
-    
-    EVP_PKEY* pkey = EVP_PKEY_new();
-    EVP_PKEY_assign_DSA(pkey, dsa);
-    
-    return pkey;
-}
-
-/**
- * private static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
- */
-static EVP_PKEY* NativeCrypto_EVP_PKEY_new_RSA(JNIEnv* env, jclass clazz, jbyteArray n, jbyteArray e, jbyteArray d, jbyteArray p, jbyteArray q) {
-    // LOGD("Entering EVP_PKEY_new_RSA()");
-  
-    RSA* rsa = RSA_new();
-  
-    rsa->n = arrayToBignum(env, n);
-    rsa->e = arrayToBignum(env, e);
-    
-    if (d != NULL) {
-        rsa->d = arrayToBignum(env, d);
-    }
-    
-    if (p != NULL) {
-        rsa->p = arrayToBignum(env, p);
-    }
-    
-    if (q != NULL) {
-        rsa->q = arrayToBignum(env, q);
-    }
-
-    // int check = RSA_check_key(rsa);
-    // LOGI("RSA_check_key returns %d", check);
-    
-    if (rsa->n == NULL || rsa->e == NULL) {
-        RSA_free(rsa);
-        jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
-        return NULL;
-    }
-    
-    EVP_PKEY* pkey = EVP_PKEY_new();
-    EVP_PKEY_assign_RSA(pkey, rsa);
-    
-    return pkey;
-}
-
-/**
- * private static native void EVP_PKEY_free(int pkey);
- */
-static void NativeCrypto_EVP_PKEY_free(JNIEnv* env, jclass clazz, EVP_PKEY* pkey) {
-    // LOGD("Entering EVP_PKEY_free()");
-
-    if (pkey != NULL) {
-        EVP_PKEY_free(pkey);
-    }
-}
-
-/*
- * public static native int EVP_new()
- */
-static jint NativeCrypto_EVP_new(JNIEnv* env, jclass clazz) {
-    // LOGI("NativeCrypto_EVP_DigestNew");
-    
-    return (jint)EVP_MD_CTX_create();
-}
-
-/*
- * public static native void EVP_free(int)
- */
-static void NativeCrypto_EVP_free(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
-    // LOGI("NativeCrypto_EVP_DigestFree");
-  
-    if (ctx != NULL) {
-        EVP_MD_CTX_destroy(ctx);
-    }
-}
-
-/*
- * public static native int EVP_DigestFinal(int, byte[], int)
- */
-static jint NativeCrypto_EVP_DigestFinal(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray hash, jint offset) {
-    // LOGI("NativeCrypto_EVP_DigestFinal%x, %x, %d, %d", ctx, hash, offset);
-    
-    if (ctx == NULL || hash == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return -1;
-    }
-
-    int result = -1;
-    
-    jbyte* hashBytes = env->GetByteArrayElements(hash, NULL);
-    EVP_DigestFinal(ctx, (unsigned char*) (hashBytes + offset), (unsigned int*)&result);
-    env->ReleaseByteArrayElements(hash, hashBytes, 0);
-
-    throwExceptionIfNecessary(env);
-    
-    return result;
-}
-
-/*
- * public static native void EVP_DigestInit(int, java.lang.String)
- */
-static void NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jstring algorithm) {
-    // LOGI("NativeCrypto_EVP_DigestInit");
-    
-    if (ctx == NULL || algorithm == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return;
-    }
-    
-    const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
-    
-    const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars));
-    env->ReleaseStringUTFChars(algorithm, algorithmChars);
-    
-    if (digest == NULL) {
-        jniThrowRuntimeException(env, "Hash algorithm not found");
-        return;
-    }
-    
-    EVP_DigestInit(ctx, digest);
-    
-    throwExceptionIfNecessary(env);
-}
-
-/*
- * public static native void EVP_DigestSize(int)
- */
-static jint NativeCrypto_EVP_DigestSize(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
-    // LOGI("NativeCrypto_EVP_DigestSize");
-    
-    if (ctx == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return -1;
-    }
-    
-    int result = EVP_MD_CTX_size(ctx);
-    
-    throwExceptionIfNecessary(env);
-    
-    return result;
-}
-
-/*
- * public static native void EVP_DigestBlockSize(int)
- */
-static jint NativeCrypto_EVP_DigestBlockSize(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
-    // LOGI("NativeCrypto_EVP_DigestBlockSize");
-    
-    if (ctx == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return -1;
-    }
-    
-    int result = EVP_MD_CTX_block_size(ctx);
-    
-    throwExceptionIfNecessary(env);
-    
-    return result;
-}
-
-/*
- * public static native void EVP_DigestUpdate(int, byte[], int, int)
- */
-static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length) {
-    // LOGI("NativeCrypto_EVP_DigestUpdate %x, %x, %d, %d", ctx, buffer, offset, length);
-    
-    if (ctx == NULL || buffer == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return;
-    }
-  
-    jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
-    EVP_DigestUpdate(ctx, (unsigned char*) (bufferBytes + offset), length);
-    env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
-  
-    throwExceptionIfNecessary(env);
-}
-
-/*
- * public static native void EVP_VerifyInit(int, java.lang.String)
- */
-static void NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jstring algorithm) {
-    // LOGI("NativeCrypto_EVP_VerifyInit");
-    
-    if (ctx == NULL || algorithm == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return;
-    }
-    
-    const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
-    
-    const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars));
-    env->ReleaseStringUTFChars(algorithm, algorithmChars);
-    
-    if (digest == NULL) {
-        jniThrowRuntimeException(env, "Hash algorithm not found");
-        return;
-    }
-    
-    EVP_VerifyInit(ctx, digest);
-    
-    throwExceptionIfNecessary(env);
-}
-
-/*
- * public static native void EVP_VerifyUpdate(int, byte[], int, int)
- */
-static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length) {
-    // LOGI("NativeCrypto_EVP_VerifyUpdate %x, %x, %d, %d", ctx, buffer, offset, length);
-    
-    if (ctx == NULL || buffer == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return;
-    }
-  
-    jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
-    EVP_VerifyUpdate(ctx, (unsigned char*) (bufferBytes + offset), length);
-    env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
-  
-    throwExceptionIfNecessary(env);
-}
-
-/*
- * public static native void EVP_VerifyFinal(int, byte[], int, int, int)
- */
-static int NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length, EVP_PKEY* pkey) {
-    // LOGI("NativeCrypto_EVP_VerifyFinal %x, %x, %d, %d %x", ctx, buffer, offset, length, pkey);
-    
-    if (ctx == NULL || buffer == NULL || pkey == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return -1;
-    }
-  
-    jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
-    int result = EVP_VerifyFinal(ctx, (unsigned char*) (bufferBytes + offset), length, pkey);
-    env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
-  
-    throwExceptionIfNecessary(env);
-    
-    return result;
-}
-
-/*
- * Defines the mapping from Java methods and their signatures
- * to native functions. Order is (1) Java name, (2) signature,
- * (3) pointer to C function.
- */
-static JNINativeMethod sNativeCryptoMethods[] = {
-    { "EVP_PKEY_new_DSA",    "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_DSA },
-    { "EVP_PKEY_new_RSA",    "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_RSA },
-    { "EVP_PKEY_free",       "(I)V",          (void*)NativeCrypto_EVP_PKEY_free },
-    { "EVP_new",             "()I",           (void*)NativeCrypto_EVP_new },
-    { "EVP_free",            "(I)V",          (void*)NativeCrypto_EVP_free },
-    { "EVP_DigestFinal",     "(I[BI)I",       (void*)NativeCrypto_EVP_DigestFinal },
-    { "EVP_DigestInit",      "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_DigestInit },
-    { "EVP_DigestBlockSize", "(I)I",          (void*)NativeCrypto_EVP_DigestBlockSize },
-    { "EVP_DigestSize",      "(I)I",          (void*)NativeCrypto_EVP_DigestSize },
-    { "EVP_DigestUpdate",    "(I[BII)V",      (void*)NativeCrypto_EVP_DigestUpdate },
-    { "EVP_VerifyInit",      "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_VerifyInit },
-    { "EVP_VerifyUpdate",    "(I[BII)V",      (void*)NativeCrypto_EVP_VerifyUpdate },
-    { "EVP_VerifyFinal",     "(I[BIII)I",     (void*)NativeCrypto_EVP_VerifyFinal }
-};
-
-/**
- * Module scope variables initialized during JNI registration.
- */
-static jfieldID field_Socket_ssl_ctx;
-static jfieldID field_Socket_ssl;
-static jfieldID field_FileDescriptor_descriptor;
-static jfieldID field_Socket_mImpl;
-static jfieldID field_Socket_mFD;
-static jfieldID field_Socket_timeout;
-
-/**
- * Gets the chars of a String object as a '\0'-terminated UTF-8 string,
- * stored in a freshly-allocated BIO memory buffer.
- */
-static BIO *stringToMemBuf(JNIEnv* env, jstring string) {
-    jsize byteCount = env->GetStringUTFLength(string);
-    LocalArray<1024> buf(byteCount + 1);
-    env->GetStringUTFRegion(string, 0, env->GetStringLength(string), &buf[0]);
-
-    BIO* result = BIO_new(BIO_s_mem());
-    BIO_puts(result, &buf[0]);
-    return result;
-}
 
 /**
  * Throws an SocketTimeoutException with the given string as a message.
@@ -423,7 +101,7 @@
 /**
  * Throws an IOException with a message constructed from the current
  * SSL errors. This will also log the errors.
- * 
+ *
  * @param env the JNI environment
  * @param sslReturnCode return code from failing SSL function
  * @param sslErrorCode error code returned from SSL_get_error()
@@ -435,7 +113,7 @@
     char* str;
     int ret;
 
-    // First consult the SSL error code for the general message. 
+    // First consult the SSL error code for the general message.
     switch (sslErrorCode) {
         case SSL_ERROR_NONE:
             messageStr = "Ok";
@@ -511,7 +189,7 @@
             free(allocStr);
             allocStr = str;
         }
-    // For errors during system calls, errno might be our friend.        
+    // For errors during system calls, errno might be our friend.
     } else if (sslErrorCode == SSL_ERROR_SYSCALL) {
         if (asprintf(&str, "%s, %s", allocStr, strerror(errno)) >= 0) {
             free(allocStr);
@@ -533,21 +211,20 @@
 }
 
 /**
- * Helper function that grabs the ssl pointer out of the given object.
+ * Helper function that grabs the casts an ssl pointer and then checks for nullness.
  * If this function returns NULL and <code>throwIfNull</code> is
  * passed as <code>true</code>, then this function will call
  * <code>throwIOExceptionStr</code> before returning, so in this case of
  * NULL, a caller of this function should simply return and allow JNI
  * to do its thing.
- * 
- * @param env non-null; the JNI environment
- * @param obj non-null; socket object
+ *
+ * @param env the JNI environment
+ * @param ssl_address; the ssl_address pointer as an integer
  * @param throwIfNull whether to throw if the SSL pointer is NULL
  * @returns the pointer, which may be NULL
  */
-static SSL *getSslPointer(JNIEnv* env, jobject obj, bool throwIfNull) {
-    SSL *ssl = (SSL *)env->GetIntField(obj, field_Socket_ssl);
-
+static SSL* getSslPointer(JNIEnv* env, int ssl_address, bool throwIfNull) {
+    SSL* ssl = reinterpret_cast<SSL*>(static_cast<uintptr_t>(ssl_address));
     if ((ssl == NULL) && throwIfNull) {
         throwIOExceptionStr(env, "null SSL pointer");
     }
@@ -555,9 +232,19 @@
     return ssl;
 }
 
-// ============================================================================
-// === OpenSSL-related helper stuff begins here. ==============================
-// ============================================================================
+/**
+ * Converts a Java byte[] to an OpenSSL BIGNUM, allocating the BIGNUM on the
+ * fly.
+ */
+static BIGNUM* arrayToBignum(JNIEnv* env, jbyteArray source) {
+    // LOGD("Entering arrayToBignum()");
+
+    jbyte* sourceBytes = env->GetByteArrayElements(source, NULL);
+    int sourceLength = env->GetArrayLength(source);
+    BIGNUM* bignum = BN_bin2bn((unsigned char*) sourceBytes, sourceLength, NULL);
+    env->ReleaseByteArrayElements(source, sourceBytes, JNI_ABORT);
+    return bignum;
+}
 
 /**
  * OpenSSL locking support. Taken from the O'Reilly book by Viega et al., but I
@@ -626,59 +313,611 @@
     return 1;
 }
 
-int get_socket_timeout(int type, int sd) {
-    struct timeval tv;
-    socklen_t len = sizeof(tv);
-    if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
-         LOGE("getsockopt(%d, SOL_SOCKET): %s (%d)",
-              sd,
-              strerror(errno),
-              errno);
-        return 0;         
+/**
+ * Initialization phase for every OpenSSL job: Loads the Error strings, the
+ * crypto algorithms and reset the OpenSSL library
+ */
+static void NativeCrypto_clinit(JNIEnv* env, jclass)
+{
+    SSL_load_error_strings();
+    ERR_load_crypto_strings();
+    SSL_library_init();
+    OpenSSL_add_all_algorithms();
+    THREAD_setup();
+}
+
+/**
+ * public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] pub_key, byte[] priv_key);
+ */
+static EVP_PKEY* NativeCrypto_EVP_PKEY_new_DSA(JNIEnv* env, jclass clazz, jbyteArray p, jbyteArray q, jbyteArray g, jbyteArray pub_key, jbyteArray priv_key) {
+    // LOGD("Entering EVP_PKEY_new_DSA()");
+
+    DSA* dsa = DSA_new();
+
+    dsa->p = arrayToBignum(env, p);
+    dsa->q = arrayToBignum(env, q);
+    dsa->g = arrayToBignum(env, g);
+    dsa->pub_key = arrayToBignum(env, pub_key);
+
+    if (priv_key != NULL) {
+        dsa->priv_key = arrayToBignum(env, priv_key);
     }
-    // LOGI("Current socket timeout (%d(s), %d(us))!",
-    //      (int)tv.tv_sec, (int)tv.tv_usec);
-    int timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
-    return timeout;
-}
 
-#ifdef TIMEOUT_DEBUG_SSL
-
-void print_socket_timeout(const char* name, int type, int sd) {
-    struct timeval tv;
-    int len = sizeof(tv);
-    if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
-         LOGE("getsockopt(%d, SOL_SOCKET, %s): %s (%d)",
-              sd,
-              name,
-              strerror(errno),
-              errno);
+    if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL || dsa->pub_key == NULL) {
+        DSA_free(dsa);
+        jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
+        return NULL;
     }
-    LOGI("Current socket %s is (%d(s), %d(us))!",
-          name, (int)tv.tv_sec, (int)tv.tv_usec);
+
+    EVP_PKEY* pkey = EVP_PKEY_new();
+    EVP_PKEY_assign_DSA(pkey, dsa);
+
+    return pkey;
 }
 
-void print_timeout(const char* method, SSL* ssl) {    
-    LOGI("SSL_get_default_timeout %d in %s", SSL_get_default_timeout(ssl), method);
-    int fd = SSL_get_fd(ssl);
-    print_socket_timeout("SO_RCVTIMEO", SO_RCVTIMEO, fd);
-    print_socket_timeout("SO_SNDTIMEO", SO_SNDTIMEO, fd);
+/**
+ * private static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
+ */
+static EVP_PKEY* NativeCrypto_EVP_PKEY_new_RSA(JNIEnv* env, jclass clazz, jbyteArray n, jbyteArray e, jbyteArray d, jbyteArray p, jbyteArray q) {
+    // LOGD("Entering EVP_PKEY_new_RSA()");
+
+    RSA* rsa = RSA_new();
+
+    rsa->n = arrayToBignum(env, n);
+    rsa->e = arrayToBignum(env, e);
+
+    if (d != NULL) {
+        rsa->d = arrayToBignum(env, d);
+    }
+
+    if (p != NULL) {
+        rsa->p = arrayToBignum(env, p);
+    }
+
+    if (q != NULL) {
+        rsa->q = arrayToBignum(env, q);
+    }
+
+    // int check = RSA_check_key(rsa);
+    // LOGI("RSA_check_key returns %d", check);
+
+    if (rsa->n == NULL || rsa->e == NULL) {
+        RSA_free(rsa);
+        jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
+        return NULL;
+    }
+
+    EVP_PKEY* pkey = EVP_PKEY_new();
+    EVP_PKEY_assign_RSA(pkey, rsa);
+
+    return pkey;
 }
 
+/**
+ * private static native void EVP_PKEY_free(int pkey);
+ */
+static void NativeCrypto_EVP_PKEY_free(JNIEnv* env, jclass clazz, EVP_PKEY* pkey) {
+    // LOGD("Entering EVP_PKEY_free()");
+
+    if (pkey != NULL) {
+        EVP_PKEY_free(pkey);
+    }
+}
+
+/*
+ * public static native int EVP_new()
+ */
+static jint NativeCrypto_EVP_new(JNIEnv* env, jclass clazz) {
+    // LOGI("NativeCrypto_EVP_DigestNew");
+
+    return (jint)EVP_MD_CTX_create();
+}
+
+/*
+ * public static native void EVP_free(int)
+ */
+static void NativeCrypto_EVP_free(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
+    // LOGI("NativeCrypto_EVP_DigestFree");
+
+    if (ctx != NULL) {
+        EVP_MD_CTX_destroy(ctx);
+    }
+}
+
+/*
+ * public static native int EVP_DigestFinal(int, byte[], int)
+ */
+static jint NativeCrypto_EVP_DigestFinal(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray hash, jint offset) {
+    // LOGI("NativeCrypto_EVP_DigestFinal%x, %x, %d, %d", ctx, hash, offset);
+
+    if (ctx == NULL || hash == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return -1;
+    }
+
+    int result = -1;
+
+    jbyte* hashBytes = env->GetByteArrayElements(hash, NULL);
+    EVP_DigestFinal(ctx, (unsigned char*) (hashBytes + offset), (unsigned int*)&result);
+    env->ReleaseByteArrayElements(hash, hashBytes, 0);
+
+    throwExceptionIfNecessary(env);
+
+    return result;
+}
+
+/*
+ * public static native void EVP_DigestInit(int, java.lang.String)
+ */
+static void NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jstring algorithm) {
+    // LOGI("NativeCrypto_EVP_DigestInit");
+
+    if (ctx == NULL || algorithm == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+
+    const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
+
+    const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars));
+    env->ReleaseStringUTFChars(algorithm, algorithmChars);
+
+    if (digest == NULL) {
+        jniThrowRuntimeException(env, "Hash algorithm not found");
+        return;
+    }
+
+    EVP_DigestInit(ctx, digest);
+
+    throwExceptionIfNecessary(env);
+}
+
+/*
+ * public static native void EVP_DigestSize(int)
+ */
+static jint NativeCrypto_EVP_DigestSize(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
+    // LOGI("NativeCrypto_EVP_DigestSize");
+
+    if (ctx == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return -1;
+    }
+
+    int result = EVP_MD_CTX_size(ctx);
+
+    throwExceptionIfNecessary(env);
+
+    return result;
+}
+
+/*
+ * public static native void EVP_DigestBlockSize(int)
+ */
+static jint NativeCrypto_EVP_DigestBlockSize(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
+    // LOGI("NativeCrypto_EVP_DigestBlockSize");
+
+    if (ctx == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return -1;
+    }
+
+    int result = EVP_MD_CTX_block_size(ctx);
+
+    throwExceptionIfNecessary(env);
+
+    return result;
+}
+
+/*
+ * public static native void EVP_DigestUpdate(int, byte[], int, int)
+ */
+static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length) {
+    // LOGI("NativeCrypto_EVP_DigestUpdate %x, %x, %d, %d", ctx, buffer, offset, length);
+
+    if (ctx == NULL || buffer == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+
+    jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
+    EVP_DigestUpdate(ctx, (unsigned char*) (bufferBytes + offset), length);
+    env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
+
+    throwExceptionIfNecessary(env);
+}
+
+/*
+ * public static native void EVP_VerifyInit(int, java.lang.String)
+ */
+static void NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jstring algorithm) {
+    // LOGI("NativeCrypto_EVP_VerifyInit");
+
+    if (ctx == NULL || algorithm == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+
+    const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
+
+    const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars));
+    env->ReleaseStringUTFChars(algorithm, algorithmChars);
+
+    if (digest == NULL) {
+        jniThrowRuntimeException(env, "Hash algorithm not found");
+        return;
+    }
+
+    EVP_VerifyInit(ctx, digest);
+
+    throwExceptionIfNecessary(env);
+}
+
+/*
+ * public static native void EVP_VerifyUpdate(int, byte[], int, int)
+ */
+static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length) {
+    // LOGI("NativeCrypto_EVP_VerifyUpdate %x, %x, %d, %d", ctx, buffer, offset, length);
+
+    if (ctx == NULL || buffer == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+
+    jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
+    EVP_VerifyUpdate(ctx, (unsigned char*) (bufferBytes + offset), length);
+    env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
+
+    throwExceptionIfNecessary(env);
+}
+
+/*
+ * public static native void EVP_VerifyFinal(int, byte[], int, int, int)
+ */
+static int NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length, EVP_PKEY* pkey) {
+    // LOGI("NativeCrypto_EVP_VerifyFinal %x, %x, %d, %d %x", ctx, buffer, offset, length, pkey);
+
+    if (ctx == NULL || buffer == NULL || pkey == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return -1;
+    }
+
+    jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
+    int result = EVP_VerifyFinal(ctx, (unsigned char*) (bufferBytes + offset), length, pkey);
+    env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
+
+    throwExceptionIfNecessary(env);
+
+    return result;
+}
+
+/**
+ * Convert ssl version constant to string. Based on SSL_get_version
+ */
+static const char* get_ssl_version(int ssl_version) {
+    switch (ssl_version) {
+        // newest to oldest
+        case TLS1_VERSION: {
+          return SSL_TXT_TLSV1;
+        }
+        case SSL3_VERSION: {
+          return SSL_TXT_SSLV3;
+        }
+        case SSL2_VERSION: {
+          return SSL_TXT_SSLV2;
+        }
+        default: {
+          return "unknown";
+        }
+    }
+}
+
+/**
+ * Convert content type constant to string.
+ */
+static const char* get_content_type(int content_type) {
+    switch (content_type) {
+        case SSL3_RT_CHANGE_CIPHER_SPEC: {
+            return "SSL3_RT_CHANGE_CIPHER_SPEC";
+        }
+        case SSL3_RT_ALERT: {
+            return "SSL3_RT_ALERT";
+        }
+        case SSL3_RT_HANDSHAKE: {
+            return "SSL3_RT_HANDSHAKE";
+        }
+        case SSL3_RT_APPLICATION_DATA: {
+            return "SSL3_RT_APPLICATION_DATA";
+        }
+        default: {
+            LOGD("Unknown TLS/SSL content type %d", content_type);
+            return "<unknown>";
+        }
+    }
+}
+
+/**
+ * Simple logging call back to show hand shake messages
+ */
+static void ssl_msg_callback_LOG(int write_p, int ssl_version, int content_type,
+                                 const void *buf, size_t len, SSL* ssl, void* arg) {
+    LOGD("SSL %p %s %s %s %p %d %p",
+         ssl,
+         (write_p) ? "send" : "recv",
+         get_ssl_version(ssl_version),
+         get_content_type(content_type),
+         buf,
+         len,
+         arg);
+}
+
+/*
+ * public static native int SSL_CTX_new();
+ */
+static int NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass clazz) {
+    SSL_CTX* sslCtx = SSL_CTX_new(SSLv23_method());
+    // Note: We explicitly do not allow SSLv2 to be used.
+    SSL_CTX_set_options(sslCtx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
+
+    int mode = SSL_CTX_get_mode(sslCtx);
+    /*
+     * Turn on "partial write" mode. This means that SSL_write() will
+     * behave like Posix write() and possibly return after only
+     * writing a partial buffer. Note: The alternative, perhaps
+     * surprisingly, is not that SSL_write() always does full writes
+     * but that it will force you to retry write calls having
+     * preserved the full state of the original call. (This is icky
+     * and undesirable.)
+     */
+    mode |= SSL_MODE_ENABLE_PARTIAL_WRITE;
+#if defined(SSL_MODE_SMALL_BUFFERS) /* not all SSL versions have this */
+    mode |= SSL_MODE_SMALL_BUFFERS;  /* lazily allocate record buffers; usually saves
+                                      * 44k over the default */
 #endif
+#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) /* not all SSL versions have this */
+    mode |= SSL_MODE_HANDSHAKE_CUTTHROUGH;  /* enable sending of client data as soon as
+                                             * ClientCCS and ClientFinished are sent */
+#endif
+    SSL_CTX_set_mode(sslCtx, mode);
+
+    // SSL_CTX_set_msg_callback(sslCtx, ssl_msg_callback_LOG); /* enable for handshake debug */
+    return (jint) sslCtx;
+}
+
+static jobjectArray makeCipherList(JNIEnv* env, STACK_OF(SSL_CIPHER)* cipher_list) {
+    // Create a String[].
+    jclass stringClass = env->FindClass("java/lang/String");
+    if (stringClass == NULL) {
+        return NULL;
+    }
+    int cipherCount = sk_SSL_CIPHER_num(cipher_list);
+    jobjectArray array = env->NewObjectArray(cipherCount, stringClass, NULL);
+    if (array == NULL) {
+        return NULL;
+    }
+
+    // Fill in the cipher names.
+    for (int i = 0; i < cipherCount; ++i) {
+        const char* c = sk_SSL_CIPHER_value(cipher_list, i)->name;
+        env->SetObjectArrayElement(array, i, env->NewStringUTF(c));
+    }
+    return array;
+}
+
+/**
+ * Loads the ciphers suites that are supported by an SSL_CTX
+ * and returns them in a string array.
+ */
+static jobjectArray NativeCrypto_SSL_CTX_get_ciphers(JNIEnv* env,
+        jclass, jint ssl_ctx_address)
+{
+    SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+    if (ssl_ctx == NULL) {
+        jniThrowNullPointerException(env, "SSL_CTX is null");
+        return NULL;
+    }
+    return makeCipherList(env, ssl_ctx->cipher_list);
+}
+
+/**
+ * public static native void SSL_CTX_free(int ssl_ctx)
+ */
+static void NativeCrypto_SSL_CTX_free(JNIEnv* env,
+        jclass, jint ssl_ctx_address)
+{
+    SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+    if (ssl_ctx == NULL) {
+        jniThrowNullPointerException(env, "SSL_CTX is null");
+        return;
+    }
+    SSL_CTX_free(ssl_ctx);
+}
+
+/**
+ * Gets the chars of a String object as a '\0'-terminated UTF-8 string,
+ * stored in a freshly-allocated BIO memory buffer.
+ */
+static BIO *stringToMemBuf(JNIEnv* env, jstring string) {
+    jsize byteCount = env->GetStringUTFLength(string);
+    LocalArray<1024> buf(byteCount + 1);
+    env->GetStringUTFRegion(string, 0, env->GetStringLength(string), &buf[0]);
+
+    BIO* result = BIO_new(BIO_s_mem());
+    BIO_puts(result, &buf[0]);
+    return result;
+}
+
+/**
+ * public static native int SSL_new(int ssl_ctx, String privatekey, String certificate, byte[] seed) throws IOException;
+ */
+static jint NativeCrypto_SSL_new(JNIEnv* env, jclass,
+        jint ssl_ctx_address, jstring privatekey, jstring certificates, jbyteArray seed)
+{
+    SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+    if (ssl_ctx == NULL) {
+        jniThrowNullPointerException(env, "SSL_CTX is null");
+        return 0;
+    }
+
+    // 'seed == null' when no SecureRandom Object is set
+    // in the SSLContext.
+    if (seed != NULL) {
+        jbyte* randseed = env->GetByteArrayElements(seed, NULL);
+        RAND_seed((unsigned char*) randseed, 1024);
+        env->ReleaseByteArrayElements(seed, randseed, 0);
+    } else {
+        RAND_load_file("/dev/urandom", 1024);
+    }
+
+    SSL* ssl = SSL_new(ssl_ctx);
+    if (ssl == NULL) {
+        throwIOExceptionWithSslErrors(env, 0, 0,
+                "Unable to create SSL structure");
+        return NULL;
+    }
+
+    /* Java code in class OpenSSLSocketImpl does the verification. Meaning of
+     * SSL_VERIFY_NONE flag in client mode: if not using an anonymous cipher
+     * (by default disabled), the server will send a certificate which will
+     * be checked. The result of the certificate verification process can be
+     * checked after the TLS/SSL handshake using the SSL_get_verify_result(3)
+     * function. The handshake will be continued regardless of the
+     * verification result.
+     */
+    SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
+
+    if (privatekey != NULL) {
+        BIO* privatekeybio = stringToMemBuf(env, (jstring) privatekey);
+        EVP_PKEY* privatekeyevp =
+          PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
+        BIO_free(privatekeybio);
+
+        if (privatekeyevp == NULL) {
+            LOGE(ERR_error_string(ERR_get_error(), NULL));
+            throwIOExceptionWithSslErrors(env, 0, 0,
+                    "Error parsing the private key");
+            SSL_free(ssl);
+            return NULL;
+        }
+
+        BIO* certificatesbio = stringToMemBuf(env, (jstring) certificates);
+        X509* certificatesx509 =
+          PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
+        BIO_free(certificatesbio);
+
+        if (certificatesx509 == NULL) {
+            LOGE(ERR_error_string(ERR_get_error(), NULL));
+            throwIOExceptionWithSslErrors(env, 0, 0,
+                    "Error parsing the certificates");
+            EVP_PKEY_free(privatekeyevp);
+            SSL_free(ssl);
+            return NULL;
+        }
+
+        int ret = SSL_use_certificate(ssl, certificatesx509);
+        if (ret != 1) {
+            LOGE(ERR_error_string(ERR_get_error(), NULL));
+            throwIOExceptionWithSslErrors(env, ret, 0,
+                    "Error setting the certificates");
+            X509_free(certificatesx509);
+            EVP_PKEY_free(privatekeyevp);
+            SSL_free(ssl);
+            return NULL;
+        }
+
+        ret = SSL_use_PrivateKey(ssl, privatekeyevp);
+        if (ret != 1) {
+            LOGE(ERR_error_string(ERR_get_error(), NULL));
+            throwIOExceptionWithSslErrors(env, ret, 0,
+                    "Error setting the private key");
+            X509_free(certificatesx509);
+            EVP_PKEY_free(privatekeyevp);
+            SSL_free(ssl);
+            return NULL;
+        }
+
+        ret = SSL_check_private_key(ssl);
+        if (ret != 1) {
+            throwIOExceptionWithSslErrors(env, ret, 0,
+                    "Error checking the private key");
+            X509_free(certificatesx509);
+            EVP_PKEY_free(privatekeyevp);
+            SSL_free(ssl);
+            return NULL;
+        }
+    }
+    return (jint)ssl;
+}
+
+/**
+ * public static native long SSL_get_options(int ssl);
+ */
+static jlong NativeCrypto_SSL_get_options(JNIEnv* env, jclass,
+        jint ssl_address) {
+    SSL* ssl = getSslPointer(env, ssl_address, true);
+    if (ssl == NULL) {
+      return 0;
+    }
+    return SSL_get_options(ssl);
+}
+
+/**
+ * public static native long SSL_set_options(int ssl, long options);
+ */
+static jlong NativeCrypto_SSL_set_options(JNIEnv* env, jclass,
+        jint ssl_address, jlong options) {
+    SSL* ssl = getSslPointer(env, ssl_address, true);
+    if (ssl == NULL) {
+      return 0 ;
+    }
+    return SSL_set_options(ssl, options);
+}
+
+/**
+ * Loads the ciphers suites that are enabled in the SSL
+ * and returns them in a string array.
+ */
+static jobjectArray NativeCrypto_SSL_get_ciphers(JNIEnv* env,
+        jclass, jint ssl_address)
+{
+    SSL* ssl = getSslPointer(env, ssl_address, true);
+    if (ssl == NULL) {
+      return NULL;
+    }
+    return makeCipherList(env, SSL_get_ciphers(ssl));
+}
+
+/**
+ * Sets the ciphers suites that are enabled in the SSL
+ */
+static void NativeCrypto_SSL_set_cipher_list(JNIEnv* env, jclass,
+        jint ssl_address, jstring controlString)
+{
+    SSL* ssl = getSslPointer(env, ssl_address, true);
+    if (ssl == NULL) {
+      return;
+    }
+    const char* str = env->GetStringUTFChars(controlString, NULL);
+    int rc = SSL_set_cipher_list(ssl, str);
+    env->ReleaseStringUTFChars(controlString, str);
+    if (rc == 0) {
+        freeSslErrorState();
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "Illegal cipher suite strings.");
+    }
+}
 
 /**
  * Our additional application data needed for getting synchronization right.
  * This maybe warrants a bit of lengthy prose:
- * 
+ *
  * (1) We use a flag to reflect whether we consider the SSL connection alive.
  * Any read or write attempt loops will be cancelled once this flag becomes 0.
- * 
+ *
  * (2) We use an int to count the number of threads that are blocked by the
  * underlying socket. This may be at most two (one reader and one writer), since
  * the Java layer ensures that no more threads will enter the native code at the
  * same time.
- * 
+ *
  * (3) The pipe is used primarily as a means of cancelling a blocking select()
  * when we want to close the connection (aka "emergency button"). It is also
  * necessary for dealing with a possible race condition situation: There might
@@ -687,23 +926,23 @@
  * If one leaves the select() successfully before the other enters it, the
  * "success" event is already consumed and the second thread will be blocked,
  * possibly forever (depending on network conditions).
- *  
+ *
  * The idea for solving the problem looks like this: Whenever a thread is
  * successful in moving around data on the network, and it knows there is
  * another thread stuck in a select(), it will write a byte to the pipe, waking
  * up the other thread. A thread that returned from select(), on the other hand,
  * knows whether it's been woken up by the pipe. If so, it will consume the
  * byte, and the original state of affairs has been restored.
- * 
+ *
  * The pipe may seem like a bit of overhead, but it fits in nicely with the
  * other file descriptors of the select(), so there's only one condition to wait
  * for.
- * 
+ *
  * (4) Finally, a mutex is needed to make sure that at most one thread is in
  * either SSL_read() or SSL_write() at any given time. This is an OpenSSL
  * requirement. We use the same mutex to guard the field for counting the
  * waiting threads.
- * 
+ *
  * Note: The current implementation assumes that we don't have to deal with
  * problems induced by multiple cores or processors and their respective
  * memory caches. One possible problem is that of inconsistent views on the
@@ -712,7 +951,7 @@
  * currently this seems a bit like overkill.
  */
 typedef struct app_data {
-    int aliveAndKicking;
+    volatile int aliveAndKicking;
     int waitingThreads;
     int fdsEmergency[2];
     MUTEX_TYPE mutex;
@@ -720,7 +959,7 @@
 
 /**
  * Creates our application data and attaches it to a given SSL connection.
- * 
+ *
  * @param ssl The SSL connection to attach the data to.
  * @return 0 on success, -1 on failure.
  */
@@ -751,16 +990,16 @@
 
 /**
  * Destroys our application data, cleaning up everything in the process.
- * 
+ *
  * @param ssl The SSL connection to take the data from.
- */ 
+ */
 static void sslDestroyAppData(SSL* ssl) {
     APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
 
     if (data != NULL) {
         SSL_set_app_data(ssl, NULL);
 
-        data -> aliveAndKicking = 0;
+        data->aliveAndKicking = 0;
 
         if (data->fdsEmergency[0] != -1) {
             close(data->fdsEmergency[0]);
@@ -776,72 +1015,101 @@
     }
 }
 
-
 /**
- * Frees the SSL_CTX struct for the given instance.
+ * public static native void SSL_free(int ssl);
  */
-static void free_ssl_ctx(JNIEnv* env, jobject object) {
-    /*
-     * Preserve and restore the exception state around this call, so
-     * that GetIntField and SetIntField will operate without complaint.
-     */
-    jthrowable exception = env->ExceptionOccurred();
-
-    if (exception != NULL) {
-        env->ExceptionClear();
+static void NativeCrypto_SSL_free(JNIEnv* env, jclass, jint ssl_address)
+{
+    SSL* ssl = getSslPointer(env, ssl_address, true);
+    if (ssl == NULL) {
+      return;
     }
-
-    SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_Socket_ssl_ctx);
-
-    if (ctx != NULL) {
-        SSL_CTX_free(ctx);
-        env->SetIntField(object, field_Socket_ssl_ctx, (int) NULL);
-    }
-
-    if (exception != NULL) {
-        env->Throw(exception);
-    }
+    sslDestroyAppData(ssl);
+    SSL_free(ssl);
 }
 
-/**
- * Frees the SSL struct for the given instance.
+/*
+ * Defines the mapping from Java methods and their signatures
+ * to native functions. Order is (1) Java name, (2) signature,
+ * (3) pointer to C function.
  */
-static void free_ssl(JNIEnv* env, jobject object) {
-    /*
-     * Preserve and restore the exception state around this call, so
-     * that GetIntField and SetIntField will operate without complaint.
-     */
-    jthrowable exception = env->ExceptionOccurred();
-
-    if (exception != NULL) {
-        env->ExceptionClear();
-    }
-
-    SSL *ssl = (SSL *)env->GetIntField(object, field_Socket_ssl);
-
-    if (ssl != NULL) {
-        sslDestroyAppData(ssl);
-        SSL_free(ssl);
-        env->SetIntField(object, field_Socket_ssl, (int) NULL);
-    }
-
-    if (exception != NULL) {
-        env->Throw(exception);
-    }
-}
+static JNINativeMethod sNativeCryptoMethods[] = {
+    { "clinit",              "()V",           (void*)NativeCrypto_clinit},
+    { "EVP_PKEY_new_DSA",    "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_DSA },
+    { "EVP_PKEY_new_RSA",    "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_RSA },
+    { "EVP_PKEY_free",       "(I)V",          (void*)NativeCrypto_EVP_PKEY_free },
+    { "EVP_new",             "()I",           (void*)NativeCrypto_EVP_new },
+    { "EVP_free",            "(I)V",          (void*)NativeCrypto_EVP_free },
+    { "EVP_DigestFinal",     "(I[BI)I",       (void*)NativeCrypto_EVP_DigestFinal },
+    { "EVP_DigestInit",      "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_DigestInit },
+    { "EVP_DigestBlockSize", "(I)I",          (void*)NativeCrypto_EVP_DigestBlockSize },
+    { "EVP_DigestSize",      "(I)I",          (void*)NativeCrypto_EVP_DigestSize },
+    { "EVP_DigestUpdate",    "(I[BII)V",      (void*)NativeCrypto_EVP_DigestUpdate },
+    { "EVP_VerifyInit",      "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_VerifyInit },
+    { "EVP_VerifyUpdate",    "(I[BII)V",      (void*)NativeCrypto_EVP_VerifyUpdate },
+    { "EVP_VerifyFinal",     "(I[BIII)I",     (void*)NativeCrypto_EVP_VerifyFinal },
+    { "SSL_CTX_new",         "()I",           (void*)NativeCrypto_SSL_CTX_new },
+    { "SSL_CTX_get_ciphers", "(I)[Ljava/lang/String;", (void*)NativeCrypto_SSL_CTX_get_ciphers},
+    { "SSL_CTX_free",        "(I)V",          (void*)NativeCrypto_SSL_CTX_free },
+    { "SSL_new",             "(ILjava/lang/String;Ljava/lang/String;[B)I", (void*)NativeCrypto_SSL_new},
+    { "SSL_get_options",     "(I)J",          (void*)NativeCrypto_SSL_get_options },
+    { "SSL_set_options",     "(IJ)J",         (void*)NativeCrypto_SSL_set_options },
+    { "SSL_get_ciphers",     "(I)[Ljava/lang/String;", (void*)NativeCrypto_SSL_get_ciphers},
+    { "SSL_set_cipher_list", "(ILjava/lang/String;)V", (void*)NativeCrypto_SSL_set_cipher_list},
+    { "SSL_free",            "(I)V",          (void*)NativeCrypto_SSL_free},
+};
 
 /**
- * Constructs the SSL struct for the given instance, replacing one
- * that was already made, if any.
+ * Module scope variables initialized during JNI registration.
  */
-static SSL* create_ssl(JNIEnv* env, jobject object, SSL_CTX*  ssl_ctx) {
-    free_ssl(env, object);
+static jfieldID field_Socket_mImpl;
+static jfieldID field_Socket_mFD;
 
-    SSL *ssl = SSL_new(ssl_ctx);
-    env->SetIntField(object, field_Socket_ssl, (int) ssl);
-    return ssl;
+// ============================================================================
+// === OpenSSL-related helper stuff begins here. ==============================
+// ============================================================================
+
+int get_socket_timeout(int type, int sd) {
+    struct timeval tv;
+    socklen_t len = sizeof(tv);
+    if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
+         LOGE("getsockopt(%d, SOL_SOCKET): %s (%d)",
+              sd,
+              strerror(errno),
+              errno);
+        return 0;
+    }
+    // LOGI("Current socket timeout (%d(s), %d(us))!",
+    //      (int)tv.tv_sec, (int)tv.tv_usec);
+    int timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+    return timeout;
 }
 
+#ifdef TIMEOUT_DEBUG_SSL
+
+void print_socket_timeout(const char* name, int type, int sd) {
+    struct timeval tv;
+    int len = sizeof(tv);
+    if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
+         LOGE("getsockopt(%d, SOL_SOCKET, %s): %s (%d)",
+              sd,
+              name,
+              strerror(errno),
+              errno);
+    }
+    LOGI("Current socket %s is (%d(s), %d(us))!",
+          name, (int)tv.tv_sec, (int)tv.tv_usec);
+}
+
+void print_timeout(const char* method, SSL* ssl) {
+    LOGI("SSL_get_default_timeout %d in %s", SSL_get_default_timeout(ssl), method);
+    int fd = SSL_get_fd(ssl);
+    print_socket_timeout("SO_RCVTIMEO", SO_RCVTIMEO, fd);
+    print_socket_timeout("SO_SNDTIMEO", SO_SNDTIMEO, fd);
+}
+
+#endif
+
 /**
  * Dark magic helper function that checks, for a given SSL session, whether it
  * can SSL_read() or SSL_write() without blocking. Takes into account any
@@ -851,15 +1119,15 @@
  * specifies whether we are waiting for readability or writability. It expects
  * to be passed either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, since we
  * only need to wait in case one of these problems occurs.
- * 
+ *
  * @param type Either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
  * @param fd The file descriptor to wait for (the underlying socket)
- * @param data The application data structure with mutex info etc. 
+ * @param data The application data structure with mutex info etc.
  * @param timeout The timeout value for select call, with the special value
  *                0 meaning no timeout at all (wait indefinitely). Note: This is
  *                the Java semantics of the timeout value, not the usual
  *                select() semantics.
- * @return The result of the inner select() call, -1 on additional errors 
+ * @return The result of the inner select() call, -1 on additional errors
  */
 static int sslSelect(int type, int fd, APP_DATA *data, int timeout) {
     fd_set rfds;
@@ -888,16 +1156,16 @@
     } else {
         ptv = NULL;
     }
-    
+
     // LOGD("Doing select() for SSL_ERROR_WANT_%s...", type == SSL_ERROR_WANT_READ ? "READ" : "WRITE");
     int result = select(max + 1, &rfds, &wfds, NULL, ptv);
     // LOGD("Returned from select(), result is %d", result);
-    
+
     // Lock
     if (MUTEX_LOCK(data->mutex) == -1) {
         return -1;
     }
-    
+
     // If we have been woken up by the emergency pipe, there must be a token in
     // it. Thus we can safely read it (even in a blocking way).
     if (FD_ISSET(data->fdsEmergency[0], &rfds)) {
@@ -910,7 +1178,7 @@
     // Tell the world that there is now one thread less waiting for the
     // underlying network.
     data->waitingThreads--;
-    
+
     // Unlock
     MUTEX_UNLOCK(data->mutex);
     // LOGD("leave sslSelect");
@@ -921,8 +1189,8 @@
  * Helper function that wakes up a thread blocked in select(), in case there is
  * one. Is being called by sslRead() and sslWrite() as well as by JNI glue
  * before closing the connection.
- * 
- * @param data The application data structure with mutex info etc. 
+ *
+ * @param data The application data structure with mutex info etc.
  */
 static void sslNotify(APP_DATA *data) {
     // Write a byte to the emergency pipe, so a concurrent select() can return.
@@ -940,7 +1208,7 @@
 /**
  * Helper function which does the actual reading. The Java layer guarantees that
  * at most one thread will enter this function at any given time.
- * 
+ *
  * @param ssl non-null; the SSL context
  * @param buf non-null; buffer to read into
  * @param len length of the buffer, in bytes
@@ -953,7 +1221,7 @@
         int* sslErrorCode, int timeout) {
 
     // LOGD("Entering sslRead, caller requests to read %d bytes...", len);
-    
+
     if (len == 0) {
         // Don't bother doing anything in this case.
         return 0;
@@ -961,7 +1229,7 @@
 
     int fd = SSL_get_fd(ssl);
     BIO *bio = SSL_get_rbio(ssl);
-    
+
     APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
 
     while (data->aliveAndKicking) {
@@ -973,7 +1241,7 @@
         }
 
         unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
-        
+
         // LOGD("Doing SSL_Read()");
         int result = SSL_read(ssl, buf, len);
         int error = SSL_ERROR_NONE;
@@ -989,13 +1257,13 @@
         if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) {
             sslNotify(data);
         }
-        
+
         // If we are blocked by the underlying socket, tell the world that
         // there will be one more waiting thread now.
         if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
             data->waitingThreads++;
         }
-        
+
         // Unlock
         MUTEX_UNLOCK(data->mutex);
 
@@ -1010,7 +1278,7 @@
                 return -1;
             }
 
-            // Need to wait for availability of underlying layer, then retry. 
+            // Need to wait for availability of underlying layer, then retry.
             case SSL_ERROR_WANT_READ:
             case SSL_ERROR_WANT_WRITE: {
                 int selectResult = sslSelect(error, fd, data, timeout);
@@ -1021,7 +1289,7 @@
                 } else if (selectResult == 0) {
                     return THROW_SOCKETTIMEOUTEXCEPTION;
                 }
-                
+
                 break;
             }
 
@@ -1033,16 +1301,16 @@
                 if (result == 0) {
                     return -1;
                 }
-                
+
                 // System call has been interrupted. Simply retry.
                 if (errno == EINTR) {
                     break;
                 }
-                
+
                 // Note that for all other system call errors we fall through
-                // to the default case, which results in an Exception. 
+                // to the default case, which results in an Exception.
             }
-            
+
             // Everything else is basically an error.
             default: {
                 *sslReturnCode = result;
@@ -1051,14 +1319,14 @@
             }
         }
     }
-    
+
     return -1;
 }
 
 /**
  * Helper function which does the actual writing. The Java layer guarantees that
  * at most one thread will enter this function at any given time.
- * 
+ *
  * @param ssl non-null; the SSL context
  * @param buf non-null; buffer to write
  * @param len length of the buffer, in bytes
@@ -1069,29 +1337,29 @@
  */
 static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode,
         int* sslErrorCode) {
-  
+
     // LOGD("Entering sslWrite(), caller requests to write %d bytes...", len);
 
     if (len == 0) {
         // Don't bother doing anything in this case.
         return 0;
     }
-    
+
     int fd = SSL_get_fd(ssl);
     BIO *bio = SSL_get_wbio(ssl);
-    
+
     APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
-    
+
     int count = len;
-    
-    while(data->aliveAndKicking && len > 0) {
+
+    while (data->aliveAndKicking && len > 0) {
         errno = 0;
         if (MUTEX_LOCK(data->mutex) == -1) {
             return -1;
         }
-        
+
         unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
-        
+
         // LOGD("Doing SSL_write() with %d bytes to go", len);
         int result = SSL_write(ssl, buf, len);
         int error = SSL_ERROR_NONE;
@@ -1107,15 +1375,15 @@
         if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) {
             sslNotify(data);
         }
-        
+
         // If we are blocked by the underlying socket, tell the world that
         // there will be one more waiting thread now.
         if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
             data->waitingThreads++;
         }
-        
+
         MUTEX_UNLOCK(data->mutex);
-        
+
         switch (error) {
              // Sucessfully write at least one byte.
             case SSL_ERROR_NONE: {
@@ -1128,7 +1396,7 @@
             case SSL_ERROR_ZERO_RETURN: {
                 return -1;
             }
-                
+
             // Need to wait for availability of underlying layer, then retry.
             // The concept of a write timeout doesn't really make sense, and
             // it's also not standard Java behavior, so we wait forever here.
@@ -1142,7 +1410,7 @@
                 } else if (selectResult == 0) {
                     return THROW_SOCKETTIMEOUTEXCEPTION;
                 }
-                
+
                 break;
             }
 
@@ -1154,16 +1422,16 @@
                 if (result == 0) {
                     return -1;
                 }
-                
+
                 // System call has been interrupted. Simply retry.
                 if (errno == EINTR) {
                     break;
                 }
-                
+
                 // Note that for all other system call errors we fall through
-                // to the default case, which results in an Exception. 
+                // to the default case, which results in an Exception.
             }
-            
+
             // Everything else is basically an error.
             default: {
                 *sslReturnCode = result;
@@ -1173,20 +1441,20 @@
         }
     }
     // LOGD("Successfully wrote %d bytes", count);
-    
+
     return count;
 }
 
 /**
  * Helper function that creates an RSA public key from two buffers containing
  * the big-endian bit representation of the modulus and the public exponent.
- * 
+ *
  * @param mod The data of the modulus
  * @param modLen The length of the modulus data
  * @param exp The data of the exponent
  * @param expLen The length of the exponent data
- * 
- * @return A pointer to the new RSA structure, or NULL on error 
+ *
+ * @return A pointer to the new RSA structure, or NULL on error
  */
 static RSA* rsaCreateKey(unsigned char* mod, int modLen, unsigned char* exp, int expLen) {
     // LOGD("Entering rsaCreateKey()");
@@ -1207,7 +1475,7 @@
 /**
  * Helper function that frees an RSA key. Just calls the corresponding OpenSSL
  * function.
- * 
+ *
  * @param rsa The pointer to the new RSA structure to free.
  */
 static void rsaFreeKey(RSA* rsa) {
@@ -1220,16 +1488,16 @@
 
 /**
  * Helper function that verifies a given RSA signature for a given message.
- * 
+ *
  * @param msg The message to verify
  * @param msgLen The length of the message
  * @param sig The signature to verify
  * @param sigLen The length of the signature
  * @param algorithm The name of the hash/sign algorithm to use, e.g. "RSA-SHA1"
  * @param rsa The RSA public key to use
- * 
+ *
  * @return 1 on success, 0 on failure, -1 on error (check SSL errors then)
- * 
+ *
  */
 static int rsaVerify(unsigned char* msg, unsigned int msgLen, unsigned char* sig,
                      unsigned int sigLen, char* algorithm, RSA* rsa) {
@@ -1248,7 +1516,7 @@
 
     EVP_MD_CTX ctx;
 
-    EVP_MD_CTX_init(&ctx);    
+    EVP_MD_CTX_init(&ctx);
     if (EVP_VerifyInit_ex(&ctx, type, NULL) == 0) {
         goto cleanup;
     }
@@ -1271,163 +1539,21 @@
 // ============================================================================
 
 /**
- * Initialization phase for every OpenSSL job: Loads the Error strings, the 
- * crypto algorithms and reset the OpenSSL library
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic(JNIEnv* env, jobject obj)
-{
-    SSL_load_error_strings();
-    ERR_load_crypto_strings();
-    SSL_library_init();
-    OpenSSL_add_all_algorithms();
-    THREAD_setup();
-}
-
-/**
- * Initialization phase for a socket with OpenSSL.  The server's private key
- * and X509 certificate are read and the Linux /dev/urandom file is loaded 
- * as RNG for the session keys.
- *  
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init(JNIEnv* env, jobject object,
-        jstring privatekey, jstring certificates, jbyteArray seed)
-{   
-    SSL_CTX* ssl_ctx;
-
-    // 'seed == null' when no SecureRandom Object is set
-    // in the SSLContext.
-    if (seed != NULL) {
-        jbyte* randseed = env->GetByteArrayElements(seed, NULL);
-        RAND_seed((unsigned char*) randseed, 1024);
-        env->ReleaseByteArrayElements(seed, randseed, 0);
-    } else {
-        RAND_load_file("/dev/urandom", 1024);
-    }
-
-    ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-
-    // Note: We explicitly do not allow SSLv2 to be used. It
-    SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
-
-    /* Java code in class OpenSSLSocketImpl does the verification. Meaning of 
-     * SSL_VERIFY_NONE flag in client mode: if not using an anonymous cipher
-     * (by default disabled), the server will send a certificate which will 
-     * be checked. The result of the certificate verification process can be  
-     * checked after the TLS/SSL handshake using the SSL_get_verify_result(3) 
-     * function. The handshake will be continued regardless of the 
-     * verification result.    
-     */
-    SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
-
-    int mode = SSL_CTX_get_mode(ssl_ctx);
-    /*
-     * Turn on "partial write" mode. This means that SSL_write() will
-     * behave like Posix write() and possibly return after only
-     * writing a partial buffer. Note: The alternative, perhaps
-     * surprisingly, is not that SSL_write() always does full writes
-     * but that it will force you to retry write calls having
-     * preserved the full state of the original call. (This is icky
-     * and undesirable.)
-     */
-    mode |= SSL_MODE_ENABLE_PARTIAL_WRITE;
-#if defined(SSL_MODE_SMALL_BUFFERS) /* not all SSL versions have this */
-    mode |= SSL_MODE_SMALL_BUFFERS;  /* lazily allocate record buffers; usually saves
-                                      * 44k over the default */
-#endif
-#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) /* not all SSL versions have this */
-    mode |= SSL_MODE_HANDSHAKE_CUTTHROUGH;  /* enable sending of client data as soon as
-                                             * ClientCCS and ClientFinished are sent */
-#endif
-
-    SSL_CTX_set_mode(ssl_ctx, mode);
-
-    if (privatekey != NULL) {
-        BIO* privatekeybio = stringToMemBuf(env, (jstring) privatekey);
-        EVP_PKEY* privatekeyevp =
-          PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
-        BIO_free(privatekeybio);
-
-        if (privatekeyevp == NULL) {
-            throwIOExceptionWithSslErrors(env, 0, 0,
-                    "Error parsing the private key");
-            SSL_CTX_free(ssl_ctx);
-            return;
-        }
-
-        BIO* certificatesbio = stringToMemBuf(env, (jstring) certificates);
-        X509* certificatesx509 =
-          PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
-        BIO_free(certificatesbio);
-
-        if (certificatesx509 == NULL) {
-            throwIOExceptionWithSslErrors(env, 0, 0,
-                    "Error parsing the certificates");
-            EVP_PKEY_free(privatekeyevp);
-            SSL_CTX_free(ssl_ctx);
-            return;
-        }
-
-        int ret = SSL_CTX_use_certificate(ssl_ctx, certificatesx509);
-        if (ret != 1) {
-            throwIOExceptionWithSslErrors(env, ret, 0,
-                    "Error setting the certificates");
-            X509_free(certificatesx509);
-            EVP_PKEY_free(privatekeyevp);
-            SSL_CTX_free(ssl_ctx);
-            return;
-        }
-
-        ret = SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp);
-        if (ret != 1) {
-            throwIOExceptionWithSslErrors(env, ret, 0,
-                    "Error setting the private key");
-            X509_free(certificatesx509);
-            EVP_PKEY_free(privatekeyevp);
-            SSL_CTX_free(ssl_ctx);
-            return;
-        }
-
-        ret = SSL_CTX_check_private_key(ssl_ctx);
-        if (ret != 1) {
-            throwIOExceptionWithSslErrors(env, ret, 0,
-                    "Error checking the private key");
-            X509_free(certificatesx509);
-            EVP_PKEY_free(privatekeyevp);
-            SSL_CTX_free(ssl_ctx);
-            return;
-        }
-    }
-
-    env->SetIntField(object, field_Socket_ssl_ctx, (int)ssl_ctx);
-}
-
-/**
  * A connection within an OpenSSL context is established. (1) A new socket is
- * constructed, (2) the TLS/SSL handshake with a server is initiated. 
+ * constructed, (2) the TLS/SSL handshake with a server is initiated.
  */
-static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect(JNIEnv* env, jobject object,
-        jint ctx, jobject socketObject, jboolean client_mode, jint session)
+static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect(JNIEnv* env, jclass,
+        jint ssl_address, jobject socketObject, jint timeout, jboolean client_mode, jint ssl_session_address)
 {
     // LOGD("ENTER connect");
-    int ret, fd;
-    SSL_CTX* ssl_ctx;
-    SSL* ssl;
-    SSL_SESSION* ssl_session;
-
-    ssl_ctx = (SSL_CTX*)env->GetIntField(object, field_Socket_ssl_ctx);
-
-    ssl = create_ssl(env, object, ssl_ctx);
+    SSL* ssl = getSslPointer(env, ssl_address, true);
     if (ssl == NULL) {
-        throwIOExceptionWithSslErrors(env, 0, 0,
-                "Unable to create SSL structure");
-        free_ssl_ctx(env, object);
-        return (jboolean) false;
+      return (jboolean) false;
     }
+    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
 
     jobject socketImplObject = env->GetObjectField(socketObject, field_Socket_mImpl);
     if (socketImplObject == NULL) {
-        free_ssl(env, object);
-        free_ssl_ctx(env, object);
         throwIOExceptionStr(env,
             "couldn't get the socket impl from the socket");
         return (jboolean) false;
@@ -1435,30 +1561,25 @@
 
     jobject fdObject = env->GetObjectField(socketImplObject, field_Socket_mFD);
     if (fdObject == NULL) {
-        free_ssl(env, object);
-        free_ssl_ctx(env, object);
         throwIOExceptionStr(env,
             "couldn't get the file descriptor from the socket impl");
         return (jboolean) false;
     }
 
-    fd = jniGetFDFromFileDescriptor(env, fdObject);
+    int fd = jniGetFDFromFileDescriptor(env, fdObject);
 
-    ssl_session = (SSL_SESSION *) session;
-
-    ret = SSL_set_fd(ssl, fd);
+    int ret = SSL_set_fd(ssl, fd);
 
     if (ret != 1) {
         throwIOExceptionWithSslErrors(env, ret, 0,
                 "Error setting the file descriptor");
-        free_ssl(env, object);
-        free_ssl_ctx(env, object);
+        SSL_clear(ssl);
         return (jboolean) false;
     }
 
     if (ssl_session != NULL) {
+        // LOGD("Trying to reuse session %p", ssl_session);
         ret = SSL_set_session(ssl, ssl_session);
-
         if (ret != 1) {
             /*
              * Translate the error, and throw if it turns out to be a real
@@ -1468,8 +1589,7 @@
             if (sslErrorCode != SSL_ERROR_ZERO_RETURN) {
                 throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
                         "SSL session set");
-                free_ssl(env, object);
-                free_ssl_ctx(env, object);
+                SSL_clear(ssl);
                 return (jboolean) false;
             }
         }
@@ -1482,8 +1602,7 @@
     int mode = fcntl(fd, F_GETFL);
     if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) {
         throwIOExceptionStr(env, "Unable to make socket non blocking");
-        free_ssl(env, object);
-        free_ssl_ctx(env, object);
+        SSL_clear(ssl);
         return (jboolean) false;
     }
 
@@ -1492,19 +1611,15 @@
      */
     if (sslCreateAppData(ssl) == -1) {
         throwIOExceptionStr(env, "Unable to create application data");
-        free_ssl(env, object);
-        free_ssl_ctx(env, object);
+        SSL_clear(ssl);
         // TODO
         return (jboolean) false;
     }
-    
+
     APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
-    env->SetIntField(object, field_Socket_ssl, (int)ssl);
-    
-    int timeout = (int)env->GetIntField(object, field_Socket_timeout);
-    
+
     while (data->aliveAndKicking) {
-        errno = 0;        
+        errno = 0;
         ret = SSL_connect(ssl);
         if (ret == 1) {
             break;
@@ -1525,26 +1640,24 @@
             if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
                 data->waitingThreads++;
                 int selectResult = sslSelect(error, fd, data, timeout);
-                
+
                 if (selectResult == -1) {
                     throwIOExceptionWithSslErrors(env, -1, error,
                         "Connect error");
-                    free_ssl(env, object);
-                    free_ssl_ctx(env, object);
+                    SSL_clear(ssl);
                     return (jboolean) false;
                 } else if (selectResult == 0) {
                     throwSocketTimeoutException(env, "SSL handshake timed out");
+                    SSL_clear(ssl);
                     freeSslErrorState();
-                    free_ssl(env, object);
-                    free_ssl_ctx(env, object);
                     return (jboolean) false;
                 }
             } else {
                 LOGE("Unknown error %d during connect", error);
                 break;
             }
-        }        
-    } 
+        }
+    }
 
     if (ret != 1) {
         /*
@@ -1555,80 +1668,72 @@
         if (sslErrorCode != SSL_ERROR_ZERO_RETURN) {
             throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
                     "SSL handshake failure");
-            free_ssl(env, object);
-            free_ssl_ctx(env, object);
+            SSL_clear(ssl);
             return (jboolean) false;
         }
     }
 
     if (ssl_session != NULL) {
         ret = SSL_session_reused(ssl);
-        // if (ret == 1) LOGD("A session was reused");
-        // else LOGD("A new session was negotiated");
+        // if (ret == 1) LOGD("Session %p was reused", ssl_session);
+        // else LOGD("Session %p was not reused, using new session %p", ssl_session, SSL_get_session(ssl));
         return (jboolean) ret;
     } else {
-        // LOGD("A new session was negotiated");
+        // LOGD("New session %p was negotiated", SSL_get_session(ssl));
         return (jboolean) 0;
     }
     // LOGD("LEAVE connect");
 }
 
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession(JNIEnv* env, jobject object,
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession(JNIEnv* env, jclass,
         jint jssl)
 {
-    return (jint) SSL_get1_session((SSL *) jssl);
+    return (jint) SSL_get1_session((SSL*) jssl);
 }
 
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEnv* env, jobject object,
-        jobject socketObject, jint jssl_ctx, jboolean client_mode)
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEnv* env, jclass,
+        jint ssl_address, jobject socketObject)
 {
-    int sd, ret;
-    BIO *bio;
-    SSL *ssl;
-    SSL_CTX *ssl_ctx;
-    jsse_ssl_app_data_t appdata;
+    SSL* serverSocketSsl = reinterpret_cast<SSL*>(static_cast<uintptr_t>(ssl_address));
+    if (serverSocketSsl == NULL) {
+        throwIOExceptionWithSslErrors(env, 0, 0,
+                "Unusable SSL structure");
+        return NULL;
+    }
 
-    ssl_ctx = (SSL_CTX *)jssl_ctx;
-
-    ssl = create_ssl(env, object, ssl_ctx);
+    SSL* ssl = SSL_dup(serverSocketSsl);
     if (ssl == NULL) {
         throwIOExceptionWithSslErrors(env, 0, 0,
                 "Unable to create SSL structure");
-        return;
+        return NULL;
     }
 
     jobject socketImplObject = env->GetObjectField(socketObject, field_Socket_mImpl);
     if (socketImplObject == NULL) {
-        free_ssl(env, object);
         throwIOExceptionStr(env, "couldn't get the socket impl from the socket");
-        return;
+        return NULL;
     }
 
     jobject fdObject = env->GetObjectField(socketImplObject, field_Socket_mFD);
     if (fdObject == NULL) {
-        free_ssl(env, object);
         throwIOExceptionStr(env, "couldn't get the file descriptor from the socket impl");
-        return;
+        return NULL;
     }
 
 
-    sd = jniGetFDFromFileDescriptor(env, fdObject);
+    int sd = jniGetFDFromFileDescriptor(env, fdObject);
 
-    bio = BIO_new_socket(sd, BIO_NOCLOSE);
-
-    /* The parameter client_mode must be 1 */
-    if (client_mode != 0)
-        client_mode = 1;
-    BIO_set_ssl_mode(bio, client_mode);
-
+    BIO* bio = BIO_new_socket(sd, BIO_NOCLOSE);
     SSL_set_bio(ssl, bio, bio);
 
     /*
-     * Fill in the appdata structure needed for the certificate callback and
-     * store this in the SSL application data slot.
+     * Fill in the stack allocated appdata structure needed for the
+     * certificate callback and store this in the SSL application data
+     * slot.
      */
+    jsse_ssl_app_data_t appdata;
     appdata.env = env;
-    appdata.object = object;
+    appdata.object = socketObject;
     SSL_set_app_data(ssl, &appdata);
 
     /*
@@ -1636,7 +1741,7 @@
      * Maybe we need to deal with all the special SSL error cases (WANT_*),
      * just like we do for SSL_connect(). But currently it is looking ok.
      */
-    ret = SSL_accept(ssl);
+    int ret = SSL_accept(ssl);
 
     /*
      * Clear the SSL application data slot again, so we can safely use it for
@@ -1654,14 +1759,14 @@
          */
         int sslErrorCode = SSL_get_error(ssl, ret);
         if (sslErrorCode == SSL_ERROR_NONE ||
-                (sslErrorCode == SSL_ERROR_SYSCALL && errno == 0)) {
-            throwIOExceptionStr(env, "Connection closed by peer");
+            (sslErrorCode == SSL_ERROR_SYSCALL && errno == 0)) {
+          throwIOExceptionStr(env, "Connection closed by peer");
         } else {
-            throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
-                    "Trouble accepting connection");
+          throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
+              "Trouble accepting connection");
     	}
-        free_ssl(env, object);
-        return;
+        SSL_clear(ssl);
+        return NULL;
     } else if (ret < 0) {
         /*
          * Translate the error and throw exception. We are sure it is an error
@@ -1670,8 +1775,8 @@
         int sslErrorCode = SSL_get_error(ssl, ret);
         throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
                 "Trouble accepting connection");
-        free_ssl(env, object);
-        return;
+        SSL_clear(ssl);
+        return NULL;
     }
 
     /*
@@ -1682,8 +1787,8 @@
     int mode = fcntl(fd, F_GETFL);
     if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) {
         throwIOExceptionStr(env, "Unable to make socket non blocking");
-        free_ssl(env, object);
-        return;
+        SSL_clear(ssl);
+        return NULL;
     }
 
     /*
@@ -1691,114 +1796,11 @@
      */
     if (sslCreateAppData(ssl) == -1) {
         throwIOExceptionStr(env, "Unable to create application data");
-        free_ssl(env, object);
-        return;
-    }
-}
-
-/**
- * Loads the desired protocol for the OpenSSL client and enables it.  
- * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols(JNIEnv* env, jobject object,
-        jlong protocol)
-{
-    if (protocol != 0x00000000L) {
-        if (protocol & SSL_OP_NO_SSLv3)
-            LOGD("SSL_OP_NO_SSLv3 is set");
-        if (protocol & SSL_OP_NO_TLSv1)
-            LOGD("SSL_OP_NO_TLSv1 is set");
-
-        SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_Socket_ssl_ctx);
-        int options = SSL_CTX_get_options(ctx);
-        options |= protocol; // Note: SSLv2 disabled earlier.
-        SSL_CTX_set_options(ctx, options);
-    }
-}
-
-static jobjectArray makeCipherList(JNIEnv* env, SSL* ssl) {
-    // Count the ciphers.
-    int cipherCount = 0;
-    while (SSL_get_cipher_list(ssl, cipherCount) != NULL) {
-        ++cipherCount;
-    }
-
-    // Create a String[].
-    jclass stringClass = env->FindClass("java/lang/String");
-    if (stringClass == NULL) {
-        return NULL;
-    }
-    jobjectArray array = env->NewObjectArray(cipherCount, stringClass, NULL);
-    if (array == NULL) {
+        SSL_clear(ssl);
         return NULL;
     }
 
-    // Fill in the cipher names.
-    for (int i = 0; i < cipherCount; ++i) {
-        const char* c = SSL_get_cipher_list(ssl, i);
-        env->SetObjectArrayElement(array, i, env->NewStringUTF(c));
-    }
-    return array;
-}
-
-jobjectArray makeCipherList(JNIEnv* env, SSL_CTX* ssl_ctx) {
-    SSL* ssl = SSL_new(ssl_ctx);
-    if (ssl == NULL) {
-        return NULL;
-    }
-    jobjectArray result = makeCipherList(env, ssl);
-    SSL_free(ssl);
-    return result;
-}
-
-/**
- * Loads the ciphers suites that are supported by the OpenSSL client
- * and returns them in a string array.
- */
-static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites(JNIEnv* env,
-        jobject object)
-{
-    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-    if (ssl_ctx == NULL) {
-        return NULL;
-    }
-    jobjectArray result = makeCipherList(env, ssl_ctx);
-    SSL_CTX_free(ssl_ctx);
-    return result;
-}
-
-/**
- * Loads the ciphers suites that are enabled in the OpenSSL client
- * and returns them in a string array.
- */
-static jobjectArray OpenSSLSocketImpl_nativeGetEnabledCipherSuites(JNIEnv* env,
-        jclass, jint ssl_ctx_address)
-{
-    SSL_CTX* ssl_ctx =
-            reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
-    return makeCipherList(env, ssl_ctx);
-}
-
-void setEnabledCipherSuites(JNIEnv* env, jstring controlString, SSL_CTX* ssl_ctx) {
-    const char* str = env->GetStringUTFChars(controlString, NULL);
-    int rc = SSL_CTX_set_cipher_list(ssl_ctx, str);
-    env->ReleaseStringUTFChars(controlString, str);
-    if (rc == 0) {
-        freeSslErrorState();
-        jniThrowException(env, "java/lang/IllegalArgumentException",
-                          "Illegal cipher suite strings.");
-    }
-}
-
-/**
- * Sets the ciphers suites that are enabled in the OpenSSL client.
- */
-static void OpenSSLSocketImpl_nativeSetEnabledCipherSuites(JNIEnv* env, jclass,
-        jint ssl_ctx_address, jstring controlString)
-{
-    SSL_CTX* ssl_ctx =
-            reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
-    setEnabledCipherSuites(env, controlString, ssl_ctx);
+    return (jint) ssl;
 }
 
 #define SSL_AUTH_MASK           0x00007F00L
@@ -1815,24 +1817,18 @@
  * Sets  the client's crypto algorithms and authentication methods.
  */
 static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod(JNIEnv* env,
-        jobject object)
+        jclass, jint ssl_address)
 {
-    SSL* ssl;
-    SSL_CIPHER *cipher;
-    jstring ret;
-    char buf[512];
-    unsigned long alg;
-    const char *au;
-
-    ssl = getSslPointer(env, object, true);
+    SSL* ssl = getSslPointer(env, ssl_address, true);
     if (ssl == NULL) {
         return NULL;
     }
 
-    cipher = SSL_get_current_cipher(ssl);
+    SSL_CIPHER* cipher = SSL_get_current_cipher(ssl);
 
-    alg = cipher->algorithms;
+    unsigned long alg = cipher->algorithms;
 
+    const char *au;
     switch (alg&SSL_AUTH_MASK) {
         case SSL_aRSA:
             au="RSA";
@@ -1857,7 +1853,7 @@
             break;
     }
 
-    ret = env->NewStringUTF(au);
+    jstring ret = env->NewStringUTF(au);
 
     return ret;
 }
@@ -1865,9 +1861,9 @@
 /**
  * OpenSSL read function (1): only one chunk is read (returned as jint).
  */
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv* env, jobject object, jint timeout)
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv* env, jclass, jint ssl_address, jint timeout)
 {
-    SSL *ssl = getSslPointer(env, object, true);
+    SSL* ssl = getSslPointer(env, ssl_address, true);
     if (ssl == NULL) {
         return 0;
     }
@@ -1897,12 +1893,12 @@
 }
 
 /**
- * OpenSSL read function (2): read into buffer at offset n chunks. 
+ * OpenSSL read function (2): read into buffer at offset n chunks.
  * Returns 1 (success) or value <= 0 (failure).
  */
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEnv* env, jobject obj, jbyteArray dest, jint offset, jint len, jint timeout)
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEnv* env, jclass, jint ssl_address, jbyteArray dest, jint offset, jint len, jint timeout)
 {
-    SSL *ssl = getSslPointer(env, obj, true);
+    SSL* ssl = getSslPointer(env, ssl_address, true);
     if (ssl == NULL) {
         return 0;
     }
@@ -1932,9 +1928,9 @@
 /**
  * OpenSSL write function (1): only one chunk is written.
  */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv* env, jobject object, jint b)
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv* env, jclass, jint ssl_address, jint b)
 {
-    SSL *ssl = getSslPointer(env, object, true);
+    SSL* ssl = getSslPointer(env, ssl_address, true);
     if (ssl == NULL) {
         return;
     }
@@ -1954,12 +1950,12 @@
 }
 
 /**
- * OpenSSL write function (2): write into buffer at offset n chunks. 
+ * OpenSSL write function (2): write into buffer at offset n chunks.
  */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIEnv* env, jobject obj,
-        jbyteArray dest, jint offset, jint len)
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIEnv* env, jclass,
+        jint ssl_address, jbyteArray dest, jint offset, jint len)
 {
-    SSL *ssl = getSslPointer(env, obj, true);
+    SSL* ssl = getSslPointer(env, ssl_address, true);
     if (ssl == NULL) {
         return;
     }
@@ -1967,8 +1963,7 @@
     jbyte* bytes = env->GetByteArrayElements(dest, NULL);
     int returnCode = 0;
     int errorCode = 0;
-    int timeout = (int)env->GetIntField(obj, field_Socket_timeout);
-    int ret = sslWrite(ssl, (const char *) (bytes + offset), len, 
+    int ret = sslWrite(ssl, (const char *) (bytes + offset), len,
             &returnCode, &errorCode);
 
     env->ReleaseByteArrayElements(dest, bytes, 0);
@@ -1983,11 +1978,11 @@
 }
 
 /**
- * Interrupt any pending IO before closing the socket. 
+ * Interrupt any pending IO before closing the socket.
  */
 static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt(
-        JNIEnv* env, jobject object) {
-    SSL *ssl = getSslPointer(env, object, false);
+        JNIEnv* env, jclass, jint ssl_address) {
+    SSL* ssl = getSslPointer(env, ssl_address, false);
     if (ssl == NULL) {
         return;
     }
@@ -2007,11 +2002,11 @@
 }
 
 /**
- * OpenSSL close SSL socket function. 
+ * OpenSSL close SSL socket function.
  */
 static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close(
-        JNIEnv* env, jobject object) {
-    SSL *ssl = getSslPointer(env, object, false);
+        JNIEnv* env, jclass, jint ssl_address) {
+    SSL* ssl = getSslPointer(env, ssl_address, false);
     if (ssl == NULL) {
         return;
     }
@@ -2051,23 +2046,13 @@
              * Everything else is a real error condition. We should
              * let the Java layer know about this by throwing an
              * exception.
-             */ 
+             */
             throwIOExceptionWithSslErrors(env, ret, 0, "SSL shutdown failed.");
             break;
     }
 
+    SSL_clear(ssl);
     freeSslErrorState();
-    free_ssl(env, object);
-    free_ssl_ctx(env, object);
-}    
-
-/**
- * OpenSSL free SSL socket function. 
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free(JNIEnv* env, jobject object)
-{
-    free_ssl(env, object);
-    free_ssl_ctx(env, object);
 }
 
 /**
@@ -2130,151 +2115,20 @@
 
 static JNINativeMethod sSocketImplMethods[] =
 {
-    {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic},
-    {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init},
-    {"nativeconnect", "(ILjava/net/Socket;ZI)Z", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect},
+    {"nativeconnect", "(ILjava/net/Socket;IZI)Z", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect},
     {"nativegetsslsession", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession},
-    {"nativeread", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read},
-    {"nativeread", "([BIII)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba},
-    {"nativewrite", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write},
-    {"nativewrite", "([BII)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba},
-    {"nativeaccept", "(Ljava/net/Socket;IZ)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept},
-    {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols},
-    {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites},
-    {"nativeGetEnabledCipherSuites", "(I)[Ljava/lang/String;", (void*) OpenSSLSocketImpl_nativeGetEnabledCipherSuites},
-    {"nativeSetEnabledCipherSuites", "(ILjava/lang/String;)V", (void*) OpenSSLSocketImpl_nativeSetEnabledCipherSuites},
-    {"nativecipherauthenticationmethod", "()Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod},
-    {"nativeinterrupt", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt},
-    {"nativeclose", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close},
-    {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free},
+    {"nativeread", "(II)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read},
+    {"nativeread", "(I[BIII)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba},
+    {"nativewrite", "(II)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write},
+    {"nativewrite", "(I[BII)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba},
+    {"nativeaccept", "(ILjava/net/Socket;)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept},
+    {"nativecipherauthenticationmethod", "(I)Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod},
+    {"nativeinterrupt", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt},
+    {"nativeclose", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close},
     {"nativeverifysignature", "([B[BLjava/lang/String;[B[B)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignature},
 };
 
 /**
- * Module scope variables initialized during JNI registration.
- */
-static jfieldID field_ServerSocket_ssl_ctx;
-
-/**
- * Initialization phase of OpenSSL: Loads the Error strings, the crypto algorithms and reset the OpenSSL library
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic(JNIEnv* env, jobject obj)
-{
-    SSL_load_error_strings();
-    ERR_load_crypto_strings();
-    SSL_library_init();
-    OpenSSL_add_all_algorithms();
-}
-
-/**
- * Initialization phase for a server socket with OpenSSL.  The server's private key and X509 certificate are read and  
- * the Linux /dev/random file is loaded as RNG for the session keys.
- *  
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init(JNIEnv* env, jobject object,
-        jstring privatekey, jstring certificates, jbyteArray seed)
-{
-    SSL_CTX *ssl_ctx;
-    const char *privatekeychar;
-    const char *certificateschar;
-    EVP_PKEY * privatekeyevp;
-
-    BIO *privatekeybio;
-    BIO *certificatesbio;
-
-    // 'seed == null' when no SecureRandom Object is set
-    // in the SSLContext.
-    if (seed != NULL) {
-        jbyte* randseed = env->GetByteArrayElements(seed, NULL);
-        RAND_seed((unsigned char*) randseed, 1024);
-        env->ReleaseByteArrayElements(seed, randseed, 0);
-    } else {
-        RAND_load_file("/dev/urandom", 1024);
-    }
-
-    ssl_ctx = SSL_CTX_new(SSLv23_server_method());
-    SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
-
-    privatekeychar = env->GetStringUTFChars((jstring)privatekey, NULL);
-    privatekeybio = BIO_new_mem_buf((void*)privatekeychar, -1);
-
-    privatekeyevp = PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
-    env->ReleaseStringUTFChars(privatekey, privatekeychar);
-
-    if (privatekeyevp == NULL) {
-        LOGE(ERR_error_string(ERR_get_error(), NULL));
-        throwIOExceptionStr(env, "Error parsing the private key");
-        return;
-    }
-
-    certificateschar = env->GetStringUTFChars((jstring)certificates, NULL);
-    certificatesbio = BIO_new_mem_buf((void*)certificateschar, -1);
-
-    X509 * certificatesx509 = PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
-    env->ReleaseStringUTFChars(certificates, certificateschar);
-
-    if (certificatesx509 == NULL) {
-        LOGE(ERR_error_string(ERR_get_error(), NULL));
-        throwIOExceptionStr(env, "Error parsing the certificates");
-        return;
-    }
-
-    if (!SSL_CTX_use_certificate(ssl_ctx, certificatesx509)) {
-        LOGE(ERR_error_string(ERR_get_error(), NULL));
-        throwIOExceptionStr(env, "Error setting the certificates");
-        return;
-    }
-
-    if (!SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp)) {
-        LOGE(ERR_error_string(ERR_get_error(), NULL));
-        throwIOExceptionStr(env, "Error setting the private key");
-        return;
-    }
-
-    if (!SSL_CTX_check_private_key(ssl_ctx)) {
-        LOGE(ERR_error_string(ERR_get_error(), NULL));
-        throwIOExceptionStr(env, "Error checking private key");
-        return;
-    }
-
-    env->SetIntField(object, field_ServerSocket_ssl_ctx, (int)ssl_ctx);
-}
-
-/**
- * Loads the desired protocol for the OpenSSL server and enables it.  
- * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols(JNIEnv* env,
-        jobject object, jlong protocol)
-{
-    if (protocol != 0x00000000L) {
-        if (protocol & SSL_OP_NO_SSLv3)
-            LOGD("SSL_OP_NO_SSLv3 is set");
-        if (protocol & SSL_OP_NO_TLSv1)
-            LOGD("SSL_OP_NO_TLSv1 is set");
-
-        SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_ServerSocket_ssl_ctx);
-        SSL_CTX_set_options((SSL_CTX*)ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|(long)protocol);
-    }
-}
-
-/**
- * Loads the ciphers suites that are supported by the OpenSSL server
- * and returns them in a string array.
- */
-static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites(JNIEnv* env,
-        jobject object)
-{
-    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_server_method());
-    if (ssl_ctx == NULL) {
-        return NULL;
-    }
-    jobjectArray result = makeCipherList(env, ssl_ctx);
-    SSL_CTX_free(ssl_ctx);
-    return result;
-}
-
-/**
  * Gives an array back containing all the X509 certificate's bytes.
  */
 static jobjectArray getcertificatebytes(JNIEnv* env,
@@ -2338,79 +2192,64 @@
  */
 static int verify_callback(int preverify_ok, X509_STORE_CTX *x509_store_ctx)
 {
-    SSL *ssl;
-    jsse_ssl_app_data_t *appdata;
-    jclass cls;
-
-    jobjectArray objectArray;
-
     /* Get the correct index to the SSLobject stored into X509_STORE_CTX. */
-    ssl = (SSL*)X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+    SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
 
-    appdata = (jsse_ssl_app_data_t*)SSL_get_app_data(ssl);
+    jsse_ssl_app_data_t* appdata = (jsse_ssl_app_data_t*)SSL_get_app_data(ssl);
 
-    cls = appdata->env->GetObjectClass(appdata->object);
+    jclass cls = appdata->env->GetObjectClass(appdata->object);
 
-    jmethodID methodID = appdata->env->GetMethodID(cls, "verify_callback", "([[B)I");
+    jmethodID methodID = appdata->env->GetMethodID(cls, "verifyCertificateChain", "([[B)Z");
 
-    objectArray = getcertificatebytes(appdata->env, x509_store_ctx->untrusted);
+    jobjectArray objectArray = getcertificatebytes(appdata->env, x509_store_ctx->untrusted);
 
-    appdata->env->CallIntMethod(appdata->object, methodID, objectArray);
+    jboolean verified = appdata->env->CallBooleanMethod(appdata->object, methodID, objectArray);
 
-    return 1;
+    return (verified) ? 1 : 0;
 }
 
 /**
  * Sets  the client's credentials and the depth of theirs verification.
  */
 static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth(JNIEnv* env,
-        jobject object, jint value)
+        jclass, jint ssl_address, jint value)
 {
-    SSL_CTX *ssl_ctx = (SSL_CTX *)env->GetIntField(object, field_ServerSocket_ssl_ctx);
-    SSL_CTX_set_verify(ssl_ctx, (int)value, verify_callback);
-}
-
-/**
- * The actual SSL context is reset.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree(JNIEnv* env, jobject object)
-{
-    SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_ServerSocket_ssl_ctx);
-    SSL_CTX_free(ctx);
-    env->SetIntField(object, field_ServerSocket_ssl_ctx, 0);
+    SSL* ssl = getSslPointer(env, ssl_address, true);
+    if (ssl == NULL) {
+      return;
+    }
+    SSL_set_verify(ssl, (int)value, verify_callback);
 }
 
 static JNINativeMethod sServerSocketImplMethods[] =
 {
-    {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic},
-    {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init},
-    {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols},
-    {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites},
-    {"nativesetclientauth", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth},
-    {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree}
+    {"nativesetclientauth", "(II)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth},
 };
 
-static jfieldID field_Session_session;
-
-static SSL_SESSION* getSslSessionPointer(JNIEnv* env, jobject object) {
-    return reinterpret_cast<SSL_SESSION*>(env->GetIntField(object, field_Session_session));
+/**
+ * Our implementation of what might be considered
+ * SSL_SESSION_get_peer_cert_chain
+ */
+static STACK_OF(X509)* SSL_SESSION_get_peer_cert_chain(SSL_CTX* ssl_ctx, SSL_SESSION* ssl_session) {
+    SSL* ssl = SSL_new(ssl_ctx);
+    SSL_set_session(ssl, ssl_session);
+    STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
+    SSL_free(ssl);
+    return chain;
 }
 
 // Fills a byte[][] with the peer certificates in the chain.
 static jobjectArray OpenSSLSessionImpl_getPeerCertificatesImpl(JNIEnv* env,
-        jobject object, jint jssl)
+        jclass, jint ssl_ctx_address, jint ssl_session_address)
 {
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
-    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-    SSL* ssl = SSL_new(ssl_ctx);
-
-    SSL_set_session(ssl, ssl_session);
-
-    STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
+    SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+    if (ssl_ctx == NULL) {
+        jniThrowNullPointerException(env, "SSL_CTX is null");
+        return NULL;
+    }
+    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
+    STACK_OF(X509)* chain = SSL_SESSION_get_peer_cert_chain(ssl_ctx, ssl_session);
     jobjectArray objectArray = getcertificatebytes(env, chain);
-
-    SSL_free(ssl);
-    SSL_CTX_free(ssl_ctx);
     return objectArray;
 }
 
@@ -2419,8 +2258,8 @@
  * not certificates). Returns a byte[] containing the DER-encoded state.
  * See apache mod_ssl.
  */
-static jbyteArray OpenSSLSessionImpl_getEncoded(JNIEnv* env, jobject object) {
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+static jbyteArray OpenSSLSessionImpl_getEncoded(JNIEnv* env, jclass, jint ssl_session_address) {
+    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
     if (ssl_session == NULL) {
         return NULL;
     }
@@ -2445,7 +2284,7 @@
 /**
  * Deserialize the session.
  */
-static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jobject object, jbyteArray bytes, jint size) {
+static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jclass, jbyteArray bytes, jint size) {
     if (bytes == NULL) {
         return 0;
     }
@@ -2461,8 +2300,8 @@
 /**
  * Gets and returns in a byte array the ID of the actual SSL session.
  */
-static jbyteArray OpenSSLSessionImpl_getId(JNIEnv* env, jobject object) {
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+static jbyteArray OpenSSLSessionImpl_getId(JNIEnv* env, jclass, jint ssl_session_address) {
+    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
 
     jbyteArray result = env->NewByteArray(ssl_session->session_id_length);
     if (result != NULL) {
@@ -2477,68 +2316,61 @@
  * Gets and returns in a long integer the creation's time of the
  * actual SSL session.
  */
-static jlong OpenSSLSessionImpl_getCreationTime(JNIEnv* env, jobject object) {
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+static jlong OpenSSLSessionImpl_getCreationTime(JNIEnv* env, jclass, jint ssl_session_address) {
+    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
     jlong result = SSL_SESSION_get_time(ssl_session);
     result *= 1000; // OpenSSL uses seconds, Java uses milliseconds.
     return result;
 }
 
 /**
+ * Our implementation of what might be considered
+ * SSL_SESSION_get_version, based on SSL_get_version.
+ * See get_ssl_version above.
+ */
+static const char* SSL_SESSION_get_version(SSL_SESSION* ssl_session) {
+  return get_ssl_version(ssl_session->ssl_version);
+}
+
+/**
  * Gets and returns in a string the version of the SSL protocol. If it
  * returns the string "unknown" it means that no connection is established.
  */
-static jstring OpenSSLSessionImpl_getProtocol(JNIEnv* env, jobject object) {
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
-    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-    SSL* ssl = SSL_new(ssl_ctx);
-
-    SSL_set_session(ssl, ssl_session);
-
-    const char* protocol = SSL_get_version(ssl);
+static jstring OpenSSLSessionImpl_getProtocol(JNIEnv* env, jclass, jint ssl_session_address) {
+    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
+    const char* protocol = SSL_SESSION_get_version(ssl_session);
     jstring result = env->NewStringUTF(protocol);
-
-    SSL_free(ssl);
-    SSL_CTX_free(ssl_ctx);
     return result;
 }
 
 /**
  * Gets and returns in a string the set of ciphers the actual SSL session uses.
  */
-static jstring OpenSSLSessionImpl_getCipherSuite(JNIEnv* env, jobject object) {
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
-    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-    SSL* ssl = SSL_new(ssl_ctx);
-
-    SSL_set_session(ssl, ssl_session);
-
-    SSL_CIPHER* cipher = SSL_get_current_cipher(ssl);
+static jstring OpenSSLSessionImpl_getCipherSuite(JNIEnv* env, jclass, jint ssl_session_address) {
+    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
+    SSL_CIPHER* cipher = ssl_session->cipher;
     jstring result = env->NewStringUTF(SSL_CIPHER_get_name(cipher));
-
-    SSL_free(ssl);
-    SSL_CTX_free(ssl_ctx);
     return result;
 }
 
 /**
  * Frees the SSL session.
  */
-static void OpenSSLSessionImpl_freeImpl(JNIEnv* env, jobject object, jint session) {
-    LOGD("Freeing OpenSSL session");
+static void OpenSSLSessionImpl_freeImpl(JNIEnv* env, jclass, jint session) {
     SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(session);
+    // LOGD("Freeing OpenSSL session %p", session);
     SSL_SESSION_free(ssl_session);
 }
 
 static JNINativeMethod sSessionImplMethods[] = {
     { "freeImpl", "(I)V", (void*) OpenSSLSessionImpl_freeImpl },
-    { "getCipherSuite", "()Ljava/lang/String;", (void*) OpenSSLSessionImpl_getCipherSuite },
-    { "getCreationTime", "()J", (void*) OpenSSLSessionImpl_getCreationTime },
-    { "getEncoded", "()[B", (void*) OpenSSLSessionImpl_getEncoded },
-    { "getId", "()[B", (void*) OpenSSLSessionImpl_getId },
-    { "getPeerCertificatesImpl", "()[[B", (void*) OpenSSLSessionImpl_getPeerCertificatesImpl },
-    { "getProtocol", "()Ljava/lang/String;", (void*) OpenSSLSessionImpl_getProtocol },
-    { "initializeNativeImpl", "([BI)I", (void*) OpenSSLSessionImpl_initializeNativeImpl }
+    { "getCipherSuite", "(I)Ljava/lang/String;", (void*) OpenSSLSessionImpl_getCipherSuite },
+    { "getCreationTime", "(I)J", (void*) OpenSSLSessionImpl_getCreationTime },
+    { "getEncoded", "(I)[B", (void*) OpenSSLSessionImpl_getEncoded },
+    { "getId", "(I)[B", (void*) OpenSSLSessionImpl_getId },
+    { "getPeerCertificatesImpl", "(II)[[B", (void*) OpenSSLSessionImpl_getPeerCertificatesImpl },
+    { "getProtocol", "(I)Ljava/lang/String;", (void*) OpenSSLSessionImpl_getProtocol },
+    { "initializeNativeImpl", "([BI)I", (void*) OpenSSLSessionImpl_initializeNativeImpl },
 };
 
 typedef struct {
@@ -2573,18 +2405,6 @@
         }
     }
 
-    // java.io.FileDescriptor
-    jclass fileDescriptor = env->FindClass("java/io/FileDescriptor");
-    if (fileDescriptor == NULL) {
-        LOGE("Can't find java/io/FileDescriptor");
-        return -1;
-    }
-    field_FileDescriptor_descriptor = env->GetFieldID(fileDescriptor, "descriptor", "I");
-    if (field_FileDescriptor_descriptor == NULL) {
-        LOGE("Can't find FileDescriptor.descriptor");
-        return -1;
-    }
-
     // java.net.Socket
     jclass socket = env->FindClass("java/net/Socket");
     if (socket == NULL) {
@@ -2609,58 +2429,5 @@
         return -1;
     }
 
-    // org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl
-    jclass socketImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl");
-    if (socketImpl == NULL) {
-        LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl");
-        return -1;
-    }
-    // Note: do these after the registration of native methods, because 
-    // there is a static method "initstatic" that's called when the
-    // OpenSSLSocketImpl class is first loaded, and that required
-    // a native method to be associated with it.
-    field_Socket_ssl_ctx = env->GetFieldID(socketImpl, "ssl_ctx", "I");
-    if (field_Socket_ssl_ctx == NULL) {
-      LOGE("Can't find OpenSSLSocketImpl.ssl_ctx");
-      return -1;
-    }
-    field_Socket_ssl = env->GetFieldID(socketImpl, "ssl", "I");
-    if (field_Socket_ssl == NULL) {
-      LOGE("Can't find OpenSSLSocketImpl.ssl");
-      return -1;
-    }
-    field_Socket_timeout = env->GetFieldID(socketImpl, "timeout", "I");
-    if (field_Socket_timeout == NULL) {
-      LOGE("Can't find OpenSSLSocketImpl.timeout");
-      return -1;
-    }
-
-    // org.apache.harmony.xnet.provider.jsse.OpenSSLServerSocketImpl
-    jclass serverSocketImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl");
-    if (serverSocketImpl == NULL) {
-        LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl");
-        return -1;
-    }
-    // Note: do these after the registration of native methods, because
-    // there is a static method "initstatic" that's called when the
-    // OpenSSLServerSocketImpl class is first loaded, and that required
-    // a native method to be associated with it.
-    field_ServerSocket_ssl_ctx = env->GetFieldID(serverSocketImpl, "ssl_ctx", "I");
-    if (field_ServerSocket_ssl_ctx == NULL) {
-      LOGE("Can't find OpenSSLServerSocketImpl.ssl_ctx");
-      return -1;
-    }
-
-    // org.apache.harmony.xnet.provider.jsse.OpenSSLSessionImpl
-    jclass sessionImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl");
-    if (sessionImpl == NULL) {
-        return -1;
-    }
-    field_Session_session = env->GetFieldID(sessionImpl, "session", "I");
-    if (field_Session_session == NULL) {
-      LOGE("Can't find OpenSSLSessionImpl.session");
-      return -1;
-    }
-
     return 0;
 }
diff --git a/libdex/InstrUtils.h b/libdex/InstrUtils.h
index 0c4e162..60244a8 100644
--- a/libdex/InstrUtils.h
+++ b/libdex/InstrUtils.h
@@ -90,8 +90,8 @@
 typedef signed char InstructionWidth;
 
 /*
- * Instruction flags, used by the verifier to determine where control
- * can flow to next.
+ * Instruction flags, used by the verifier and JIT to determine where
+ * control can flow to next.  Expected to fit in 8 bits.
  */
 typedef unsigned char InstructionFlags;
 enum InstructionFlags {
diff --git a/vm/Dalvik.h b/vm/Dalvik.h
index 054b838..27fa316 100644
--- a/vm/Dalvik.h
+++ b/vm/Dalvik.h
@@ -67,7 +67,7 @@
 #include "JniInternal.h"
 #include "LinearAlloc.h"
 #include "analysis/DexVerify.h"
-#include "analysis/DexOptimize.h"
+#include "analysis/DexPrepare.h"
 #include "analysis/RegisterMap.h"
 #include "Init.h"
 #include "libdex/OpCode.h"
diff --git a/vm/Dvm.mk b/vm/Dvm.mk
index cf0d0c9..a059b41 100644
--- a/vm/Dvm.mk
+++ b/vm/Dvm.mk
@@ -138,8 +138,9 @@
 	alloc/DdmHeap.c \
 	alloc/Verify.c \
 	analysis/CodeVerify.c \
-	analysis/DexOptimize.c \
+	analysis/DexPrepare.c \
 	analysis/DexVerify.c \
+	analysis/Optimize.c \
 	analysis/ReduceConstants.c \
 	analysis/RegisterMap.c \
 	analysis/VerifySubs.c \
diff --git a/vm/Globals.h b/vm/Globals.h
index 07bf522..551ca1f 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -425,9 +425,6 @@
     ReferenceTable  jniPinRefTable;
     pthread_mutex_t jniPinRefLock;
 
-    /* special ReferenceQueue for JNI weak globals */
-    Object*     jniWeakGlobalRefQueue;
-
     /*
      * Native shared library table.
      */
diff --git a/vm/Jni.c b/vm/Jni.c
index 6692f3b..15b0294 100644
--- a/vm/Jni.c
+++ b/vm/Jni.c
@@ -918,53 +918,6 @@
     dvmUnlockMutex(&gDvm.jniGlobalRefLock);
 }
 
-
-/*
- * Get the "magic" JNI weak global ReferenceQueue.  It's allocated on
- * first use.
- *
- * Returns NULL with an exception raised if allocation fails.
- */
-static Object* getWeakGlobalRefQueue(void)
-{
-    /* use an indirect variable to avoid "type-punned pointer" complaints */
-    Object** pGlobalQ = &gDvm.jniWeakGlobalRefQueue;
-
-    if (*pGlobalQ != NULL)
-        return *pGlobalQ;
-
-    ClassObject* clazz = dvmFindSystemClass("Ljava/lang/ref/ReferenceQueue;");
-    if (clazz == NULL) {
-        LOGE("Unable to find java.lang.ref.ReferenceQueue");
-        dvmAbort();
-    }
-
-    /*
-     * Create an instance of ReferenceQueue.  The object is never actually
-     * used for anything, so we don't need to call a constructor.  (We could
-     * get away with using an instance of Object, but this is cleaner.)
-     */
-    Object* queue = dvmAllocObject(clazz, ALLOC_DEFAULT);
-    if (queue == NULL) {
-        LOGW("Failed allocating weak global ref queue\n");
-        assert(dvmCheckException(dvmThreadSelf()));
-        return NULL;
-    }
-    dvmReleaseTrackedAlloc(queue, NULL);
-
-    /*
-     * Save it, using atomic ops to ensure we don't double-up.  The gDvm
-     * field is known to the GC.
-     */
-    if (!ATOMIC_CMP_SWAP((int*) pGlobalQ, 0, (int) queue)) {
-        LOGD("WOW: lost race to create weak global ref queue\n");
-        queue = *pGlobalQ;
-    }
-
-    return queue;
-}
-
-
 /*
  * We create a PhantomReference that references the object, add a
  * global reference to it, and then flip some bits before returning it.
@@ -980,7 +933,6 @@
 
     Thread* self = ((JNIEnvExt*)env)->self;
     Object* obj = dvmDecodeIndirectRef(env, jobj);
-    Object* weakGlobalQueue = getWeakGlobalRefQueue();
     Object* phantomObj;
     jobject phantomRef;
 
@@ -1004,7 +956,7 @@
 
     JValue unused;
     dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj,
-        &unused, jobj, weakGlobalQueue);
+        &unused, jobj, NULL);
     dvmReleaseTrackedAlloc(phantomObj, self);
 
     if (dvmCheckException(self)) {
diff --git a/vm/Sync.c b/vm/Sync.c
index c00a630..3b13159 100644
--- a/vm/Sync.c
+++ b/vm/Sync.c
@@ -310,7 +310,8 @@
      * the object, in which case we've got some bad
      * native code somewhere.
      */
-    assert(dvmTryLockMutex(&mon->lock) == 0);
+    assert(pthread_mutex_trylock(&mon->lock) == 0);
+    assert(pthread_mutex_unlock(&mon->lock) == 0);
     dvmDestroyMutex(&mon->lock);
 #ifdef WITH_DEADLOCK_PREDICTION
     expandObjClear(&mon->historyChildren);
@@ -399,8 +400,7 @@
 static bool unlockMonitor(Thread* self, Monitor* mon)
 {
     assert(self != NULL);
-    assert(mon != NULL);        // can this happen?
-
+    assert(mon != NULL);
     if (mon->owner == self) {
         /*
          * We own the monitor, so nobody else can be in here.
@@ -418,9 +418,7 @@
          * in this case.
          */
         dvmThrowExceptionFmt("Ljava/lang/IllegalMonitorStateException;",
-                             "unlock of unowned monitor, self=%d owner=%d",
-                             self->threadId, 
-                             mon->owner ? mon->owner->threadId : 0);
+                             "unlock of unowned monitor");
         return false;
     }
     return true;
diff --git a/vm/Thread.c b/vm/Thread.c
index 63b07cb..180ccf7 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -547,7 +547,9 @@
     do {
         cc = dvmTryLockMutex(&gDvm._threadSuspendLock);
         if (cc != 0) {
-            if (!dvmCheckSuspendPending(NULL)) {
+            Thread* self = dvmThreadSelf();
+
+            if (!dvmCheckSuspendPending(self)) {
                 /*
                  * Could be that a resume-all is in progress, and something
                  * grabbed the CPU when the wakeup was broadcast.  The thread
@@ -569,7 +571,7 @@
                  */
                 LOGI("threadid=%d ODD: want thread-suspend lock (%s:%s),"
                      " it's held, no suspend pending\n",
-                    dvmThreadSelf()->threadId, who, getSuspendCauseStr(why));
+                    self->threadId, who, getSuspendCauseStr(why));
             } else {
                 /* we suspended; reset timeout */
                 sleepIter = 0;
@@ -581,7 +583,7 @@
             if (!dvmIterativeSleep(sleepIter++, kSpinSleepTime, startWhen)) {
                 LOGE("threadid=%d: couldn't get thread-suspend lock (%s:%s),"
                      " bailing\n",
-                    dvmThreadSelf()->threadId, who, getSuspendCauseStr(why));
+                    self->threadId, who, getSuspendCauseStr(why));
                 /* threads are not suspended, thread dump could crash */
                 dvmDumpAllThreads(false);
                 dvmAbort();
@@ -3007,26 +3009,25 @@
  * Check to see if we need to suspend ourselves.  If so, go to sleep on
  * a condition variable.
  *
- * Takes "self" as an argument as an optimization.  Pass in NULL to have
- * it do the lookup.
+ * If "newStatus" is not THREAD_UNDEFINED, we change to that state before
+ * we release the thread suspend count lock.
  *
  * Returns "true" if we suspended ourselves.
  */
-bool dvmCheckSuspendPending(Thread* self)
+static bool checkSuspendAndChangeStatus(Thread* self, ThreadStatus newStatus)
 {
     bool didSuspend;
 
-    if (self == NULL)
-        self = dvmThreadSelf();
+    assert(self != NULL);
+    assert(self->suspendCount >= 0);
 
-    /* fast path: if count is zero, bail immediately */
-    if (self->suspendCount == 0)
+    /* fast path: if count is zero and no state change, bail immediately */
+    if (self->suspendCount == 0 && newStatus == THREAD_UNDEFINED) {
         return false;
+    }
 
     lockThreadSuspendCount();   /* grab gDvm.threadSuspendCountLock */
 
-    assert(self->suspendCount >= 0);        /* XXX: valid? useful? */
-
     didSuspend = (self->suspendCount != 0);
     self->isSuspended = true;
     LOG_THREAD("threadid=%d: self-suspending\n", self->threadId);
@@ -3042,12 +3043,31 @@
     LOG_THREAD("threadid=%d: self-reviving, status=%d\n",
         self->threadId, self->status);
 
+    /*
+     * The status change needs to happen while the suspend count lock is
+     * held.  Otherwise we could switch to RUNNING after another thread
+     * increases our suspend count, which isn't a "bad" state for us
+     * (we'll suspend on the next check) but could be a problem for the
+     * other thread (which thinks we're safely in VMWAIT or NATIVE with
+     * a nonzero suspend count, and proceeds to initate GC).
+     */
+    if (newStatus != THREAD_UNDEFINED)
+        self->status = newStatus;
+
     unlockThreadSuspendCount();
 
     return didSuspend;
 }
 
 /*
+ * One-argument wrapper for checkSuspendAndChangeStatus().
+ */
+bool dvmCheckSuspendPending(Thread* self)
+{
+    return checkSuspendAndChangeStatus(self, THREAD_UNDEFINED);
+}
+
+/*
  * Update our status.
  *
  * The "self" argument, which may be NULL, is accepted as an optimization.
@@ -3070,22 +3090,21 @@
         /*
          * Change our status to THREAD_RUNNING.  The transition requires
          * that we check for pending suspension, because the VM considers
-         * us to be "asleep" in all other states.
+         * us to be "asleep" in all other states, and another thread could
+         * be performing a GC now.
          *
-         * We need to do the "suspend pending" check FIRST, because it grabs
-         * a lock that could be held by something that wants us to suspend.
-         * If we're in RUNNING it will wait for us, and we'll be waiting
-         * for the lock it holds.
+         * The check for suspension requires holding the thread suspend
+         * count lock, which the suspend-all code also grabs.  We want to
+         * check our suspension status and change to RUNNING atomically
+         * to avoid a situation where suspend-all thinks we're safe
+         * (e.g. VMWAIT or NATIVE with suspendCount=1) but we've actually
+         * switched to RUNNING and are executing code.
          */
         assert(self->status != THREAD_RUNNING);
-
-        dvmCheckSuspendPending(self);
-        self->status = THREAD_RUNNING;
+        checkSuspendAndChangeStatus(self, newStatus);
     } else {
         /*
-         * Change from one state to another, neither of which is
-         * THREAD_RUNNING.  This is most common during system or thread
-         * initialization.
+         * Not changing to THREAD_RUNNING.  No additional work required.
          */
         self->status = newStatus;
     }
diff --git a/vm/Thread.h b/vm/Thread.h
index 29cbec6..4956009 100644
--- a/vm/Thread.h
+++ b/vm/Thread.h
@@ -44,6 +44,8 @@
  * Note that "suspended" is orthogonal to these values (so says JDWP).
  */
 typedef enum ThreadStatus {
+    THREAD_UNDEFINED    = -1,       /* threads are never in this state */
+
     /* these match up with JDWP values */
     THREAD_ZOMBIE       = 0,        /* TERMINATED */
     THREAD_RUNNING      = 1,        /* RUNNABLE or running now */
@@ -328,8 +330,6 @@
 /*
  * Check to see if we should be suspended now.  If so, suspend ourselves
  * by sleeping on a condition variable.
- *
- * If "self" is NULL, this will use dvmThreadSelf().
  */
 bool dvmCheckSuspendPending(Thread* self);
 
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
index ed4e4f3..f4d8e7b 100644
--- a/vm/alloc/Heap.c
+++ b/vm/alloc/Heap.c
@@ -67,8 +67,6 @@
     gcHeap->heapWorkerCurrentObject = NULL;
     gcHeap->heapWorkerCurrentMethod = NULL;
     gcHeap->heapWorkerInterpStartTime = 0LL;
-    gcHeap->softReferenceCollectionState = SR_COLLECT_NONE;
-    gcHeap->softReferenceHeapSizeThreshold = gDvm.heapSizeStart;
     gcHeap->ddmHpifWhen = 0;
     gcHeap->ddmHpsgWhen = 0;
     gcHeap->ddmHpsgWhat = 0;
@@ -260,50 +258,13 @@
     return obj;
 }
 
-/* Used for a heap size change hysteresis to avoid collecting
- * SoftReferences when the heap only grows by a small amount.
- */
-#define SOFT_REFERENCE_GROWTH_SLACK (128 * 1024)
-
 /* Whenever the effective heap size may have changed,
  * this function must be called.
  */
 void dvmHeapSizeChanged()
 {
-    GcHeap *gcHeap = gDvm.gcHeap;
-    size_t currentHeapSize;
-
-    currentHeapSize = dvmHeapSourceGetIdealFootprint();
-
-    /* See if the heap size has changed enough that we should care
-     * about it.
-     */
-    if (currentHeapSize <= gcHeap->softReferenceHeapSizeThreshold -
-            4 * SOFT_REFERENCE_GROWTH_SLACK)
-    {
-        /* The heap has shrunk enough that we'll use this as a new
-         * threshold.  Since we're doing better on space, there's
-         * no need to collect any SoftReferences.
-         *
-         * This is 4x the growth hysteresis because we don't want
-         * to snap down so easily after a shrink.  If we just cleared
-         * up a bunch of SoftReferences, we don't want to disallow
-         * any new ones from being created.
-         * TODO: determine if the 4x is important, needed, or even good
-         */
-        gcHeap->softReferenceHeapSizeThreshold = currentHeapSize;
-        gcHeap->softReferenceCollectionState = SR_COLLECT_NONE;
-    } else if (currentHeapSize >= gcHeap->softReferenceHeapSizeThreshold +
-            SOFT_REFERENCE_GROWTH_SLACK)
-    {
-        /* The heap has grown enough to warrant collecting SoftReferences.
-         */
-        gcHeap->softReferenceHeapSizeThreshold = currentHeapSize;
-        gcHeap->softReferenceCollectionState = SR_COLLECT_SOME;
-    }
 }
 
-
 /* Do a full garbage collection, which may grow the
  * heap as a side-effect if the live set is large.
  */
@@ -737,13 +698,9 @@
  * way to enforce this is to refuse to GC on an allocation made by the
  * JDWP thread -- we have to expand the heap or fail.
  */
-void dvmCollectGarbageInternal(bool collectSoftReferences, GcReason reason)
+void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason)
 {
     GcHeap *gcHeap = gDvm.gcHeap;
-    Object *softReferences;
-    Object *weakReferences;
-    Object *phantomReferences;
-
     u8 now;
     s8 timeSinceLastGc;
     s8 gcElapsedTime;
@@ -760,8 +717,6 @@
     size_t strongMarkSize = 0;
     size_t finalizeMarkCount = 0;
     size_t finalizeMarkSize = 0;
-    size_t phantomMarkCount = 0;
-    size_t phantomMarkSize = 0;
 #endif
 
     /* The heap lock must be held.
@@ -914,18 +869,6 @@
     gcHeap->weakReferences = NULL;
     gcHeap->phantomReferences = NULL;
 
-    /* Make sure that we don't hard-mark the referents of Reference
-     * objects by default.
-     */
-    gcHeap->markAllReferents = false;
-
-    /* Don't mark SoftReferences if our caller wants us to collect them.
-     * This has to be set before calling dvmHeapScanMarkedObjects().
-     */
-    if (collectSoftReferences) {
-        gcHeap->softReferenceCollectionState = SR_COLLECT_ALL;
-    }
-
     /* Recursively mark any objects that marked objects point to strongly.
      * If we're not collecting soft references, soft-reachable
      * objects will also be marked.
@@ -939,29 +882,16 @@
     gcHeap->markSize = 0;
 #endif
 
-    /* Latch these so that the other calls to dvmHeapScanMarkedObjects() don't
-     * mess with them.
-     */
-    softReferences = gcHeap->softReferences;
-    weakReferences = gcHeap->weakReferences;
-    phantomReferences = gcHeap->phantomReferences;
-
     /* All strongly-reachable objects have now been marked.
      */
-    if (gcHeap->softReferenceCollectionState != SR_COLLECT_NONE) {
-        LOGD_HEAP("Handling soft references...");
-        dvmHeapHandleReferences(softReferences, REF_SOFT);
-        // markCount always zero
+    LOGD_HEAP("Handling soft references...");
+    if (!clearSoftRefs) {
+        dvmHandleSoftRefs(&gcHeap->softReferences);
+    }
+    dvmClearWhiteRefs(&gcHeap->softReferences);
 
-        /* Now that we've tried collecting SoftReferences,
-         * fall back to not collecting them.  If the heap
-         * grows, we will start collecting again.
-         */
-        gcHeap->softReferenceCollectionState = SR_COLLECT_NONE;
-    } // else dvmHeapScanMarkedObjects() already marked the soft-reachable set
     LOGD_HEAP("Handling weak references...");
-    dvmHeapHandleReferences(weakReferences, REF_WEAK);
-    // markCount always zero
+    dvmClearWhiteRefs(&gcHeap->weakReferences);
 
     /* Once all weak-reachable objects have been taken
      * care of, any remaining unmarked objects can be finalized.
@@ -975,24 +905,22 @@
     gcHeap->markSize = 0;
 #endif
 
+    LOGD_HEAP("Handling f-reachable soft references...");
+    dvmClearWhiteRefs(&gcHeap->softReferences);
+
+    LOGD_HEAP("Handling f-reachable weak references...");
+    dvmClearWhiteRefs(&gcHeap->weakReferences);
+
     /* Any remaining objects that are not pending finalization
      * could be phantom-reachable.  This will mark any phantom-reachable
      * objects, as well as enqueue their references.
      */
     LOGD_HEAP("Handling phantom references...");
-    dvmHeapHandleReferences(phantomReferences, REF_PHANTOM);
-#if DVM_TRACK_HEAP_MARKING
-    phantomMarkCount = gcHeap->markCount;
-    phantomMarkSize = gcHeap->markSize;
-    gcHeap->markCount = 0;
-    gcHeap->markSize = 0;
-#endif
-
-//TODO: take care of JNI weak global references
+    dvmClearWhiteRefs(&gcHeap->phantomReferences);
 
 #if DVM_TRACK_HEAP_MARKING
-    LOGI_HEAP("Marked objects: %dB strong, %dB final, %dB phantom\n",
-            strongMarkSize, finalizeMarkSize, phantomMarkSize);
+    LOGI_HEAP("Marked objects: %dB strong, %dB final\n",
+              strongMarkSize, finalizeMarkSize);
 #endif
 
 #ifdef WITH_DEADLOCK_PREDICTION
diff --git a/vm/alloc/Heap.h b/vm/alloc/Heap.h
index 5237a56..0b0bc47 100644
--- a/vm/alloc/Heap.h
+++ b/vm/alloc/Heap.h
@@ -79,6 +79,6 @@
 /*
  * Run the garbage collector without doing any locking.
  */
-void dvmCollectGarbageInternal(bool collectSoftReferences, GcReason reason);
+void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason);
 
 #endif  // _DALVIK_ALLOC_HEAP
diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h
index 91d5976..f078fdd 100644
--- a/vm/alloc/HeapInternal.h
+++ b/vm/alloc/HeapInternal.h
@@ -24,8 +24,6 @@
 #include "HeapTable.h"
 #include "MarkSweep.h"
 
-#define SCHEDULED_REFERENCE_MAGIC   ((Object*)0x87654321)
-
 struct GcHeap {
     HeapSource      *heapSource;
 
@@ -111,31 +109,6 @@
      */
     bool            gcRunning;
 
-    /* Set at the end of a GC to indicate the collection policy
-     * for SoftReferences during the following GC.
-     */
-    enum { SR_COLLECT_NONE, SR_COLLECT_SOME, SR_COLLECT_ALL }
-                    softReferenceCollectionState;
-
-    /* The size of the heap is compared against this value
-     * to determine when to start collecting SoftReferences.
-     */
-    size_t          softReferenceHeapSizeThreshold;
-
-    /* A value that will increment every time we see a SoftReference
-     * whose referent isn't marked (during SR_COLLECT_SOME).
-     * The absolute value is meaningless, and does not need to
-     * be reset or initialized at any point.
-     */
-    int             softReferenceColor;
-
-    /* Indicates whether or not the object scanner should bother
-     * keeping track of any references.  If markAllReferents is
-     * true, referents will be hard-marked.  If false, normal
-     * reference following is used.
-     */
-    bool            markAllReferents;
-
 #if DVM_TRACK_HEAP_MARKING
     /* Every time an unmarked object becomes marked, markCount
      * is incremented and markSize increases by the size of
diff --git a/vm/alloc/HeapSource.c b/vm/alloc/HeapSource.c
index defedc6..98a9ba3 100644
--- a/vm/alloc/HeapSource.c
+++ b/vm/alloc/HeapSource.c
@@ -184,9 +184,9 @@
     size_t heapLength;
 
     /*
-     * The object (live, allocated) bitmap.
+     * The live object bitmap.
      */
-    HeapBitmap objBits;
+    HeapBitmap liveBits;
 
     /*
      * The mark bitmap.
@@ -276,7 +276,7 @@
     if (isObj) {
         heap->objectsAllocated++;
         hs = gDvm.gcHeap->heapSource;
-        dvmHeapBitmapSetObjectBit(&hs->objBits, ptr);
+        dvmHeapBitmapSetObjectBit(&hs->liveBits, ptr);
     }
 
     assert(heap->bytesAllocated < mspace_footprint(heap->msp));
@@ -297,7 +297,7 @@
     }
     if (isObj) {
         hs = gDvm.gcHeap->heapSource;
-        dvmHeapBitmapClearObjectBit(&hs->objBits, ptr);
+        dvmHeapBitmapClearObjectBit(&hs->liveBits, ptr);
         if (heap->objectsAllocated > 0) {
             heap->objectsAllocated--;
         }
@@ -478,13 +478,13 @@
         LOGE_HEAP("Can't add initial heap\n");
         goto fail;
     }
-    if (!dvmHeapBitmapInit(&hs->objBits, base, length, "dalvik-bitmap-1")) {
-        LOGE_HEAP("Can't create objBits\n");
+    if (!dvmHeapBitmapInit(&hs->liveBits, base, length, "dalvik-bitmap-1")) {
+        LOGE_HEAP("Can't create liveBits\n");
         goto fail;
     }
     if (!dvmHeapBitmapInit(&hs->markBits, base, length, "dalvik-bitmap-2")) {
         LOGE_HEAP("Can't create markBits\n");
-        dvmHeapBitmapDelete(&hs->objBits);
+        dvmHeapBitmapDelete(&hs->liveBits);
         goto fail;
     }
 
@@ -546,7 +546,7 @@
         assert((char *)*gcHeap >= hs->heapBase);
         assert((char *)*gcHeap < hs->heapBase + hs->heapLength);
 
-        dvmHeapBitmapDelete(&hs->objBits);
+        dvmHeapBitmapDelete(&hs->liveBits);
         dvmHeapBitmapDelete(&hs->markBits);
 
         munmap(hs->heapBase, hs->heapLength);
@@ -630,7 +630,7 @@
  * object and mark bitmaps.  This routine is used by the sweep code
  * which needs to free each object in the correct heap.
  */
-void dvmHeapSourceGetObjectBitmaps(HeapBitmap objBits[], HeapBitmap markBits[],
+void dvmHeapSourceGetObjectBitmaps(HeapBitmap liveBits[], HeapBitmap markBits[],
                                    size_t numHeaps)
 {
     HeapSource *hs = gHs;
@@ -643,7 +643,7 @@
     for (i = 0; i < hs->numHeaps; ++i) {
         base = (uintptr_t)hs->heaps[i].base;
         max = (uintptr_t)hs->heaps[i].limit - 1;
-        aliasBitmap(&objBits[i], &hs->objBits, base, max);
+        aliasBitmap(&liveBits[i], &hs->liveBits, base, max);
         aliasBitmap(&markBits[i], &hs->markBits, base, max);
     }
 }
@@ -655,14 +655,14 @@
 {
     HS_BOILERPLATE();
 
-    return &gHs->objBits;
+    return &gHs->liveBits;
 }
 
 void dvmHeapSourceSwapBitmaps(void)
 {
     HeapBitmap tmp;
-    tmp = gHs->objBits;
-    gHs->objBits = gHs->markBits;
+    tmp = gHs->liveBits;
+    gHs->liveBits = gHs->markBits;
     gHs->markBits = tmp;
     dvmHeapBitmapZero(&gHs->markBits);
 }
@@ -676,13 +676,13 @@
      * Copy the contents of the live bit vector for immune object
      * range into the mark bit vector.
      */
-    assert(gHs->objBits.base == gHs->markBits.base);
-    assert(gHs->objBits.bitsLen == gHs->markBits.bitsLen);
+    assert(gHs->liveBits.base == gHs->markBits.base);
+    assert(gHs->liveBits.bitsLen == gHs->markBits.bitsLen);
     for (i = 1; i < gHs->numHeaps; ++i) {
         /* Compute the number of words to copy in the bitmap. */
-        index = HB_OFFSET_TO_INDEX((uintptr_t)gHs->heaps[i].base - gHs->objBits.base);
+        index = HB_OFFSET_TO_INDEX((uintptr_t)gHs->heaps[i].base - gHs->liveBits.base);
         /* Compute the starting offset in the live and mark bits. */
-        src = (char *)(gHs->objBits.bits + index);
+        src = (char *)(gHs->liveBits.bits + index);
         dst = (char *)(gHs->markBits.bits + index);
         /* Compute the number of bytes of the live bitmap to copy. */
         length = HB_OFFSET_TO_BYTE_INDEX(gHs->heaps[i].limit - gHs->heaps[i].base);
@@ -919,8 +919,8 @@
 {
     HS_BOILERPLATE();
 
-    if (dvmHeapBitmapCoversAddress(&gHs->objBits, ptr)) {
-        return dvmHeapBitmapIsObjectBitSet(&gHs->objBits, ptr) != 0;
+    if (dvmHeapBitmapCoversAddress(&gHs->liveBits, ptr)) {
+        return dvmHeapBitmapIsObjectBitSet(&gHs->liveBits, ptr) != 0;
     }
     return false;
 }
diff --git a/vm/alloc/HeapTable.c b/vm/alloc/HeapTable.c
index 61a88ec..7d42600 100644
--- a/vm/alloc/HeapTable.c
+++ b/vm/alloc/HeapTable.c
@@ -202,10 +202,8 @@
         lastRef = table->refs.nextEntry;
         if (stripLowBits) {
             /* This case is used for marking reference objects that
-             * are still waiting for the heap worker thread to get to
-             * them.  The referents pointed to by the references are
-             * marked when a SCHEDULED_REFERENCE_MAGIC is encountered
-             * during scanning.
+             * are still waiting for the heap worker thread to push
+             * them onto their reference queue.
              */
             while (ref < lastRef) {
                 dvmMarkObjectNonNull((Object *)((uintptr_t)*ref++ & ~3));
diff --git a/vm/alloc/HeapWorker.c b/vm/alloc/HeapWorker.c
index c769521..bf924cd 100644
--- a/vm/alloc/HeapWorker.c
+++ b/vm/alloc/HeapWorker.c
@@ -278,6 +278,10 @@
             callMethod(self, obj, method);
         } else {
             if (op & WORKER_ENQUEUE) {
+                assert(dvmGetFieldObject(
+                           obj, gDvm.offJavaLangRefReference_queue) != NULL);
+                assert(dvmGetFieldObject(
+                           obj, gDvm.offJavaLangRefReference_queueNext) == NULL);
                 numReferencesEnqueued++;
                 callMethod(self, obj,
                         gDvm.methJavaLangRefReference_enqueueInternal);
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index 1b62cf4..d9860d9 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -329,7 +329,6 @@
     dvmMarkObjectNonNull(gDvm.outOfMemoryObj);
     dvmMarkObjectNonNull(gDvm.internalErrorObj);
     dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj);
-    dvmMarkObject(gDvm.jniWeakGlobalRefQueue);
 //TODO: scan object references sitting in gDvm;  use pointer begin & end
 
     HPROF_CLEAR_GC_SCAN_STATE();
@@ -538,43 +537,10 @@
              */
             referent = dvmGetFieldObject(obj,
                     gDvm.offJavaLangRefReference_referent);
-            if (referent != NULL &&
-                    !isMarked(referent, &gcHeap->markContext))
+            if (referent != NULL && !isMarked(referent, ctx))
             {
                 u4 refFlags;
 
-                if (gcHeap->markAllReferents) {
-                    LOG_REF("Hard-marking a reference\n");
-
-                    /* Don't bother with normal reference-following
-                     * behavior, just mark the referent.  This should
-                     * only be used when following objects that just
-                     * became scheduled for finalization.
-                     */
-                    markObjectNonNull(referent, ctx);
-                    goto skip_reference;
-                }
-
-                /* See if this reference was handled by a previous GC.
-                 */
-                if (dvmGetFieldObject(obj,
-                            gDvm.offJavaLangRefReference_vmData) ==
-                        SCHEDULED_REFERENCE_MAGIC)
-                {
-                    LOG_REF("Skipping scheduled reference\n");
-
-                    /* Don't reschedule it, but make sure that its
-                     * referent doesn't get collected (in case it's
-                     * a PhantomReference and wasn't cleared automatically).
-                     */
-                    //TODO: Mark these after handling all new refs of
-                    //      this strength, in case the new refs refer
-                    //      to the same referent.  Not a very common
-                    //      case, though.
-                    markObjectNonNull(referent, ctx);
-                    goto skip_reference;
-                }
-
                 /* Find out what kind of reference is pointing
                  * to referent.
                  */
@@ -606,25 +572,7 @@
                      * we'll attempt to collect all of them, some of
                      * them, or none of them.
                      */
-                    if (gcHeap->softReferenceCollectionState ==
-                            SR_COLLECT_NONE)
-                    {
-                sr_collect_none:
-                        markObjectNonNull(referent, ctx);
-                    } else if (gcHeap->softReferenceCollectionState ==
-                            SR_COLLECT_ALL)
-                    {
-                sr_collect_all:
-                        ADD_REF_TO_LIST(gcHeap->softReferences, obj);
-                    } else {
-                        /* We'll only try to collect half of the
-                         * referents.
-                         */
-                        if (gcHeap->softReferenceColor++ & 1) {
-                            goto sr_collect_none;
-                        }
-                        goto sr_collect_all;
-                    }
+                    ADD_REF_TO_LIST(gcHeap->softReferences, obj);
                 } else {
                     /* It's a weak or phantom reference.
                      * Clearing CLASS_ISREFERENCE will reveal which.
@@ -642,7 +590,6 @@
             }
         }
 
-    skip_reference:
         /* If this is a class object, mark various other things that
          * its internals point to.
          *
@@ -697,7 +644,7 @@
  * reachable objects.  When this returns, the entire set of
  * live objects will be marked and the mark stack will be empty.
  */
-void dvmHeapScanMarkedObjects()
+void dvmHeapScanMarkedObjects(void)
 {
     GcMarkContext *ctx = &gDvm.gcHeap->markContext;
 
@@ -735,9 +682,11 @@
             gDvm.offJavaLangRefReference_referent, NULL);
 }
 
-/** @return true if we need to schedule a call to enqueue().
+/*
+ * Returns true if the reference was registered with a reference queue
+ * and has not yet been enqueued.
  */
-static bool enqueueReference(Object *reference)
+static bool isEnqueuable(const Object *reference)
 {
     Object *queue = dvmGetFieldObject(reference,
             gDvm.offJavaLangRefReference_queue);
@@ -757,161 +706,124 @@
     return true;
 }
 
-/* All objects for stronger reference levels have been
- * marked before this is called.
+/*
+ * Schedules a reference to be appended to its reference queue.
  */
-void dvmHeapHandleReferences(Object *refListHead, enum RefType refType)
+static void enqueueReference(Object *ref)
 {
-    Object *reference;
-    GcMarkContext *markContext = &gDvm.gcHeap->markContext;
-    const int offVmData = gDvm.offJavaLangRefReference_vmData;
-    const int offReferent = gDvm.offJavaLangRefReference_referent;
-    bool workRequired = false;
+    LargeHeapRefTable **table;
+    Object *op;
 
-    reference = refListHead;
-    while (reference != NULL) {
-        Object *next;
-        Object *referent;
-
-        /* Pull the interesting fields out of the Reference object.
-         */
-        next = dvmGetFieldObject(reference, offVmData);
-        referent = dvmGetFieldObject(reference, offReferent);
-
-        //TODO: when handling REF_PHANTOM, unlink any references
-        //      that fail this initial if().  We need to re-walk
-        //      the list, and it would be nice to avoid the extra
-        //      work.
-        if (referent != NULL && !isMarked(referent, markContext)) {
-            bool schedEnqueue;
-
-            /* This is the strongest reference that refers to referent.
-             * Do the right thing.
-             */
-            switch (refType) {
-            case REF_SOFT:
-            case REF_WEAK:
-                clearReference(reference);
-                schedEnqueue = enqueueReference(reference);
-                break;
-            case REF_PHANTOM:
-                /* PhantomReferences are not cleared automatically.
-                 * Until someone clears it (or the reference itself
-                 * is collected), the referent must remain alive.
-                 *
-                 * It's necessary to fully mark the referent because
-                 * it will still be present during the next GC, and
-                 * all objects that it points to must be valid.
-                 * (The referent will be marked outside of this loop,
-                 * after handing all references of this strength, in
-                 * case multiple references point to the same object.)
-                 *
-                 * One exception: JNI "weak global" references are handled
-                 * as a special case.  They're identified by the queue.
-                 */
-                if (gDvm.jniWeakGlobalRefQueue != NULL) {
-                    Object* queue = dvmGetFieldObject(reference,
-                            gDvm.offJavaLangRefReference_queue);
-                    if (queue == gDvm.jniWeakGlobalRefQueue) {
-                        LOGV("+++ WGR: clearing + not queueing %p:%p\n",
-                            reference, referent);
-                        clearReference(reference);
-                        schedEnqueue = false;
-                        break;
-                    }
-                }
-
-                /* A PhantomReference is only useful with a
-                 * queue, but since it's possible to create one
-                 * without a queue, we need to check.
-                 */
-                schedEnqueue = enqueueReference(reference);
-                break;
-            default:
-                assert(!"Bad reference type");
-                schedEnqueue = false;
-                break;
-            }
-
-            if (schedEnqueue) {
-                /* Stuff the enqueue bit in the bottom of the pointer.
-                 * Assumes that objects are 8-byte aligned.
-                 *
-                 * Note that we are adding the *Reference* (which
-                 * is by definition already marked at this point) to
-                 * this list; we're not adding the referent (which
-                 * has already been cleared).
-                 */
-                assert(((intptr_t)reference & 3) == 0);
-                assert((WORKER_ENQUEUE & ~3) == 0);
-                if (!dvmHeapAddRefToLargeTable(
-                        &gDvm.gcHeap->referenceOperations,
-                        (Object *)((uintptr_t)reference | WORKER_ENQUEUE)))
-                {
-                    LOGE_HEAP("dvmMalloc(): no room for any more "
-                            "reference operations\n");
-                    dvmAbort();
-                }
-                workRequired = true;
-            }
-
-            if (refType != REF_PHANTOM) {
-                /* Let later GCs know not to reschedule this reference.
-                 */
-                dvmSetFieldObject(reference, offVmData,
-                        SCHEDULED_REFERENCE_MAGIC);
-            } // else this is handled later for REF_PHANTOM
-
-        } // else there was a stronger reference to the referent.
-
-        reference = next;
-    }
-
-    /* Walk though the reference list again, and mark any non-clear/marked
-     * referents.  Only PhantomReferences can have non-clear referents
-     * at this point.
+    assert(((uintptr_t)ref & 3) == 0);
+    assert((WORKER_ENQUEUE & ~3) == 0);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
+    /* Stuff the enqueue bit in the bottom of the pointer.
+     * Assumes that objects are 8-byte aligned.
      *
-     * (Could skip this for JNI weak globals, since we know they've been
-     * cleared.)
+     * Note that we are adding the *Reference* (which
+     * is by definition already marked at this point) to
+     * this list; we're not adding the referent (which
+     * has already been cleared).
      */
-    if (refType == REF_PHANTOM) {
-        bool scanRequired = false;
-
-        HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_REFERENCE_CLEANUP, 0);
-        reference = refListHead;
-        while (reference != NULL) {
-            Object *next;
-            Object *referent;
-
-            /* Pull the interesting fields out of the Reference object.
-             */
-            next = dvmGetFieldObject(reference, offVmData);
-            referent = dvmGetFieldObject(reference, offReferent);
-
-            if (referent != NULL && !isMarked(referent, markContext)) {
-                markObjectNonNull(referent, markContext);
-                scanRequired = true;
-
-                /* Let later GCs know not to reschedule this reference.
-                 */
-                dvmSetFieldObject(reference, offVmData,
-                        SCHEDULED_REFERENCE_MAGIC);
-            }
-
-            reference = next;
-        }
-        HPROF_CLEAR_GC_SCAN_STATE();
-
-        if (scanRequired) {
-            processMarkStack(markContext);
-        }
-    }
-
-    if (workRequired) {
-        dvmSignalHeapWorker(false);
+    table = &gDvm.gcHeap->referenceOperations;
+    op = (Object *)((uintptr_t)ref | WORKER_ENQUEUE);
+    if (!dvmHeapAddRefToLargeTable(table, op)) {
+        LOGE_HEAP("enqueueReference(): no room for any more "
+                  "reference operations\n");
+        dvmAbort();
     }
 }
 
+/*
+ * Walks the reference list marking any references subject to the
+ * reference clearing policy.  References with a black referent are
+ * removed from the list.  References with white referents biased
+ * toward saving are blackened and also removed from the list.
+ */
+void dvmHandleSoftRefs(Object **list)
+{
+    GcMarkContext *markContext;
+    Object *ref, *referent;
+    Object *prev, *next;
+    size_t referentOffset, vmDataOffset;
+    unsigned counter;
+    bool marked;
+
+    markContext = &gDvm.gcHeap->markContext;
+    vmDataOffset = gDvm.offJavaLangRefReference_vmData;
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    counter = 0;
+    prev = next = NULL;
+    ref = *list;
+    while (ref != NULL) {
+        referent = dvmGetFieldObject(ref, referentOffset);
+        next = dvmGetFieldObject(ref, vmDataOffset);
+        assert(referent != NULL);
+        marked = isMarked(referent, markContext);
+        if (!marked && ((++counter) & 1)) {
+            /* Referent is white and biased toward saving, mark it. */
+            markObjectNonNull(referent, markContext);
+            marked = true;
+        }
+        if (marked) {
+            /* Referent is black, unlink it. */
+            if (prev != NULL) {
+                dvmSetFieldObject(ref, vmDataOffset, NULL);
+                dvmSetFieldObject(prev, vmDataOffset, next);
+            }
+        } else {
+            /* Referent is white, skip over it. */
+            prev = ref;
+        }
+        ref = next;
+    }
+    /*
+     * Restart the mark with the newly black references added to the
+     * root set.
+     */
+    processMarkStack(markContext);
+}
+
+/*
+ * Walks the reference list and clears references with an unmarked
+ * (white) referents.  Cleared references registered to a reference
+ * queue are scheduled for appending by the heap worker thread.
+ */
+void dvmClearWhiteRefs(Object **list)
+{
+    GcMarkContext *markContext;
+    Object *ref, *referent;
+    size_t referentOffset, vmDataOffset;
+    bool doSignal;
+
+    markContext = &gDvm.gcHeap->markContext;
+    vmDataOffset = gDvm.offJavaLangRefReference_vmData;
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    doSignal = false;
+    while (*list != NULL) {
+        ref = *list;
+        referent = dvmGetFieldObject(ref, referentOffset);
+        *list = dvmGetFieldObject(ref, vmDataOffset);
+        assert(referent != NULL);
+        if (!isMarked(referent, markContext)) {
+            /* Referent is "white", clear it. */
+            clearReference(ref);
+            if (isEnqueuable(ref)) {
+                enqueueReference(ref);
+                doSignal = true;
+            }
+        }
+    }
+    /*
+     * If we cleared a reference with a reference queue we must notify
+     * the heap worker to append the reference.
+     */
+    if (doSignal) {
+        dvmSignalHeapWorker(false);
+    }
+    assert(*list == NULL);
+}
 
 /* Find unreachable objects that need to be finalized,
  * and schedule them for finalization.
@@ -1018,16 +930,7 @@
         ref++;
     }
     HPROF_CLEAR_GC_SCAN_STATE();
-
-    /* Set markAllReferents so that we don't collect referents whose
-     * only references are in final-reachable objects.
-     * TODO: eventually provide normal reference behavior by properly
-     *       marking these references.
-     */
-    gDvm.gcHeap->markAllReferents = true;
     processMarkStack(markContext);
-    gDvm.gcHeap->markAllReferents = false;
-
     dvmSignalHeapWorker(false);
 }
 
@@ -1119,7 +1022,7 @@
 dvmHeapSweepUnmarkedObjects(GcMode mode, int *numFreed, size_t *sizeFreed)
 {
     HeapBitmap markBits[HEAP_SOURCE_MAX_HEAP_COUNT];
-    HeapBitmap objBits[HEAP_SOURCE_MAX_HEAP_COUNT];
+    HeapBitmap liveBits[HEAP_SOURCE_MAX_HEAP_COUNT];
     size_t origObjectsAllocated;
     size_t origBytesAllocated;
     size_t numBitmaps, numSweepBitmaps;
@@ -1138,14 +1041,14 @@
     dvmSweepMonitorList(&gDvm.monitorList, isUnmarkedObject);
 
     numBitmaps = dvmHeapSourceGetNumHeaps();
-    dvmHeapSourceGetObjectBitmaps(objBits, markBits, numBitmaps);
+    dvmHeapSourceGetObjectBitmaps(liveBits, markBits, numBitmaps);
     if (mode == GC_PARTIAL) {
         numSweepBitmaps = 1;
-        assert((uintptr_t)gDvm.gcHeap->markContext.immuneLimit == objBits[0].base);
+        assert((uintptr_t)gDvm.gcHeap->markContext.immuneLimit == liveBits[0].base);
     } else {
         numSweepBitmaps = numBitmaps;
     }
-    dvmHeapBitmapXorWalkLists(markBits, objBits, numSweepBitmaps,
+    dvmHeapBitmapXorWalkLists(markBits, liveBits, numSweepBitmaps,
                               sweepBitmapCallback, NULL);
 
     *numFreed = origObjectsAllocated -
diff --git a/vm/alloc/MarkSweep.h b/vm/alloc/MarkSweep.h
index e6d55bc..5323628 100644
--- a/vm/alloc/MarkSweep.h
+++ b/vm/alloc/MarkSweep.h
@@ -54,7 +54,8 @@
 bool dvmHeapBeginMarkStep(GcMode mode);
 void dvmHeapMarkRootSet(void);
 void dvmHeapScanMarkedObjects(void);
-void dvmHeapHandleReferences(Object *refListHead, enum RefType refType);
+void dvmHandleSoftRefs(Object **list);
+void dvmClearWhiteRefs(Object **list);
 void dvmHeapScheduleFinalizations(void);
 void dvmHeapFinishMarkStep(void);
 
diff --git a/vm/alloc/Verify.c b/vm/alloc/Verify.c
index 20e7b8a..db552e8 100644
--- a/vm/alloc/Verify.c
+++ b/vm/alloc/Verify.c
@@ -19,7 +19,9 @@
 #include "alloc/Verify.h"
 #include "alloc/HeapBitmap.h"
 
-/* comment everything, of course! */
+/*
+ * Assertion that the given reference points to a valid object.
+ */
 #define VERIFY_REFERENCE(x) do {                                \
         if (!verifyReference((x), &(x))) {                      \
             LOGE("Verify of %p at %p failed", (x), &(x));       \
@@ -123,12 +125,9 @@
         }
     }
     if (IS_CLASS_FLAG_SET(obj->obj.clazz, CLASS_ISREFERENCE)) {
-        /*
-         * Reference.referent is not included in the above loop. See
-         * precacheReferenceOffsets in Class.c for details.
-         */
-        addr = BYTE_OFFSET((Object *)obj,
-                           gDvm.offJavaLangRefReference_referent);
+        /* Verify the hidden Reference.referent field. */
+        offset = gDvm.offJavaLangRefReference_referent;
+        addr = BYTE_OFFSET((Object *)obj, offset);
         VERIFY_REFERENCE(((JValue *)addr)->l);
     }
     LOGV("Exiting verifyDataObject(obj=%p) %zx", obj, length);
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index 8187354..91f5414 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -29,6 +29,7 @@
  */
 #include "Dalvik.h"
 #include "analysis/CodeVerify.h"
+#include "analysis/Optimize.h"
 #include "analysis/RegisterMap.h"
 #include "libdex/DexCatch.h"
 #include "libdex/InstrUtils.h"
@@ -5561,7 +5562,8 @@
     /*
      * If we didn't just set the result register, clear it out.  This
      * ensures that you can only use "move-result" immediately after the
-     * result is set.
+     * result is set.  (We could check this statically, but it's not
+     * expensive and it makes our debugging output cleaner.)
      */
     if (!justSetResult) {
         int reg = RESULT_REGISTER(insnRegCount);
diff --git a/vm/analysis/DexOptimize.c b/vm/analysis/DexOptimize.c
deleted file mode 100644
index ae1edfc..0000000
--- a/vm/analysis/DexOptimize.c
+++ /dev/null
@@ -1,2383 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Convert the output from "dx" into a locally-optimized DEX file.
- *
- * TODO: the format of the optimized header is currently "whatever we
- * happen to write", since the VM that writes it is by definition the same
- * as the VM that reads it.  Still, it should be better documented and
- * more rigorously structured.
- */
-#include "Dalvik.h"
-#include "libdex/InstrUtils.h"
-#include "libdex/OptInvocation.h"
-#include "analysis/RegisterMap.h"
-
-#include <zlib.h>
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/file.h>
-#include <sys/wait.h>
-#include <fcntl.h>
-#include <errno.h>
-
-/*
- * Virtual/direct calls to "method" are replaced with an execute-inline
- * instruction with index "idx".
- */
-typedef struct InlineSub {
-    Method* method;
-    int     inlineIdx;
-} InlineSub;
-
-
-/* fwd */
-static int writeDependencies(int fd, u4 modWhen, u4 crc);
-static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,\
-    const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder);
-static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
-    int err);
-static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum);
-
-static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,\
-    u4* pHeaderFlags, DexClassLookup** ppClassLookup);
-static void updateChecksum(u1* addr, int len, DexHeader* pHeader);
-static bool loadAllClasses(DvmDex* pDvmDex);
-static void optimizeLoadedClasses(DexFile* pDexFile);
-static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs);
-static bool optimizeMethod(Method* method, const InlineSub* inlineSubs);
-static void rewriteInstField(Method* method, u2* insns, OpCode newOpc);
-static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc);
-static bool rewriteEmptyDirectInvoke(Method* method, u2* insns);
-static bool rewriteExecuteInline(Method* method, u2* insns,
-    MethodType methodType, const InlineSub* inlineSubs);
-static bool rewriteExecuteInlineRange(Method* method, u2* insns,
-    MethodType methodType, const InlineSub* inlineSubs);
-
-
-/*
- * Return the fd of an open file in the DEX file cache area.  If the cache
- * file doesn't exist or is out of date, this will remove the old entry,
- * create a new one (writing only the file header), and return with the
- * "new file" flag set.
- *
- * It's possible to execute from an unoptimized DEX file directly,
- * assuming the byte ordering and structure alignment is correct, but
- * disadvantageous because some significant optimizations are not possible.
- * It's not generally possible to do the same from an uncompressed Jar
- * file entry, because we have to guarantee 32-bit alignment in the
- * memory-mapped file.
- *
- * For a Jar/APK file (a zip archive with "classes.dex" inside), "modWhen"
- * and "crc32" come from the Zip directory entry.  For a stand-alone DEX
- * file, it's the modification date of the file and the Adler32 from the
- * DEX header (which immediately follows the magic).  If these don't
- * match what's stored in the opt header, we reject the file immediately.
- *
- * On success, the file descriptor will be positioned just past the "opt"
- * file header, and will be locked with flock.  "*pCachedName" will point
- * to newly-allocated storage.
- */
-int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
-    u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
-{
-    int fd, cc;
-    struct stat fdStat, fileStat;
-    bool readOnly = false;
-
-    *pNewFile = false;
-
-retry:
-    /*
-     * Try to open the cache file.  If we've been asked to,
-     * create it if it doesn't exist.
-     */
-    fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
-    if (fd < 0) {
-        fd = open(cacheFileName, O_RDONLY, 0);
-        if (fd < 0) {
-            if (createIfMissing) {
-                LOGE("Can't open dex cache '%s': %s\n",
-                    cacheFileName, strerror(errno));
-            }
-            return fd;
-        }
-        readOnly = true;
-    }
-
-    /*
-     * Grab an exclusive lock on the cache file.  If somebody else is
-     * working on it, we'll block here until they complete.  Because
-     * we're waiting on an external resource, we go into VMWAIT mode.
-     */
-    int oldStatus;
-    LOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)\n",
-        cacheFileName, fd, isBootstrap);
-    oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
-    cc = flock(fd, LOCK_EX | LOCK_NB);
-    if (cc != 0) {
-        LOGD("DexOpt: sleeping on flock(%s)\n", cacheFileName);
-        cc = flock(fd, LOCK_EX);
-    }
-    dvmChangeStatus(NULL, oldStatus);
-    if (cc != 0) {
-        LOGE("Can't lock dex cache '%s': %d\n", cacheFileName, cc);
-        close(fd);
-        return -1;
-    }
-    LOGV("DexOpt:  locked cache file\n");
-
-    /*
-     * Check to see if the fd we opened and locked matches the file in
-     * the filesystem.  If they don't, then somebody else unlinked ours
-     * and created a new file, and we need to use that one instead.  (If
-     * we caught them between the unlink and the create, we'll get an
-     * ENOENT from the file stat.)
-     */
-    cc = fstat(fd, &fdStat);
-    if (cc != 0) {
-        LOGE("Can't stat open file '%s'\n", cacheFileName);
-        LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
-        goto close_fail;
-    }
-    cc = stat(cacheFileName, &fileStat);
-    if (cc != 0 ||
-        fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
-    {
-        LOGD("DexOpt: our open cache file is stale; sleeping and retrying\n");
-        LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
-        flock(fd, LOCK_UN);
-        close(fd);
-        usleep(250 * 1000);     /* if something is hosed, don't peg machine */
-        goto retry;
-    }
-
-    /*
-     * We have the correct file open and locked.  If the file size is zero,
-     * then it was just created by us, and we want to fill in some fields
-     * in the "opt" header and set "*pNewFile".  Otherwise, we want to
-     * verify that the fields in the header match our expectations, and
-     * reset the file if they don't.
-     */
-    if (fdStat.st_size == 0) {
-        if (readOnly) {
-            LOGW("DexOpt: file has zero length and isn't writable\n");
-            goto close_fail;
-        }
-        cc = dexOptCreateEmptyHeader(fd);
-        if (cc != 0)
-            goto close_fail;
-        *pNewFile = true;
-        LOGV("DexOpt: successfully initialized new cache file\n");
-    } else {
-        bool expectVerify, expectOpt;
-
-        if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
-            expectVerify = false;
-        else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
-            expectVerify = !isBootstrap;
-        else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
-            expectVerify = true;
-
-        if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
-            expectOpt = false;
-        else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
-            expectOpt = expectVerify;
-        else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
-            expectOpt = true;
-
-        LOGV("checking deps, expecting vfy=%d opt=%d\n",
-            expectVerify, expectOpt);
-
-        if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
-                expectVerify, expectOpt))
-        {
-            if (readOnly) {
-                /*
-                 * We could unlink and rewrite the file if we own it or
-                 * the "sticky" bit isn't set on the directory.  However,
-                 * we're not able to truncate it, which spoils things.  So,
-                 * give up now.
-                 */
-                if (createIfMissing) {
-                    LOGW("Cached DEX '%s' (%s) is stale and not writable\n",
-                        fileName, cacheFileName);
-                }
-                goto close_fail;
-            }
-
-            /*
-             * If we truncate the existing file before unlinking it, any
-             * process that has it mapped will fail when it tries to touch
-             * the pages.
-             *
-             * This is very important.  The zygote process will have the
-             * boot DEX files (core, framework, etc.) mapped early.  If
-             * (say) core.dex gets updated, and somebody launches an app
-             * that uses App.dex, then App.dex gets reoptimized because it's
-             * dependent upon the boot classes.  However, dexopt will be
-             * using the *new* core.dex to do the optimizations, while the
-             * app will actually be running against the *old* core.dex
-             * because it starts from zygote.
-             *
-             * Even without zygote, it's still possible for a class loader
-             * to pull in an APK that was optimized against an older set
-             * of DEX files.  We must ensure that everything fails when a
-             * boot DEX gets updated, and for general "why aren't my
-             * changes doing anything" purposes its best if we just make
-             * everything crash when a DEX they're using gets updated.
-             */
-            LOGD("Stale deps in cache file; removing and retrying\n");
-            if (ftruncate(fd, 0) != 0) {
-                LOGW("Warning: unable to truncate cache file '%s': %s\n",
-                    cacheFileName, strerror(errno));
-                /* keep going */
-            }
-            if (unlink(cacheFileName) != 0) {
-                LOGW("Warning: unable to remove cache file '%s': %d %s\n",
-                    cacheFileName, errno, strerror(errno));
-                /* keep going; permission failure should probably be fatal */
-            }
-            LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
-            flock(fd, LOCK_UN);
-            close(fd);
-            goto retry;
-        } else {
-            LOGV("DexOpt: good deps in cache file\n");
-        }
-    }
-
-    assert(fd >= 0);
-    return fd;
-
-close_fail:
-    flock(fd, LOCK_UN);
-    close(fd);
-    return -1;
-}
-
-/*
- * Unlock the file descriptor.
- *
- * Returns "true" on success.
- */
-bool dvmUnlockCachedDexFile(int fd)
-{
-    LOGVV("DexOpt: unlocking cache file fd=%d\n", fd);
-    return (flock(fd, LOCK_UN) == 0);
-}
-
-
-/*
- * Given a descriptor for a file with DEX data in it, produce an
- * optimized version.
- *
- * The file pointed to by "fd" is expected to be a locked shared resource
- * (or private); we make no efforts to enforce multi-process correctness
- * here.
- *
- * "fileName" is only used for debug output.  "modWhen" and "crc" are stored
- * in the dependency set.
- *
- * The "isBootstrap" flag determines how the optimizer and verifier handle
- * package-scope access checks.  When optimizing, we only load the bootstrap
- * class DEX files and the target DEX, so the flag determines whether the
- * target DEX classes are given a (synthetic) non-NULL classLoader pointer.
- * This only really matters if the target DEX contains classes that claim to
- * be in the same package as bootstrap classes.
- *
- * The optimizer will need to load every class in the target DEX file.
- * This is generally undesirable, so we start a subprocess to do the
- * work and wait for it to complete.
- *
- * Returns "true" on success.  All data will have been written to "fd".
- */
-bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
-    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
-{
-    const char* lastPart = strrchr(fileName, '/');
-    if (lastPart != NULL)
-        lastPart++;
-    else
-        lastPart = fileName;
-
-    /*
-     * For basic optimizations (byte-swapping and structure aligning) we
-     * don't need to fork().  It looks like fork+exec is causing problems
-     * with gdb on our bewildered Linux distro, so in some situations we
-     * want to avoid this.
-     *
-     * For optimization and/or verification, we need to load all the classes.
-     *
-     * We don't check gDvm.generateRegisterMaps, since that is dependent
-     * upon the verifier state.
-     */
-    if (gDvm.classVerifyMode == VERIFY_MODE_NONE &&
-        (gDvm.dexOptMode == OPTIMIZE_MODE_NONE ||
-         gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED))
-    {
-        LOGD("DexOpt: --- BEGIN (quick) '%s' ---\n", lastPart);
-        return dvmContinueOptimization(fd, dexOffset, dexLength,
-                fileName, modWhen, crc, isBootstrap);
-    }
-
-
-    LOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---\n", lastPart, isBootstrap);
-
-    pid_t pid;
-
-    /*
-     * This could happen if something in our bootclasspath, which we thought
-     * was all optimized, got rejected.
-     */
-    if (gDvm.optimizing) {
-        LOGW("Rejecting recursive optimization attempt on '%s'\n", fileName);
-        return false;
-    }
-
-    pid = fork();
-    if (pid == 0) {
-        static const int kUseValgrind = 0;
-        static const char* kDexOptBin = "/bin/dexopt";
-        static const char* kValgrinder = "/usr/bin/valgrind";
-        static const int kFixedArgCount = 10;
-        static const int kValgrindArgCount = 5;
-        static const int kMaxIntLen = 12;   // '-'+10dig+'\0' -OR- 0x+8dig
-        int bcpSize = dvmGetBootPathSize();
-        int argc = kFixedArgCount + bcpSize
-            + (kValgrindArgCount * kUseValgrind);
-        char* argv[argc+1];             // last entry is NULL
-        char values[argc][kMaxIntLen];
-        char* execFile;
-        char* androidRoot;
-        int flags;
-
-        /* change process groups, so we don't clash with ProcessManager */
-        setpgid(0, 0);
-
-        /* full path to optimizer */
-        androidRoot = getenv("ANDROID_ROOT");
-        if (androidRoot == NULL) {
-            LOGW("ANDROID_ROOT not set, defaulting to /system\n");
-            androidRoot = "/system";
-        }
-        execFile = malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
-        strcpy(execFile, androidRoot);
-        strcat(execFile, kDexOptBin);
-
-        /*
-         * Create arg vector.
-         */
-        int curArg = 0;
-
-        if (kUseValgrind) {
-            /* probably shouldn't ship the hard-coded path */
-            argv[curArg++] = (char*)kValgrinder;
-            argv[curArg++] = "--tool=memcheck";
-            argv[curArg++] = "--leak-check=yes";        // check for leaks too
-            argv[curArg++] = "--leak-resolution=med";   // increase from 2 to 4
-            argv[curArg++] = "--num-callers=16";        // default is 12
-            assert(curArg == kValgrindArgCount);
-        }
-        argv[curArg++] = execFile;
-
-        argv[curArg++] = "--dex";
-
-        sprintf(values[2], "%d", DALVIK_VM_BUILD);
-        argv[curArg++] = values[2];
-
-        sprintf(values[3], "%d", fd);
-        argv[curArg++] = values[3];
-
-        sprintf(values[4], "%d", (int) dexOffset);
-        argv[curArg++] = values[4];
-
-        sprintf(values[5], "%d", (int) dexLength);
-        argv[curArg++] = values[5];
-
-        argv[curArg++] = (char*)fileName;
-
-        sprintf(values[7], "%d", (int) modWhen);
-        argv[curArg++] = values[7];
-
-        sprintf(values[8], "%d", (int) crc);
-        argv[curArg++] = values[8];
-
-        flags = 0;
-        if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE) {
-            flags |= DEXOPT_OPT_ENABLED;
-            if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
-                flags |= DEXOPT_OPT_ALL;
-        }
-        if (gDvm.classVerifyMode != VERIFY_MODE_NONE) {
-            flags |= DEXOPT_VERIFY_ENABLED;
-            if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
-                flags |= DEXOPT_VERIFY_ALL;
-        }
-        if (isBootstrap)
-            flags |= DEXOPT_IS_BOOTSTRAP;
-        if (gDvm.generateRegisterMaps)
-            flags |= DEXOPT_GEN_REGISTER_MAP;
-        sprintf(values[9], "%d", flags);
-        argv[curArg++] = values[9];
-
-        assert(((!kUseValgrind && curArg == kFixedArgCount) ||
-               ((kUseValgrind && curArg == kFixedArgCount+kValgrindArgCount))));
-
-        ClassPathEntry* cpe;
-        for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
-            argv[curArg++] = cpe->fileName;
-        }
-        assert(curArg == argc);
-
-        argv[curArg] = NULL;
-
-        if (kUseValgrind)
-            execv(kValgrinder, argv);
-        else
-            execv(execFile, argv);
-
-        LOGE("execv '%s'%s failed: %s\n", execFile,
-            kUseValgrind ? " [valgrind]" : "", strerror(errno));
-        exit(1);
-    } else {
-        LOGV("DexOpt: waiting for verify+opt, pid=%d\n", (int) pid);
-        int status;
-        pid_t gotPid;
-        int oldStatus;
-
-        /*
-         * Wait for the optimization process to finish.  We go into VMWAIT
-         * mode here so GC suspension won't have to wait for us.
-         */
-        oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
-        while (true) {
-            gotPid = waitpid(pid, &status, 0);
-            if (gotPid == -1 && errno == EINTR) {
-                LOGD("waitpid interrupted, retrying\n");
-            } else {
-                break;
-            }
-        }
-        dvmChangeStatus(NULL, oldStatus);
-        if (gotPid != pid) {
-            LOGE("waitpid failed: wanted %d, got %d: %s\n",
-                (int) pid, (int) gotPid, strerror(errno));
-            return false;
-        }
-
-        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
-            LOGD("DexOpt: --- END '%s' (success) ---\n", lastPart);
-            return true;
-        } else {
-            LOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed\n",
-                lastPart, status);
-            return false;
-        }
-    }
-}
-
-/*
- * Do the actual optimization.  This is called directly for "minimal"
- * optimization, or from a newly-created process for "full" optimization.
- *
- * For best use of disk/memory, we want to extract once and perform
- * optimizations in place.  If the file has to expand or contract
- * to match local structure padding/alignment expectations, we want
- * to do the rewrite as part of the extract, rather than extracting
- * into a temp file and slurping it back out.  (The structure alignment
- * is currently correct for all platforms, and this isn't expected to
- * change, so we should be okay with having it already extracted.)
- *
- * Returns "true" on success.
- */
-bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
-    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
-{
-    DexClassLookup* pClassLookup = NULL;
-    IndexMapSet* pIndexMapSet = NULL;
-    RegisterMapBuilder* pRegMapBuilder = NULL;
-    bool doVerify, doOpt;
-    u4 headerFlags = 0;
-
-    if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
-        doVerify = false;
-    else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
-        doVerify = !isBootstrap;
-    else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
-        doVerify = true;
-
-    if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
-        doOpt = false;
-    else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
-        doOpt = doVerify;
-    else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
-        doOpt = true;
-
-    LOGV("Continuing optimization (%s, isb=%d, vfy=%d, opt=%d)\n",
-        fileName, isBootstrap, doVerify, doOpt);
-
-    assert(dexOffset >= 0);
-
-    /* quick test so we don't blow up on empty file */
-    if (dexLength < (int) sizeof(DexHeader)) {
-        LOGE("too small to be DEX\n");
-        return false;
-    }
-    if (dexOffset < (int) sizeof(DexOptHeader)) {
-        LOGE("not enough room for opt header\n");
-        return false;
-    }
-
-    bool result = false;
-
-    /*
-     * Drop this into a global so we don't have to pass it around.  We could
-     * also add a field to DexFile, but since it only pertains to DEX
-     * creation that probably doesn't make sense.
-     */
-    gDvm.optimizingBootstrapClass = isBootstrap;
-
-    {
-        /*
-         * Map the entire file (so we don't have to worry about page
-         * alignment).  The expectation is that the output file contains
-         * our DEX data plus room for a small header.
-         */
-        bool success;
-        void* mapAddr;
-        mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,
-                    MAP_SHARED, fd, 0);
-        if (mapAddr == MAP_FAILED) {
-            LOGE("unable to mmap DEX cache: %s\n", strerror(errno));
-            goto bail;
-        }
-
-        /*
-         * Rewrite the file.  Byte reordering, structure realigning,
-         * class verification, and bytecode optimization are all performed
-         * here.
-         *
-         * In theory the file could change size and bits could shift around.
-         * In practice this would be annoying to deal with, so the file
-         * layout is designed so that it can always be rewritten in place.
-         *
-         * This sets "headerFlags" and creates the class lookup table as
-         * part of doing the processing.
-         */
-        success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
-                    doVerify, doOpt, &headerFlags, &pClassLookup);
-
-        if (success) {
-            DvmDex* pDvmDex = NULL;
-            u1* dexAddr = ((u1*) mapAddr) + dexOffset;
-
-            if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
-                LOGE("Unable to create DexFile\n");
-                success = false;
-            } else {
-                /*
-                 * If configured to do so, scan the instructions, looking
-                 * for ways to reduce the size of the resolved-constant table.
-                 * This is done post-optimization, across the instructions
-                 * in all methods in all classes (even the ones that failed
-                 * to load).
-                 */
-                pIndexMapSet = dvmRewriteConstants(pDvmDex);
-
-                /*
-                 * If configured to do so, generate a full set of register
-                 * maps for all verified classes.
-                 */
-                if (gDvm.generateRegisterMaps) {
-                    pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);
-                    if (pRegMapBuilder == NULL) {
-                        LOGE("Failed generating register maps\n");
-                        success = false;
-                    }
-                }
-
-                DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;
-                updateChecksum(dexAddr, dexLength, pHeader);
-
-                dvmDexFileFree(pDvmDex);
-            }
-        }
-
-        /* unmap the read-write version, forcing writes to disk */
-        if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) {
-            LOGW("msync failed: %s\n", strerror(errno));
-            // weird, but keep going
-        }
-#if 1
-        /*
-         * This causes clean shutdown to fail, because we have loaded classes
-         * that point into it.  For the optimizer this isn't a problem,
-         * because it's more efficient for the process to simply exit.
-         * Exclude this code when doing clean shutdown for valgrind.
-         */
-        if (munmap(mapAddr, dexOffset + dexLength) != 0) {
-            LOGE("munmap failed: %s\n", strerror(errno));
-            goto bail;
-        }
-#endif
-
-        if (!success)
-            goto bail;
-    }
-
-    /* get start offset, and adjust deps start for 64-bit alignment */
-    off_t depsOffset, auxOffset, endOffset, adjOffset;
-    int depsLength, auxLength;
-    u4 optChecksum;
-
-    depsOffset = lseek(fd, 0, SEEK_END);
-    if (depsOffset < 0) {
-        LOGE("lseek to EOF failed: %s\n", strerror(errno));
-        goto bail;
-    }
-    adjOffset = (depsOffset + 7) & ~(0x07);
-    if (adjOffset != depsOffset) {
-        LOGV("Adjusting deps start from %d to %d\n",
-            (int) depsOffset, (int) adjOffset);
-        depsOffset = adjOffset;
-        lseek(fd, depsOffset, SEEK_SET);
-    }
-
-    /*
-     * Append the dependency list.
-     */
-    if (writeDependencies(fd, modWhen, crc) != 0) {
-        LOGW("Failed writing dependencies\n");
-        goto bail;
-    }
-
-    /* compute deps length, then adjust aux start for 64-bit alignment */
-    auxOffset = lseek(fd, 0, SEEK_END);
-    depsLength = auxOffset - depsOffset;
-
-    adjOffset = (auxOffset + 7) & ~(0x07);
-    if (adjOffset != auxOffset) {
-        LOGV("Adjusting aux start from %d to %d\n",
-            (int) auxOffset, (int) adjOffset);
-        auxOffset = adjOffset;
-        lseek(fd, auxOffset, SEEK_SET);
-    }
-
-    /*
-     * Append any auxillary pre-computed data structures.
-     */
-    if (!writeAuxData(fd, pClassLookup, pIndexMapSet, pRegMapBuilder)) {
-        LOGW("Failed writing aux data\n");
-        goto bail;
-    }
-
-    endOffset = lseek(fd, 0, SEEK_END);
-    auxLength = endOffset - auxOffset;
-
-    /* compute checksum from start of deps to end of aux area */
-    if (!computeFileChecksum(fd, depsOffset,
-            (auxOffset+auxLength) - depsOffset, &optChecksum))
-    {
-        goto bail;
-    }
-
-    /*
-     * Output the "opt" header with all values filled in and a correct
-     * magic number.
-     */
-    DexOptHeader optHdr;
-    memset(&optHdr, 0xff, sizeof(optHdr));
-    memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);
-    memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);
-    optHdr.dexOffset = (u4) dexOffset;
-    optHdr.dexLength = (u4) dexLength;
-    optHdr.depsOffset = (u4) depsOffset;
-    optHdr.depsLength = (u4) depsLength;
-    optHdr.auxOffset = (u4) auxOffset;
-    optHdr.auxLength = (u4) auxLength;
-
-    optHdr.flags = headerFlags;
-    optHdr.checksum = optChecksum;
-
-    ssize_t actual;
-    lseek(fd, 0, SEEK_SET);
-    actual = write(fd, &optHdr, sizeof(optHdr));
-    if (actual != sizeof(optHdr)) {
-        logFailedWrite(sizeof(optHdr), actual, "opt header", errno);
-        goto bail;
-    }
-
-    LOGV("Successfully wrote DEX header\n");
-    result = true;
-
-    //dvmRegisterMapDumpStats();
-
-bail:
-    dvmFreeIndexMapSet(pIndexMapSet);
-    dvmFreeRegisterMapBuilder(pRegMapBuilder);
-    free(pClassLookup);
-    return result;
-}
-
-
-/*
- * Get the cache file name from a ClassPathEntry.
- */
-static const char* getCacheFileName(const ClassPathEntry* cpe)
-{
-    switch (cpe->kind) {
-    case kCpeJar:
-        return dvmGetJarFileCacheFileName((JarFile*) cpe->ptr);
-    case kCpeDex:
-        return dvmGetRawDexFileCacheFileName((RawDexFile*) cpe->ptr);
-    default:
-        LOGE("DexOpt: unexpected cpe kind %d\n", cpe->kind);
-        dvmAbort();
-        return NULL;
-    }
-}
-
-/*
- * Get the SHA-1 signature.
- */
-static const u1* getSignature(const ClassPathEntry* cpe)
-{
-    DvmDex* pDvmDex;
-
-    switch (cpe->kind) {
-    case kCpeJar:
-        pDvmDex = dvmGetJarFileDex((JarFile*) cpe->ptr);
-        break;
-    case kCpeDex:
-        pDvmDex = dvmGetRawDexFileDex((RawDexFile*) cpe->ptr);
-        break;
-    default:
-        LOGE("unexpected cpe kind %d\n", cpe->kind);
-        dvmAbort();
-        pDvmDex = NULL;         // make gcc happy
-    }
-
-    assert(pDvmDex != NULL);
-    return pDvmDex->pDexFile->pHeader->signature;
-}
-
-
-/*
- * Dependency layout:
- *  4b  Source file modification time, in seconds since 1970 UTC
- *  4b  CRC-32 from Zip entry, or Adler32 from source DEX header
- *  4b  Dalvik VM build number
- *  4b  Number of dependency entries that follow
- *  Dependency entries:
- *    4b  Name length (including terminating null)
- *    var Full path of cache entry (null terminated)
- *    20b SHA-1 signature from source DEX file
- *
- * If this changes, update DEX_OPT_MAGIC_VERS.
- */
-static const size_t kMinDepSize = 4 * 4;
-static const size_t kMaxDepSize = 4 * 4 + 1024;     // sanity check
-
-/*
- * Read the "opt" header, verify it, then read the dependencies section
- * and verify that data as well.
- *
- * If "sourceAvail" is "true", this will verify that "modWhen" and "crc"
- * match up with what is stored in the header.  If they don't, we reject
- * the file so that it can be recreated from the updated original.  If
- * "sourceAvail" isn't set, e.g. for a .odex file, we ignore these arguments.
- *
- * On successful return, the file will be seeked immediately past the
- * "opt" header.
- */
-bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
-    u4 crc, bool expectVerify, bool expectOpt)
-{
-    DexOptHeader optHdr;
-    u1* depData = NULL;
-    const u1* magic;
-    off_t posn;
-    int result = false;
-    ssize_t actual;
-
-    /*
-     * Start at the start.  The "opt" header, when present, will always be
-     * the first thing in the file.
-     */
-    if (lseek(fd, 0, SEEK_SET) != 0) {
-        LOGE("DexOpt: failed to seek to start of file: %s\n", strerror(errno));
-        goto bail;
-    }
-
-    /*
-     * Read and do trivial verification on the opt header.  The header is
-     * always in host byte order.
-     */
-    if (read(fd, &optHdr, sizeof(optHdr)) != sizeof(optHdr)) {
-        LOGE("DexOpt: failed reading opt header: %s\n", strerror(errno));
-        goto bail;
-    }
-
-    magic = optHdr.magic;
-    if (memcmp(magic, DEX_OPT_MAGIC, 4) != 0) {
-        /* not a DEX file, or previous attempt was interrupted */
-        LOGD("DexOpt: incorrect opt magic number (0x%02x %02x %02x %02x)\n",
-            magic[0], magic[1], magic[2], magic[3]);
-        goto bail;
-    }
-    if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
-        LOGW("DexOpt: stale opt version (0x%02x %02x %02x %02x)\n",
-            magic[4], magic[5], magic[6], magic[7]);
-        goto bail;
-    }
-    if (optHdr.depsLength < kMinDepSize || optHdr.depsLength > kMaxDepSize) {
-        LOGW("DexOpt: weird deps length %d, bailing\n", optHdr.depsLength);
-        goto bail;
-    }
-
-    /*
-     * Do the header flags match up with what we want?
-     *
-     * This is useful because it allows us to automatically regenerate
-     * a file when settings change (e.g. verification is now mandatory),
-     * but can cause difficulties if the bootstrap classes we depend upon
-     * were handled differently than the current options specify.  We get
-     * upset because they're not verified or optimized, but we're not able
-     * to regenerate them because the installer won't let us.
-     *
-     * (This is also of limited value when !sourceAvail.)
-     *
-     * So, for now, we essentially ignore "expectVerify" and "expectOpt"
-     * by limiting the match mask.
-     *
-     * The only thing we really can't handle is incorrect byte-ordering.
-     */
-    const u4 matchMask = DEX_OPT_FLAG_BIG;
-    u4 expectedFlags = 0;
-#if __BYTE_ORDER != __LITTLE_ENDIAN
-    expectedFlags |= DEX_OPT_FLAG_BIG;
-#endif
-    if (expectVerify)
-        expectedFlags |= DEX_FLAG_VERIFIED;
-    if (expectOpt)
-        expectedFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
-    if ((expectedFlags & matchMask) != (optHdr.flags & matchMask)) {
-        LOGI("DexOpt: header flag mismatch (0x%02x vs 0x%02x, mask=0x%02x)\n",
-            expectedFlags, optHdr.flags, matchMask);
-        goto bail;
-    }
-
-    posn = lseek(fd, optHdr.depsOffset, SEEK_SET);
-    if (posn < 0) {
-        LOGW("DexOpt: seek to deps failed: %s\n", strerror(errno));
-        goto bail;
-    }
-
-    /*
-     * Read all of the dependency stuff into memory.
-     */
-    depData = (u1*) malloc(optHdr.depsLength);
-    if (depData == NULL) {
-        LOGW("DexOpt: unable to allocate %d bytes for deps\n",
-            optHdr.depsLength);
-        goto bail;
-    }
-    actual = read(fd, depData, optHdr.depsLength);
-    if (actual != (ssize_t) optHdr.depsLength) {
-        LOGW("DexOpt: failed reading deps: %d of %d (err=%s)\n",
-            (int) actual, optHdr.depsLength, strerror(errno));
-        goto bail;
-    }
-
-    /*
-     * Verify simple items.
-     */
-    const u1* ptr;
-    u4 val;
-
-    ptr = depData;
-    val = read4LE(&ptr);
-    if (sourceAvail && val != modWhen) {
-        LOGI("DexOpt: source file mod time mismatch (%08x vs %08x)\n",
-            val, modWhen);
-        goto bail;
-    }
-    val = read4LE(&ptr);
-    if (sourceAvail && val != crc) {
-        LOGI("DexOpt: source file CRC mismatch (%08x vs %08x)\n", val, crc);
-        goto bail;
-    }
-    val = read4LE(&ptr);
-    if (val != DALVIK_VM_BUILD) {
-        LOGD("DexOpt: VM build version mismatch (%d vs %d)\n",
-            val, DALVIK_VM_BUILD);
-        goto bail;
-    }
-
-    /*
-     * Verify dependencies on other cached DEX files.  It must match
-     * exactly with what is currently defined in the bootclasspath.
-     */
-    ClassPathEntry* cpe;
-    u4 numDeps;
-
-    numDeps = read4LE(&ptr);
-    LOGV("+++ DexOpt: numDeps = %d\n", numDeps);
-    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
-        const char* cacheFileName = getCacheFileName(cpe);
-        const u1* signature = getSignature(cpe);
-        size_t len = strlen(cacheFileName) +1;
-        u4 storedStrLen;
-
-        if (numDeps == 0) {
-            /* more entries in bootclasspath than in deps list */
-            LOGI("DexOpt: not all deps represented\n");
-            goto bail;
-        }
-
-        storedStrLen = read4LE(&ptr);
-        if (len != storedStrLen ||
-            strcmp(cacheFileName, (const char*) ptr) != 0)
-        {
-            LOGI("DexOpt: mismatch dep name: '%s' vs. '%s'\n",
-                cacheFileName, ptr);
-            goto bail;
-        }
-
-        ptr += storedStrLen;
-
-        if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
-            LOGI("DexOpt: mismatch dep signature for '%s'\n", cacheFileName);
-            goto bail;
-        }
-        ptr += kSHA1DigestLen;
-
-        LOGV("DexOpt: dep match on '%s'\n", cacheFileName);
-
-        numDeps--;
-    }
-
-    if (numDeps != 0) {
-        /* more entries in deps list than in classpath */
-        LOGI("DexOpt: Some deps went away\n");
-        goto bail;
-    }
-
-    // consumed all data and no more?
-    if (ptr != depData + optHdr.depsLength) {
-        LOGW("DexOpt: Spurious dep data? %d vs %d\n",
-            (int) (ptr - depData), optHdr.depsLength);
-        assert(false);
-    }
-
-    result = true;
-
-bail:
-    free(depData);
-    return result;
-}
-
-/*
- * Write the dependency info to "fd" at the current file position.
- */
-static int writeDependencies(int fd, u4 modWhen, u4 crc)
-{
-    u1* buf = NULL;
-    ssize_t actual;
-    int result = -1;
-    ssize_t bufLen;
-    ClassPathEntry* cpe;
-    int i, numDeps;
-
-    /*
-     * Count up the number of completed entries in the bootclasspath.
-     */
-    numDeps = 0;
-    bufLen = 0;
-    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
-        const char* cacheFileName = getCacheFileName(cpe);
-        LOGV("+++ DexOpt: found dep '%s'\n", cacheFileName);
-
-        numDeps++;
-        bufLen += strlen(cacheFileName) +1;
-    }
-
-    bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
-
-    buf = malloc(bufLen);
-
-    set4LE(buf+0, modWhen);
-    set4LE(buf+4, crc);
-    set4LE(buf+8, DALVIK_VM_BUILD);
-    set4LE(buf+12, numDeps);
-
-    // TODO: do we want to add dvmGetInlineOpsTableLength() here?  Won't
-    // help us if somebody replaces an existing entry, but it'd catch
-    // additions/removals.
-
-    u1* ptr = buf + 4*4;
-    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
-        const char* cacheFileName = getCacheFileName(cpe);
-        const u1* signature = getSignature(cpe);
-        int len = strlen(cacheFileName) +1;
-
-        if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
-            LOGE("DexOpt: overran buffer\n");
-            dvmAbort();
-        }
-
-        set4LE(ptr, len);
-        ptr += 4;
-        memcpy(ptr, cacheFileName, len);
-        ptr += len;
-        memcpy(ptr, signature, kSHA1DigestLen);
-        ptr += kSHA1DigestLen;
-    }
-
-    assert(ptr == buf + bufLen);
-
-    actual = write(fd, buf, bufLen);
-    if (actual != bufLen) {
-        result = (errno != 0) ? errno : -1;
-        logFailedWrite(bufLen, actual, "dep info", errno);
-    } else {
-        result = 0;
-    }
-
-    free(buf);
-    return result;
-}
-
-
-/*
- * Write a block of data in "chunk" format.
- *
- * The chunk header fields are always in "native" byte order.  If "size"
- * is not a multiple of 8 bytes, the data area is padded out.
- */
-static bool writeChunk(int fd, u4 type, const void* data, size_t size)
-{
-    ssize_t actual;
-    union {             /* save a syscall by grouping these together */
-        char raw[8];
-        struct {
-            u4 type;
-            u4 size;
-        } ts;
-    } header;
-
-    assert(sizeof(header) == 8);
-
-    LOGV("Writing chunk, type=%.4s size=%d\n", (char*) &type, size);
-
-    header.ts.type = type;
-    header.ts.size = (u4) size;
-    actual = write(fd, &header, sizeof(header));
-    if (actual != sizeof(header)) {
-        logFailedWrite(size, actual, "aux chunk header write", errno);
-        return false;
-    }
-
-    if (size > 0) {
-        actual = write(fd, data, size);
-        if (actual != (ssize_t) size) {
-            logFailedWrite(size, actual, "aux chunk write", errno);
-            return false;
-        }
-    }
-
-    /* if necessary, pad to 64-bit alignment */
-    if ((size & 7) != 0) {
-        int padSize = 8 - (size & 7);
-        LOGV("size was %d, inserting %d pad bytes\n", size, padSize);
-        lseek(fd, padSize, SEEK_CUR);
-    }
-
-    assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
-
-    return true;
-}
-
-/*
- * Write aux data.
- *
- * We have different pieces, some of which may be optional.  To make the
- * most effective use of space, we use a "chunk" format, with a 4-byte
- * type and a 4-byte length.  We guarantee 64-bit alignment for the data,
- * so it can be used directly when the file is mapped for reading.
- */
-static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,
-    const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder)
-{
-    /* pre-computed class lookup hash table */
-    if (!writeChunk(fd, (u4) kDexChunkClassLookup,
-            pClassLookup, pClassLookup->size))
-    {
-        return false;
-    }
-
-    /* remapped constants (optional) */
-    if (pIndexMapSet != NULL) {
-        if (!writeChunk(fd, pIndexMapSet->chunkType,
-                pIndexMapSet->chunkData, pIndexMapSet->chunkDataLen))
-        {
-            return false;
-        }
-    }
-
-    /* register maps (optional) */
-    if (pRegMapBuilder != NULL) {
-        if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
-                pRegMapBuilder->data, pRegMapBuilder->size))
-        {
-            return false;
-        }
-    }
-
-    /* write the end marker */
-    if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
-        return false;
-    }
-
-    return true;
-}
-
-/*
- * Log a failed write.
- */
-static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
-    int err)
-{
-    LOGE("Write failed: %s (%d of %d): %s\n",
-        msg, (int)actual, (int)expected, strerror(err));
-}
-
-/*
- * Compute a checksum on a piece of an open file.
- *
- * File will be positioned at end of checksummed area.
- *
- * Returns "true" on success.
- */
-static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum)
-{
-    unsigned char readBuf[8192];
-    ssize_t actual;
-    uLong adler;
-
-    if (lseek(fd, start, SEEK_SET) != start) {
-        LOGE("Unable to seek to start of checksum area (%ld): %s\n",
-            (long) start, strerror(errno));
-        return false;
-    }
-
-    adler = adler32(0L, Z_NULL, 0);
-
-    while (length != 0) {
-        size_t wanted = (length < sizeof(readBuf)) ? length : sizeof(readBuf);
-        actual = read(fd, readBuf, wanted);
-        if (actual <= 0) {
-            LOGE("Read failed (%d) while computing checksum (len=%zu): %s\n",
-                (int) actual, length, strerror(errno));
-            return false;
-        }
-
-        adler = adler32(adler, readBuf, actual);
-
-        length -= actual;
-    }
-
-    *pSum = adler;
-    return true;
-}
-
-
-/*
- * ===========================================================================
- *      Optimizations
- * ===========================================================================
- */
-
-/*
- * Perform in-place rewrites on a memory-mapped DEX file.
- *
- * This happens in a short-lived child process, so we can go nutty with
- * loading classes and allocating memory.
- */
-static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
-    u4* pHeaderFlags, DexClassLookup** ppClassLookup)
-{
-    u8 prepWhen, loadWhen, verifyWhen, optWhen;
-    DvmDex* pDvmDex = NULL;
-    bool result = false;
-
-    *pHeaderFlags = 0;
-
-    LOGV("+++ swapping bytes\n");
-    if (dexFixByteOrdering(addr, len) != 0)
-        goto bail;
-#if __BYTE_ORDER != __LITTLE_ENDIAN
-    *pHeaderFlags |= DEX_OPT_FLAG_BIG;
-#endif
-
-    /*
-     * Now that the DEX file can be read directly, create a DexFile for it.
-     */
-    if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
-        LOGE("Unable to create DexFile\n");
-        goto bail;
-    }
-
-    /*
-     * Create the class lookup table.
-     */
-    //startWhen = dvmGetRelativeTimeUsec();
-    *ppClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
-    if (*ppClassLookup == NULL)
-        goto bail;
-
-    /*
-     * Bail out early if they don't want The Works.  The current implementation
-     * doesn't fork a new process if this flag isn't set, so we really don't
-     * want to continue on with the crazy class loading.
-     */
-    if (!doVerify && !doOpt) {
-        result = true;
-        goto bail;
-    }
-
-    /* this is needed for the next part */
-    pDvmDex->pDexFile->pClassLookup = *ppClassLookup;
-
-    prepWhen = dvmGetRelativeTimeUsec();
-
-    /*
-     * Load all classes found in this DEX file.  If they fail to load for
-     * some reason, they won't get verified (which is as it should be).
-     */
-    if (!loadAllClasses(pDvmDex))
-        goto bail;
-    loadWhen = dvmGetRelativeTimeUsec();
-
-    /*
-     * Verify all classes in the DEX file.  Export the "is verified" flag
-     * to the DEX file we're creating.
-     */
-    if (doVerify) {
-        dvmVerifyAllClasses(pDvmDex->pDexFile);
-        *pHeaderFlags |= DEX_FLAG_VERIFIED;
-    }
-    verifyWhen = dvmGetRelativeTimeUsec();
-
-    /*
-     * Optimize the classes we successfully loaded.  If the opt mode is
-     * OPTIMIZE_MODE_VERIFIED, each class must have been successfully
-     * verified or we'll skip it.
-     */
-#ifndef PROFILE_FIELD_ACCESS
-    if (doOpt) {
-        optimizeLoadedClasses(pDvmDex->pDexFile);
-        *pHeaderFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
-    }
-#endif
-    optWhen = dvmGetRelativeTimeUsec();
-
-    LOGD("DexOpt: load %dms, verify %dms, opt %dms\n",
-        (int) (loadWhen - prepWhen) / 1000,
-        (int) (verifyWhen - loadWhen) / 1000,
-        (int) (optWhen - verifyWhen) / 1000);
-
-    result = true;
-
-bail:
-    /* free up storage */
-    dvmDexFileFree(pDvmDex);
-
-    return result;
-}
-
-/*
- * Update the Adler-32 checksum stored in the DEX file.  This covers the
- * swapped and optimized DEX data, but does not include the opt header
- * or auxillary data.
- */
-static void updateChecksum(u1* addr, int len, DexHeader* pHeader)
-{
-    /*
-     * Rewrite the checksum.  We leave the SHA-1 signature alone.
-     */
-    uLong adler = adler32(0L, Z_NULL, 0);
-    const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
-
-    adler = adler32(adler, addr + nonSum, len - nonSum);
-    pHeader->checksum = adler;
-}
-
-/*
- * Try to load all classes in the specified DEX.  If they have some sort
- * of broken dependency, e.g. their superclass lives in a different DEX
- * that wasn't previously loaded into the bootstrap class path, loading
- * will fail.  This is the desired behavior.
- *
- * We have no notion of class loader at this point, so we load all of
- * the classes with the bootstrap class loader.  It turns out this has
- * exactly the behavior we want, and has no ill side effects because we're
- * running in a separate process and anything we load here will be forgotten.
- *
- * We set the CLASS_MULTIPLE_DEFS flag here if we see multiple definitions.
- * This works because we only call here as part of optimization / pre-verify,
- * not during verification as part of loading a class into a running VM.
- *
- * This returns "false" if the world is too screwed up to do anything
- * useful at all.
- */
-static bool loadAllClasses(DvmDex* pDvmDex)
-{
-    u4 count = pDvmDex->pDexFile->pHeader->classDefsSize;
-    u4 idx;
-    int loaded = 0;
-
-    LOGV("DexOpt: +++ trying to load %d classes\n", count);
-
-    dvmSetBootPathExtraDex(pDvmDex);
-
-    /*
-     * We have some circularity issues with Class and Object that are most
-     * easily avoided by ensuring that Object is never the first thing we
-     * try to find.  Take care of that here.  (We only need to do this when
-     * loading classes from the DEX file that contains Object, and only
-     * when Object comes first in the list, but it costs very little to
-     * do it in all cases.)
-     */
-    if (dvmFindSystemClass("Ljava/lang/Class;") == NULL) {
-        LOGE("ERROR: java.lang.Class does not exist!\n");
-        return false;
-    }
-
-    for (idx = 0; idx < count; idx++) {
-        const DexClassDef* pClassDef;
-        const char* classDescriptor;
-        ClassObject* newClass;
-
-        pClassDef = dexGetClassDef(pDvmDex->pDexFile, idx);
-        classDescriptor =
-            dexStringByTypeIdx(pDvmDex->pDexFile, pClassDef->classIdx);
-
-        LOGV("+++  loading '%s'", classDescriptor);
-        //newClass = dvmDefineClass(pDexFile, classDescriptor,
-        //        NULL);
-        newClass = dvmFindSystemClassNoInit(classDescriptor);
-        if (newClass == NULL) {
-            LOGV("DexOpt: failed loading '%s'\n", classDescriptor);
-            dvmClearOptException(dvmThreadSelf());
-        } else if (newClass->pDvmDex != pDvmDex) {
-            /*
-             * We don't load the new one, and we tag the first one found
-             * with the "multiple def" flag so the resolver doesn't try
-             * to make it available.
-             */
-            LOGD("DexOpt: '%s' has an earlier definition; blocking out\n",
-                classDescriptor);
-            SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
-        } else {
-            loaded++;
-        }
-    }
-    LOGV("DexOpt: +++ successfully loaded %d classes\n", loaded);
-
-    dvmSetBootPathExtraDex(NULL);
-    return true;
-}
-
-
-/*
- * Create a table of inline substitutions.
- *
- * TODO: this is currently just a linear array.  We will want to put this
- * into a hash table as the list size increases.
- */
-static InlineSub* createInlineSubsTable(void)
-{
-    const InlineOperation* ops = dvmGetInlineOpsTable();
-    const int count = dvmGetInlineOpsTableLength();
-    InlineSub* table;
-    Method* method;
-    ClassObject* clazz;
-    int i, tableIndex;
-
-    /*
-     * Allocate for optimism: one slot per entry, plus an end-of-list marker.
-     */
-    table = malloc(sizeof(InlineSub) * (count+1));
-
-    tableIndex = 0;
-    for (i = 0; i < count; i++) {
-        clazz = dvmFindClassNoInit(ops[i].classDescriptor, NULL);
-        if (clazz == NULL) {
-            LOGV("DexOpt: can't inline for class '%s': not found\n",
-                ops[i].classDescriptor);
-            dvmClearOptException(dvmThreadSelf());
-        } else {
-            /*
-             * Method could be virtual or direct.  Try both.  Don't use
-             * the "hier" versions.
-             */
-            method = dvmFindDirectMethodByDescriptor(clazz, ops[i].methodName,
-                        ops[i].methodSignature);
-            if (method == NULL)
-                method = dvmFindVirtualMethodByDescriptor(clazz, ops[i].methodName,
-                        ops[i].methodSignature);
-            if (method == NULL) {
-                LOGW("DexOpt: can't inline %s.%s %s: method not found\n",
-                    ops[i].classDescriptor, ops[i].methodName,
-                    ops[i].methodSignature);
-            } else {
-                if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
-                    LOGW("DexOpt: WARNING: inline op on non-final class/method "
-                         "%s.%s\n",
-                        clazz->descriptor, method->name);
-                    /* fail? */
-                }
-                if (dvmIsSynchronizedMethod(method) ||
-                    dvmIsDeclaredSynchronizedMethod(method))
-                {
-                    LOGW("DexOpt: WARNING: inline op on synchronized method "
-                         "%s.%s\n",
-                        clazz->descriptor, method->name);
-                    /* fail? */
-                }
-
-                table[tableIndex].method = method;
-                table[tableIndex].inlineIdx = i;
-                tableIndex++;
-
-                LOGV("DexOpt: will inline %d: %s.%s %s\n", i,
-                    ops[i].classDescriptor, ops[i].methodName,
-                    ops[i].methodSignature);
-            }
-        }
-    }
-
-    /* mark end of table */
-    table[tableIndex].method = NULL;
-    LOGV("DexOpt: inline table has %d entries\n", tableIndex);
-
-    return table;
-}
-
-/*
- * Run through all classes that were successfully loaded from this DEX
- * file and optimize their code sections.
- */
-static void optimizeLoadedClasses(DexFile* pDexFile)
-{
-    u4 count = pDexFile->pHeader->classDefsSize;
-    u4 idx;
-    InlineSub* inlineSubs = NULL;
-
-    LOGV("DexOpt: +++ optimizing up to %d classes\n", count);
-    assert(gDvm.dexOptMode != OPTIMIZE_MODE_NONE);
-
-    inlineSubs = createInlineSubsTable();
-
-    for (idx = 0; idx < count; idx++) {
-        const DexClassDef* pClassDef;
-        const char* classDescriptor;
-        ClassObject* clazz;
-
-        pClassDef = dexGetClassDef(pDexFile, idx);
-        classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
-
-        /* all classes are loaded into the bootstrap class loader */
-        clazz = dvmLookupClass(classDescriptor, NULL, false);
-        if (clazz != NULL) {
-            if ((pClassDef->accessFlags & CLASS_ISPREVERIFIED) == 0 &&
-                gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
-            {
-                LOGV("DexOpt: not optimizing '%s': not verified\n",
-                    classDescriptor);
-            } else if (clazz->pDvmDex->pDexFile != pDexFile) {
-                /* shouldn't be here -- verifier should have caught */
-                LOGD("DexOpt: not optimizing '%s': multiple definitions\n",
-                    classDescriptor);
-            } else {
-                optimizeClass(clazz, inlineSubs);
-
-                /* set the flag whether or not we actually did anything */
-                ((DexClassDef*)pClassDef)->accessFlags |=
-                    CLASS_ISOPTIMIZED;
-            }
-        } else {
-            LOGV("DexOpt: not optimizing unavailable class '%s'\n",
-                classDescriptor);
-        }
-    }
-
-    free(inlineSubs);
-}
-
-/*
- * Optimize the specified class.
- */
-static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs)
-{
-    int i;
-
-    for (i = 0; i < clazz->directMethodCount; i++) {
-        if (!optimizeMethod(&clazz->directMethods[i], inlineSubs))
-            goto fail;
-    }
-    for (i = 0; i < clazz->virtualMethodCount; i++) {
-        if (!optimizeMethod(&clazz->virtualMethods[i], inlineSubs))
-            goto fail;
-    }
-
-    return;
-
-fail:
-    LOGV("DexOpt: ceasing optimization attempts on %s\n", clazz->descriptor);
-}
-
-/*
- * Optimize instructions in a method.
- *
- * Returns "true" if all went well, "false" if we bailed out early when
- * something failed.
- */
-static bool optimizeMethod(Method* method, const InlineSub* inlineSubs)
-{
-    u4 insnsSize;
-    u2* insns;
-    u2 inst;
-
-    if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
-        return true;
-
-    insns = (u2*) method->insns;
-    assert(insns != NULL);
-    insnsSize = dvmGetMethodInsnsSize(method);
-
-    while (insnsSize > 0) {
-        int width;
-
-        inst = *insns & 0xff;
-
-        switch (inst) {
-        case OP_IGET:
-        case OP_IGET_BOOLEAN:
-        case OP_IGET_BYTE:
-        case OP_IGET_CHAR:
-        case OP_IGET_SHORT:
-            rewriteInstField(method, insns, OP_IGET_QUICK);
-            break;
-        case OP_IGET_WIDE:
-            rewriteInstField(method, insns, OP_IGET_WIDE_QUICK);
-            break;
-        case OP_IGET_OBJECT:
-            rewriteInstField(method, insns, OP_IGET_OBJECT_QUICK);
-            break;
-        case OP_IPUT:
-        case OP_IPUT_BOOLEAN:
-        case OP_IPUT_BYTE:
-        case OP_IPUT_CHAR:
-        case OP_IPUT_SHORT:
-            rewriteInstField(method, insns, OP_IPUT_QUICK);
-            break;
-        case OP_IPUT_WIDE:
-            rewriteInstField(method, insns, OP_IPUT_WIDE_QUICK);
-            break;
-        case OP_IPUT_OBJECT:
-            rewriteInstField(method, insns, OP_IPUT_OBJECT_QUICK);
-            break;
-
-        case OP_INVOKE_VIRTUAL:
-            if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL,inlineSubs))
-            {
-                if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_VIRTUAL_QUICK))
-                    return false;
-            }
-            break;
-        case OP_INVOKE_VIRTUAL_RANGE:
-            if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL,
-                    inlineSubs))
-            {
-                if (!rewriteVirtualInvoke(method, insns,
-                        OP_INVOKE_VIRTUAL_QUICK_RANGE))
-                {
-                    return false;
-                }
-            }
-            break;
-        case OP_INVOKE_SUPER:
-            if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK))
-                return false;
-            break;
-        case OP_INVOKE_SUPER_RANGE:
-            if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE))
-                return false;
-            break;
-
-        case OP_INVOKE_DIRECT:
-            if (!rewriteExecuteInline(method, insns, METHOD_DIRECT, inlineSubs))
-            {
-                if (!rewriteEmptyDirectInvoke(method, insns))
-                    return false;
-            }
-            break;
-        case OP_INVOKE_DIRECT_RANGE:
-            rewriteExecuteInlineRange(method, insns, METHOD_DIRECT, inlineSubs);
-            break;
-
-        case OP_INVOKE_STATIC:
-            rewriteExecuteInline(method, insns, METHOD_STATIC, inlineSubs);
-            break;
-        case OP_INVOKE_STATIC_RANGE:
-            rewriteExecuteInlineRange(method, insns, METHOD_STATIC, inlineSubs);
-            break;
-
-        default:
-            // ignore this instruction
-            ;
-        }
-
-        if (*insns == kPackedSwitchSignature) {
-            width = 4 + insns[1] * 2;
-        } else if (*insns == kSparseSwitchSignature) {
-            width = 2 + insns[1] * 4;
-        } else if (*insns == kArrayDataSignature) {
-            u2 elemWidth = insns[1];
-            u4 len = insns[2] | (((u4)insns[3]) << 16);
-            width = 4 + (elemWidth * len + 1) / 2;
-        } else {
-            width = dexGetInstrWidthAbs(gDvm.instrWidth, inst);
-        }
-        assert(width > 0);
-
-        insns += width;
-        insnsSize -= width;
-    }
-
-    assert(insnsSize == 0);
-    return true;
-}
-
-
-/*
- * If "referrer" and "resClass" don't come from the same DEX file, and
- * the DEX we're working on is not destined for the bootstrap class path,
- * tweak the class loader so package-access checks work correctly.
- *
- * Only do this if we're doing pre-verification or optimization.
- */
-static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
-{
-    if (!gDvm.optimizing)
-        return;
-    assert(referrer->classLoader == NULL);
-    assert(resClass->classLoader == NULL);
-
-    if (!gDvm.optimizingBootstrapClass) {
-        /* class loader for an array class comes from element type */
-        if (dvmIsArrayClass(resClass))
-            resClass = resClass->elementClass;
-        if (referrer->pDvmDex != resClass->pDvmDex)
-            resClass->classLoader = (Object*) 0xdead3333;
-    }
-}
-
-/*
- * Undo the effects of tweakLoader.
- */
-static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
-{
-    if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
-        return;
-
-    if (dvmIsArrayClass(resClass))
-        resClass = resClass->elementClass;
-    resClass->classLoader = NULL;
-}
-
-
-/*
- * Alternate version of dvmResolveClass for use with verification and
- * optimization.  Performs access checks on every resolve, and refuses
- * to acknowledge the existence of classes defined in more than one DEX
- * file.
- *
- * Exceptions caused by failures are cleared before returning.
- *
- * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
- */
-ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
-    VerifyError* pFailure)
-{
-    DvmDex* pDvmDex = referrer->pDvmDex;
-    ClassObject* resClass;
-
-    /*
-     * Check the table first.  If not there, do the lookup by name.
-     */
-    resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
-    if (resClass == NULL) {
-        const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
-        if (className[0] != '\0' && className[1] == '\0') {
-            /* primitive type */
-            resClass = dvmFindPrimitiveClass(className[0]);
-        } else {
-            resClass = dvmFindClassNoInit(className, referrer->classLoader);
-        }
-        if (resClass == NULL) {
-            /* not found, exception should be raised */
-            LOGV("DexOpt: class %d (%s) not found\n",
-                classIdx,
-                dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
-            if (pFailure != NULL) {
-                /* dig through the wrappers to find the original failure */
-                Object* excep = dvmGetException(dvmThreadSelf());
-                while (true) {
-                    Object* cause = dvmGetExceptionCause(excep);
-                    if (cause == NULL)
-                        break;
-                    excep = cause;
-                }
-                if (strcmp(excep->clazz->descriptor,
-                    "Ljava/lang/IncompatibleClassChangeError;") == 0)
-                {
-                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
-                } else {
-                    *pFailure = VERIFY_ERROR_NO_CLASS;
-                }
-            }
-            dvmClearOptException(dvmThreadSelf());
-            return NULL;
-        }
-
-        /*
-         * Add it to the resolved table so we're faster on the next lookup.
-         */
-        dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
-    }
-
-    /* multiple definitions? */
-    if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
-        LOGI("DexOpt: not resolving ambiguous class '%s'\n",
-            resClass->descriptor);
-        if (pFailure != NULL)
-            *pFailure = VERIFY_ERROR_NO_CLASS;
-        return NULL;
-    }
-
-    /* access allowed? */
-    tweakLoader(referrer, resClass);
-    bool allowed = dvmCheckClassAccess(referrer, resClass);
-    untweakLoader(referrer, resClass);
-    if (!allowed) {
-        LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
-            referrer->descriptor, resClass->descriptor);
-        if (pFailure != NULL)
-            *pFailure = VERIFY_ERROR_ACCESS_CLASS;
-        return NULL;
-    }
-
-    return resClass;
-}
-
-/*
- * Alternate version of dvmResolveInstField().
- *
- * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
- */
-InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
-    VerifyError* pFailure)
-{
-    DvmDex* pDvmDex = referrer->pDvmDex;
-    InstField* resField;
-
-    resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
-    if (resField == NULL) {
-        const DexFieldId* pFieldId;
-        ClassObject* resClass;
-
-        pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
-
-        /*
-         * Find the field's class.
-         */
-        resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
-        if (resClass == NULL) {
-            //dvmClearOptException(dvmThreadSelf());
-            assert(!dvmCheckException(dvmThreadSelf()));
-            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
-            return NULL;
-        }
-
-        resField = (InstField*)dvmFindFieldHier(resClass,
-            dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
-            dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
-        if (resField == NULL) {
-            LOGD("DexOpt: couldn't find field %s.%s\n",
-                resClass->descriptor,
-                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
-            if (pFailure != NULL)
-                *pFailure = VERIFY_ERROR_NO_FIELD;
-            return NULL;
-        }
-        if (dvmIsStaticField(&resField->field)) {
-            LOGD("DexOpt: wanted instance, got static for field %s.%s\n",
-                resClass->descriptor,
-                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
-            if (pFailure != NULL)
-                *pFailure = VERIFY_ERROR_CLASS_CHANGE;
-            return NULL;
-        }
-
-        /*
-         * Add it to the resolved table so we're faster on the next lookup.
-         */
-        dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
-    }
-
-    /* access allowed? */
-    tweakLoader(referrer, resField->field.clazz);
-    bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
-    untweakLoader(referrer, resField->field.clazz);
-    if (!allowed) {
-        LOGI("DexOpt: access denied from %s to field %s.%s\n",
-            referrer->descriptor, resField->field.clazz->descriptor,
-            resField->field.name);
-        if (pFailure != NULL)
-            *pFailure = VERIFY_ERROR_ACCESS_FIELD;
-        return NULL;
-    }
-
-    return resField;
-}
-
-/*
- * Alternate version of dvmResolveStaticField().
- *
- * Does not force initialization of the resolved field's class.
- *
- * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
- */
-StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
-    VerifyError* pFailure)
-{
-    DvmDex* pDvmDex = referrer->pDvmDex;
-    StaticField* resField;
-
-    resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
-    if (resField == NULL) {
-        const DexFieldId* pFieldId;
-        ClassObject* resClass;
-
-        pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
-
-        /*
-         * Find the field's class.
-         */
-        resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
-        if (resClass == NULL) {
-            //dvmClearOptException(dvmThreadSelf());
-            assert(!dvmCheckException(dvmThreadSelf()));
-            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
-            return NULL;
-        }
-
-        resField = (StaticField*)dvmFindFieldHier(resClass,
-                    dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
-                    dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
-        if (resField == NULL) {
-            LOGD("DexOpt: couldn't find static field\n");
-            if (pFailure != NULL)
-                *pFailure = VERIFY_ERROR_NO_FIELD;
-            return NULL;
-        }
-        if (!dvmIsStaticField(&resField->field)) {
-            LOGD("DexOpt: wanted static, got instance for field %s.%s\n",
-                resClass->descriptor,
-                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
-            if (pFailure != NULL)
-                *pFailure = VERIFY_ERROR_CLASS_CHANGE;
-            return NULL;
-        }
-
-        /*
-         * Add it to the resolved table so we're faster on the next lookup.
-         *
-         * We can only do this if we're in "dexopt", because the presence
-         * of a valid value in the resolution table implies that the class
-         * containing the static field has been initialized.
-         */
-        if (gDvm.optimizing)
-            dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
-    }
-
-    /* access allowed? */
-    tweakLoader(referrer, resField->field.clazz);
-    bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
-    untweakLoader(referrer, resField->field.clazz);
-    if (!allowed) {
-        LOGI("DexOpt: access denied from %s to field %s.%s\n",
-            referrer->descriptor, resField->field.clazz->descriptor,
-            resField->field.name);
-        if (pFailure != NULL)
-            *pFailure = VERIFY_ERROR_ACCESS_FIELD;
-        return NULL;
-    }
-
-    return resField;
-}
-
-
-/*
- * Rewrite an iget/iput instruction.  These all have the form:
- *   op vA, vB, field@CCCC
- *
- * Where vA holds the value, vB holds the object reference, and CCCC is
- * the field reference constant pool offset.  We want to replace CCCC
- * with the byte offset from the start of the object.
- *
- * "clazz" is the referring class.  We need this because we verify
- * access rights here.
- */
-static void rewriteInstField(Method* method, u2* insns, OpCode newOpc)
-{
-    ClassObject* clazz = method->clazz;
-    u2 fieldIdx = insns[1];
-    InstField* field;
-    int byteOffset;
-
-    field = dvmOptResolveInstField(clazz, fieldIdx, NULL);
-    if (field == NULL) {
-        LOGI("DexOpt: unable to optimize field ref 0x%04x at 0x%02x in %s.%s\n",
-            fieldIdx, (int) (insns - method->insns), clazz->descriptor,
-            method->name);
-        return;
-    }
-
-    if (field->byteOffset >= 65536) {
-        LOGI("DexOpt: field offset exceeds 64K (%d)\n", field->byteOffset);
-        return;
-    }
-
-    insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
-    insns[1] = (u2) field->byteOffset;
-    LOGVV("DexOpt: rewrote access to %s.%s --> %d\n",
-        field->field.clazz->descriptor, field->field.name,
-        field->byteOffset);
-}
-
-/*
- * Alternate version of dvmResolveMethod().
- *
- * Doesn't throw exceptions, and checks access on every lookup.
- *
- * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
- */
-Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
-    MethodType methodType, VerifyError* pFailure)
-{
-    DvmDex* pDvmDex = referrer->pDvmDex;
-    Method* resMethod;
-
-    assert(methodType == METHOD_DIRECT ||
-           methodType == METHOD_VIRTUAL ||
-           methodType == METHOD_STATIC);
-
-    LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx,
-        referrer->descriptor);
-
-    resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
-    if (resMethod == NULL) {
-        const DexMethodId* pMethodId;
-        ClassObject* resClass;
-
-        pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
-
-        resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
-        if (resClass == NULL) {
-            /*
-             * Can't find the class that the method is a part of, or don't
-             * have permission to access the class.
-             */
-            LOGV("DexOpt: can't find called method's class (?.%s)\n",
-                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
-            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
-            return NULL;
-        }
-        if (dvmIsInterfaceClass(resClass)) {
-            /* method is part of an interface; this is wrong method for that */
-            LOGW("DexOpt: method is in an interface\n");
-            if (pFailure != NULL)
-                *pFailure = VERIFY_ERROR_GENERIC;
-            return NULL;
-        }
-
-        /*
-         * We need to chase up the class hierarchy to find methods defined
-         * in super-classes.  (We only want to check the current class
-         * if we're looking for a constructor.)
-         */
-        DexProto proto;
-        dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
-
-        if (methodType == METHOD_DIRECT) {
-            resMethod = dvmFindDirectMethod(resClass,
-                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
-        } else {
-            /* METHOD_STATIC or METHOD_VIRTUAL */
-            resMethod = dvmFindMethodHier(resClass,
-                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
-        }
-
-        if (resMethod == NULL) {
-            LOGV("DexOpt: couldn't find method '%s'\n",
-                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
-            if (pFailure != NULL)
-                *pFailure = VERIFY_ERROR_NO_METHOD;
-            return NULL;
-        }
-        if (methodType == METHOD_STATIC) {
-            if (!dvmIsStaticMethod(resMethod)) {
-                LOGD("DexOpt: wanted static, got instance for method %s.%s\n",
-                    resClass->descriptor, resMethod->name);
-                if (pFailure != NULL)
-                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
-                return NULL;
-            }
-        } else if (methodType == METHOD_VIRTUAL) {
-            if (dvmIsStaticMethod(resMethod)) {
-                LOGD("DexOpt: wanted instance, got static for method %s.%s\n",
-                    resClass->descriptor, resMethod->name);
-                if (pFailure != NULL)
-                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
-                return NULL;
-            }
-        }
-
-        /* see if this is a pure-abstract method */
-        if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
-            LOGW("DexOpt: pure-abstract method '%s' in %s\n",
-                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
-                resClass->descriptor);
-            if (pFailure != NULL)
-                *pFailure = VERIFY_ERROR_GENERIC;
-            return NULL;
-        }
-
-        /*
-         * Add it to the resolved table so we're faster on the next lookup.
-         *
-         * We can only do this for static methods if we're not in "dexopt",
-         * because the presence of a valid value in the resolution table
-         * implies that the class containing the static field has been
-         * initialized.
-         */
-        if (methodType != METHOD_STATIC || gDvm.optimizing)
-            dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
-    }
-
-    LOGVV("--- found method %d (%s.%s)\n",
-        methodIdx, resMethod->clazz->descriptor, resMethod->name);
-
-    /* access allowed? */
-    tweakLoader(referrer, resMethod->clazz);
-    bool allowed = dvmCheckMethodAccess(referrer, resMethod);
-    untweakLoader(referrer, resMethod->clazz);
-    if (!allowed) {
-        IF_LOGI() {
-            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
-            LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
-                resMethod->clazz->descriptor, resMethod->name, desc,
-                referrer->descriptor);
-            free(desc);
-        }
-        if (pFailure != NULL)
-            *pFailure = VERIFY_ERROR_ACCESS_METHOD;
-        return NULL;
-    }
-
-    return resMethod;
-}
-
-/*
- * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
- * invoke-super/range.  These all have the form:
- *   op vAA, meth@BBBB, reg stuff @CCCC
- *
- * We want to replace the method constant pool index BBBB with the
- * vtable index.
- */
-static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc)
-{
-    ClassObject* clazz = method->clazz;
-    Method* baseMethod;
-    u2 methodIdx = insns[1];
-
-    baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
-    if (baseMethod == NULL) {
-        LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n",
-            methodIdx,
-            (int) (insns - method->insns), clazz->descriptor,
-            method->name);
-        return false;
-    }
-
-    assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
-           (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
-           (insns[0] & 0xff) == OP_INVOKE_SUPER ||
-           (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
-
-    /*
-     * Note: Method->methodIndex is a u2 and is range checked during the
-     * initial load.
-     */
-    insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
-    insns[1] = baseMethod->methodIndex;
-
-    //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
-    //    method->clazz->descriptor, method->name,
-    //    baseMethod->clazz->descriptor, baseMethod->name);
-
-    return true;
-}
-
-/*
- * Rewrite invoke-direct, which has the form:
- *   op vAA, meth@BBBB, reg stuff @CCCC
- *
- * There isn't a lot we can do to make this faster, but in some situations
- * we can make it go away entirely.
- *
- * This must only be used when the invoked method does nothing and has
- * no return value (the latter being very important for verification).
- */
-static bool rewriteEmptyDirectInvoke(Method* method, u2* insns)
-{
-    ClassObject* clazz = method->clazz;
-    Method* calledMethod;
-    u2 methodIdx = insns[1];
-
-    calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
-    if (calledMethod == NULL) {
-        LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n",
-            methodIdx,
-            (int) (insns - method->insns), clazz->descriptor,
-            method->name);
-        return false;
-    }
-
-    /* TODO: verify that java.lang.Object() is actually empty! */
-    if (calledMethod->clazz == gDvm.classJavaLangObject &&
-        dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
-    {
-        /*
-         * Replace with "empty" instruction.  DO NOT disturb anything
-         * else about it, as we want it to function the same as
-         * OP_INVOKE_DIRECT when debugging is enabled.
-         */
-        assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
-        insns[0] = (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY;
-
-        //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
-        //    method->clazz->descriptor, method->name,
-        //    calledMethod->clazz->descriptor, calledMethod->name);
-    }
-
-    return true;
-}
-
-/*
- * Resolve an interface method reference.
- *
- * No method access check here -- interface methods are always public.
- *
- * Returns NULL if the method was not found.  Does not throw an exception.
- */
-Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
-{
-    DvmDex* pDvmDex = referrer->pDvmDex;
-    Method* resMethod;
-    int i;
-
-    LOGVV("--- resolving interface method %d (referrer=%s)\n",
-        methodIdx, referrer->descriptor);
-
-    resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
-    if (resMethod == NULL) {
-        const DexMethodId* pMethodId;
-        ClassObject* resClass;
-
-        pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
-
-        resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
-        if (resClass == NULL) {
-            /* can't find the class that the method is a part of */
-            dvmClearOptException(dvmThreadSelf());
-            return NULL;
-        }
-        if (!dvmIsInterfaceClass(resClass)) {
-            /* whoops */
-            LOGI("Interface method not part of interface class\n");
-            return NULL;
-        }
-
-        const char* methodName =
-            dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
-        DexProto proto;
-        dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
-
-        LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
-            methodName, methodSig, resClass->descriptor);
-        resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
-        if (resMethod == NULL) {
-            /* scan superinterfaces and superclass interfaces */
-            LOGVV("+++ did not resolve immediately\n");
-            for (i = 0; i < resClass->iftableCount; i++) {
-                resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
-                                methodName, &proto);
-                if (resMethod != NULL)
-                    break;
-            }
-
-            if (resMethod == NULL) {
-                LOGVV("+++ unable to resolve method %s\n", methodName);
-                return NULL;
-            }
-        } else {
-            LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
-                resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
-        }
-
-        /* we're expecting this to be abstract */
-        if (!dvmIsAbstractMethod(resMethod)) {
-            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
-            LOGW("Found non-abstract interface method %s.%s %s\n",
-                resMethod->clazz->descriptor, resMethod->name, desc);
-            free(desc);
-            return NULL;
-        }
-
-        /*
-         * Add it to the resolved table so we're faster on the next lookup.
-         */
-        dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
-    }
-
-    LOGVV("--- found interface method %d (%s.%s)\n",
-        methodIdx, resMethod->clazz->descriptor, resMethod->name);
-
-    /* interface methods are always public; no need to check access */
-
-    return resMethod;
-}
-
-/*
- * See if the method being called can be rewritten as an inline operation.
- * Works for invoke-virtual, invoke-direct, and invoke-static.
- *
- * Returns "true" if we replace it.
- */
-static bool rewriteExecuteInline(Method* method, u2* insns,
-    MethodType methodType, const InlineSub* inlineSubs)
-{
-    ClassObject* clazz = method->clazz;
-    Method* calledMethod;
-    u2 methodIdx = insns[1];
-
-    //return false;
-
-    calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
-    if (calledMethod == NULL) {
-        LOGV("+++ DexOpt inline: can't find %d\n", methodIdx);
-        return false;
-    }
-
-    while (inlineSubs->method != NULL) {
-        /*
-        if (extra) {
-            LOGI("comparing %p vs %p %s.%s %s\n",
-                inlineSubs->method, calledMethod,
-                inlineSubs->method->clazz->descriptor,
-                inlineSubs->method->name,
-                inlineSubs->method->signature);
-        }
-        */
-        if (inlineSubs->method == calledMethod) {
-            assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
-                   (insns[0] & 0xff) == OP_INVOKE_STATIC ||
-                   (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
-            insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE;
-            insns[1] = (u2) inlineSubs->inlineIdx;
-
-            //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
-            //    method->clazz->descriptor, method->name,
-            //    calledMethod->clazz->descriptor, calledMethod->name);
-            return true;
-        }
-
-        inlineSubs++;
-    }
-
-    return false;
-}
-
-/*
- * See if the method being called can be rewritten as an inline operation.
- * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range.
- *
- * Returns "true" if we replace it.
- */
-static bool rewriteExecuteInlineRange(Method* method, u2* insns,
-    MethodType methodType, const InlineSub* inlineSubs)
-{
-    ClassObject* clazz = method->clazz;
-    Method* calledMethod;
-    u2 methodIdx = insns[1];
-
-    calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
-    if (calledMethod == NULL) {
-        LOGV("+++ DexOpt inline/range: can't find %d\n", methodIdx);
-        return false;
-    }
-
-    while (inlineSubs->method != NULL) {
-        if (inlineSubs->method == calledMethod) {
-            assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
-                   (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
-                   (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
-            insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE;
-            insns[1] = (u2) inlineSubs->inlineIdx;
-
-            //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
-            //    method->clazz->descriptor, method->name,
-            //    calledMethod->clazz->descriptor, calledMethod->name);
-            return true;
-        }
-
-        inlineSubs++;
-    }
-
-    return false;
-}
-
diff --git a/vm/analysis/DexPrepare.c b/vm/analysis/DexPrepare.c
new file mode 100644
index 0000000..eea25c8
--- /dev/null
+++ b/vm/analysis/DexPrepare.c
@@ -0,0 +1,1207 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Prepare a DEX file for use by the VM.  Depending upon the VM options
+ * we will attempt to verify and/or optimize the code, possibly appending
+ * register maps.
+ *
+ * TODO: the format of the optimized header is currently "whatever we
+ * happen to write", since the VM that writes it is by definition the same
+ * as the VM that reads it.  Still, it should be better documented and
+ * more rigorously structured.
+ */
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+#include "analysis/RegisterMap.h"
+#include "analysis/Optimize.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+/* fwd */
+static void updateChecksum(u1* addr, int len, DexHeader* pHeader);
+static int writeDependencies(int fd, u4 modWhen, u4 crc);
+static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,\
+    const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder);
+static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
+    int err);
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum);
+
+
+/*
+ * Return the fd of an open file in the DEX file cache area.  If the cache
+ * file doesn't exist or is out of date, this will remove the old entry,
+ * create a new one (writing only the file header), and return with the
+ * "new file" flag set.
+ *
+ * It's possible to execute from an unoptimized DEX file directly,
+ * assuming the byte ordering and structure alignment is correct, but
+ * disadvantageous because some significant optimizations are not possible.
+ * It's not generally possible to do the same from an uncompressed Jar
+ * file entry, because we have to guarantee 32-bit alignment in the
+ * memory-mapped file.
+ *
+ * For a Jar/APK file (a zip archive with "classes.dex" inside), "modWhen"
+ * and "crc32" come from the Zip directory entry.  For a stand-alone DEX
+ * file, it's the modification date of the file and the Adler32 from the
+ * DEX header (which immediately follows the magic).  If these don't
+ * match what's stored in the opt header, we reject the file immediately.
+ *
+ * On success, the file descriptor will be positioned just past the "opt"
+ * file header, and will be locked with flock.  "*pCachedName" will point
+ * to newly-allocated storage.
+ */
+int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
+    u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
+{
+    int fd, cc;
+    struct stat fdStat, fileStat;
+    bool readOnly = false;
+
+    *pNewFile = false;
+
+retry:
+    /*
+     * Try to open the cache file.  If we've been asked to,
+     * create it if it doesn't exist.
+     */
+    fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
+    if (fd < 0) {
+        fd = open(cacheFileName, O_RDONLY, 0);
+        if (fd < 0) {
+            if (createIfMissing) {
+                LOGE("Can't open dex cache '%s': %s\n",
+                    cacheFileName, strerror(errno));
+            }
+            return fd;
+        }
+        readOnly = true;
+    }
+
+    /*
+     * Grab an exclusive lock on the cache file.  If somebody else is
+     * working on it, we'll block here until they complete.  Because
+     * we're waiting on an external resource, we go into VMWAIT mode.
+     */
+    int oldStatus;
+    LOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)\n",
+        cacheFileName, fd, isBootstrap);
+    oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+    cc = flock(fd, LOCK_EX | LOCK_NB);
+    if (cc != 0) {
+        LOGD("DexOpt: sleeping on flock(%s)\n", cacheFileName);
+        cc = flock(fd, LOCK_EX);
+    }
+    dvmChangeStatus(NULL, oldStatus);
+    if (cc != 0) {
+        LOGE("Can't lock dex cache '%s': %d\n", cacheFileName, cc);
+        close(fd);
+        return -1;
+    }
+    LOGV("DexOpt:  locked cache file\n");
+
+    /*
+     * Check to see if the fd we opened and locked matches the file in
+     * the filesystem.  If they don't, then somebody else unlinked ours
+     * and created a new file, and we need to use that one instead.  (If
+     * we caught them between the unlink and the create, we'll get an
+     * ENOENT from the file stat.)
+     */
+    cc = fstat(fd, &fdStat);
+    if (cc != 0) {
+        LOGE("Can't stat open file '%s'\n", cacheFileName);
+        LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
+        goto close_fail;
+    }
+    cc = stat(cacheFileName, &fileStat);
+    if (cc != 0 ||
+        fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
+    {
+        LOGD("DexOpt: our open cache file is stale; sleeping and retrying\n");
+        LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
+        flock(fd, LOCK_UN);
+        close(fd);
+        usleep(250 * 1000);     /* if something is hosed, don't peg machine */
+        goto retry;
+    }
+
+    /*
+     * We have the correct file open and locked.  If the file size is zero,
+     * then it was just created by us, and we want to fill in some fields
+     * in the "opt" header and set "*pNewFile".  Otherwise, we want to
+     * verify that the fields in the header match our expectations, and
+     * reset the file if they don't.
+     */
+    if (fdStat.st_size == 0) {
+        if (readOnly) {
+            LOGW("DexOpt: file has zero length and isn't writable\n");
+            goto close_fail;
+        }
+        cc = dexOptCreateEmptyHeader(fd);
+        if (cc != 0)
+            goto close_fail;
+        *pNewFile = true;
+        LOGV("DexOpt: successfully initialized new cache file\n");
+    } else {
+        bool expectVerify, expectOpt;
+
+        if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
+            expectVerify = false;
+        else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
+            expectVerify = !isBootstrap;
+        else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
+            expectVerify = true;
+
+        if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
+            expectOpt = false;
+        else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
+            expectOpt = expectVerify;
+        else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
+            expectOpt = true;
+
+        LOGV("checking deps, expecting vfy=%d opt=%d\n",
+            expectVerify, expectOpt);
+
+        if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
+                expectVerify, expectOpt))
+        {
+            if (readOnly) {
+                /*
+                 * We could unlink and rewrite the file if we own it or
+                 * the "sticky" bit isn't set on the directory.  However,
+                 * we're not able to truncate it, which spoils things.  So,
+                 * give up now.
+                 */
+                if (createIfMissing) {
+                    LOGW("Cached DEX '%s' (%s) is stale and not writable\n",
+                        fileName, cacheFileName);
+                }
+                goto close_fail;
+            }
+
+            /*
+             * If we truncate the existing file before unlinking it, any
+             * process that has it mapped will fail when it tries to touch
+             * the pages.
+             *
+             * This is very important.  The zygote process will have the
+             * boot DEX files (core, framework, etc.) mapped early.  If
+             * (say) core.dex gets updated, and somebody launches an app
+             * that uses App.dex, then App.dex gets reoptimized because it's
+             * dependent upon the boot classes.  However, dexopt will be
+             * using the *new* core.dex to do the optimizations, while the
+             * app will actually be running against the *old* core.dex
+             * because it starts from zygote.
+             *
+             * Even without zygote, it's still possible for a class loader
+             * to pull in an APK that was optimized against an older set
+             * of DEX files.  We must ensure that everything fails when a
+             * boot DEX gets updated, and for general "why aren't my
+             * changes doing anything" purposes its best if we just make
+             * everything crash when a DEX they're using gets updated.
+             */
+            LOGD("Stale deps in cache file; removing and retrying\n");
+            if (ftruncate(fd, 0) != 0) {
+                LOGW("Warning: unable to truncate cache file '%s': %s\n",
+                    cacheFileName, strerror(errno));
+                /* keep going */
+            }
+            if (unlink(cacheFileName) != 0) {
+                LOGW("Warning: unable to remove cache file '%s': %d %s\n",
+                    cacheFileName, errno, strerror(errno));
+                /* keep going; permission failure should probably be fatal */
+            }
+            LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
+            flock(fd, LOCK_UN);
+            close(fd);
+            goto retry;
+        } else {
+            LOGV("DexOpt: good deps in cache file\n");
+        }
+    }
+
+    assert(fd >= 0);
+    return fd;
+
+close_fail:
+    flock(fd, LOCK_UN);
+    close(fd);
+    return -1;
+}
+
+/*
+ * Unlock the file descriptor.
+ *
+ * Returns "true" on success.
+ */
+bool dvmUnlockCachedDexFile(int fd)
+{
+    LOGVV("DexOpt: unlocking cache file fd=%d\n", fd);
+    return (flock(fd, LOCK_UN) == 0);
+}
+
+
+/*
+ * Given a descriptor for a file with DEX data in it, produce an
+ * optimized version.
+ *
+ * The file pointed to by "fd" is expected to be a locked shared resource
+ * (or private); we make no efforts to enforce multi-process correctness
+ * here.
+ *
+ * "fileName" is only used for debug output.  "modWhen" and "crc" are stored
+ * in the dependency set.
+ *
+ * The "isBootstrap" flag determines how the optimizer and verifier handle
+ * package-scope access checks.  When optimizing, we only load the bootstrap
+ * class DEX files and the target DEX, so the flag determines whether the
+ * target DEX classes are given a (synthetic) non-NULL classLoader pointer.
+ * This only really matters if the target DEX contains classes that claim to
+ * be in the same package as bootstrap classes.
+ *
+ * The optimizer will need to load every class in the target DEX file.
+ * This is generally undesirable, so we start a subprocess to do the
+ * work and wait for it to complete.
+ *
+ * Returns "true" on success.  All data will have been written to "fd".
+ */
+bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
+    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
+{
+    const char* lastPart = strrchr(fileName, '/');
+    if (lastPart != NULL)
+        lastPart++;
+    else
+        lastPart = fileName;
+
+    /*
+     * For basic optimizations (byte-swapping and structure aligning) we
+     * don't need to fork().  It looks like fork+exec is causing problems
+     * with gdb on our bewildered Linux distro, so in some situations we
+     * want to avoid this.
+     *
+     * For optimization and/or verification, we need to load all the classes.
+     *
+     * We don't check gDvm.generateRegisterMaps, since that is dependent
+     * upon the verifier state.
+     */
+    if (gDvm.classVerifyMode == VERIFY_MODE_NONE &&
+        (gDvm.dexOptMode == OPTIMIZE_MODE_NONE ||
+         gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED))
+    {
+        LOGD("DexOpt: --- BEGIN (quick) '%s' ---\n", lastPart);
+        return dvmContinueOptimization(fd, dexOffset, dexLength,
+                fileName, modWhen, crc, isBootstrap);
+    }
+
+
+    LOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---\n", lastPart, isBootstrap);
+
+    pid_t pid;
+
+    /*
+     * This could happen if something in our bootclasspath, which we thought
+     * was all optimized, got rejected.
+     */
+    if (gDvm.optimizing) {
+        LOGW("Rejecting recursive optimization attempt on '%s'\n", fileName);
+        return false;
+    }
+
+    pid = fork();
+    if (pid == 0) {
+        static const int kUseValgrind = 0;
+        static const char* kDexOptBin = "/bin/dexopt";
+        static const char* kValgrinder = "/usr/bin/valgrind";
+        static const int kFixedArgCount = 10;
+        static const int kValgrindArgCount = 5;
+        static const int kMaxIntLen = 12;   // '-'+10dig+'\0' -OR- 0x+8dig
+        int bcpSize = dvmGetBootPathSize();
+        int argc = kFixedArgCount + bcpSize
+            + (kValgrindArgCount * kUseValgrind);
+        char* argv[argc+1];             // last entry is NULL
+        char values[argc][kMaxIntLen];
+        char* execFile;
+        char* androidRoot;
+        int flags;
+
+        /* change process groups, so we don't clash with ProcessManager */
+        setpgid(0, 0);
+
+        /* full path to optimizer */
+        androidRoot = getenv("ANDROID_ROOT");
+        if (androidRoot == NULL) {
+            LOGW("ANDROID_ROOT not set, defaulting to /system\n");
+            androidRoot = "/system";
+        }
+        execFile = malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
+        strcpy(execFile, androidRoot);
+        strcat(execFile, kDexOptBin);
+
+        /*
+         * Create arg vector.
+         */
+        int curArg = 0;
+
+        if (kUseValgrind) {
+            /* probably shouldn't ship the hard-coded path */
+            argv[curArg++] = (char*)kValgrinder;
+            argv[curArg++] = "--tool=memcheck";
+            argv[curArg++] = "--leak-check=yes";        // check for leaks too
+            argv[curArg++] = "--leak-resolution=med";   // increase from 2 to 4
+            argv[curArg++] = "--num-callers=16";        // default is 12
+            assert(curArg == kValgrindArgCount);
+        }
+        argv[curArg++] = execFile;
+
+        argv[curArg++] = "--dex";
+
+        sprintf(values[2], "%d", DALVIK_VM_BUILD);
+        argv[curArg++] = values[2];
+
+        sprintf(values[3], "%d", fd);
+        argv[curArg++] = values[3];
+
+        sprintf(values[4], "%d", (int) dexOffset);
+        argv[curArg++] = values[4];
+
+        sprintf(values[5], "%d", (int) dexLength);
+        argv[curArg++] = values[5];
+
+        argv[curArg++] = (char*)fileName;
+
+        sprintf(values[7], "%d", (int) modWhen);
+        argv[curArg++] = values[7];
+
+        sprintf(values[8], "%d", (int) crc);
+        argv[curArg++] = values[8];
+
+        flags = 0;
+        if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE) {
+            flags |= DEXOPT_OPT_ENABLED;
+            if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
+                flags |= DEXOPT_OPT_ALL;
+        }
+        if (gDvm.classVerifyMode != VERIFY_MODE_NONE) {
+            flags |= DEXOPT_VERIFY_ENABLED;
+            if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
+                flags |= DEXOPT_VERIFY_ALL;
+        }
+        if (isBootstrap)
+            flags |= DEXOPT_IS_BOOTSTRAP;
+        if (gDvm.generateRegisterMaps)
+            flags |= DEXOPT_GEN_REGISTER_MAP;
+        sprintf(values[9], "%d", flags);
+        argv[curArg++] = values[9];
+
+        assert(((!kUseValgrind && curArg == kFixedArgCount) ||
+               ((kUseValgrind && curArg == kFixedArgCount+kValgrindArgCount))));
+
+        ClassPathEntry* cpe;
+        for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+            argv[curArg++] = cpe->fileName;
+        }
+        assert(curArg == argc);
+
+        argv[curArg] = NULL;
+
+        if (kUseValgrind)
+            execv(kValgrinder, argv);
+        else
+            execv(execFile, argv);
+
+        LOGE("execv '%s'%s failed: %s\n", execFile,
+            kUseValgrind ? " [valgrind]" : "", strerror(errno));
+        exit(1);
+    } else {
+        LOGV("DexOpt: waiting for verify+opt, pid=%d\n", (int) pid);
+        int status;
+        pid_t gotPid;
+        int oldStatus;
+
+        /*
+         * Wait for the optimization process to finish.  We go into VMWAIT
+         * mode here so GC suspension won't have to wait for us.
+         */
+        oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+        while (true) {
+            gotPid = waitpid(pid, &status, 0);
+            if (gotPid == -1 && errno == EINTR) {
+                LOGD("waitpid interrupted, retrying\n");
+            } else {
+                break;
+            }
+        }
+        dvmChangeStatus(NULL, oldStatus);
+        if (gotPid != pid) {
+            LOGE("waitpid failed: wanted %d, got %d: %s\n",
+                (int) pid, (int) gotPid, strerror(errno));
+            return false;
+        }
+
+        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+            LOGD("DexOpt: --- END '%s' (success) ---\n", lastPart);
+            return true;
+        } else {
+            LOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed\n",
+                lastPart, status);
+            return false;
+        }
+    }
+}
+
+/*
+ * Do the actual optimization.  This is called directly for "minimal"
+ * optimization, or from a newly-created process for "full" optimization.
+ *
+ * For best use of disk/memory, we want to extract once and perform
+ * optimizations in place.  If the file has to expand or contract
+ * to match local structure padding/alignment expectations, we want
+ * to do the rewrite as part of the extract, rather than extracting
+ * into a temp file and slurping it back out.  (The structure alignment
+ * is currently correct for all platforms, and this isn't expected to
+ * change, so we should be okay with having it already extracted.)
+ *
+ * Returns "true" on success.
+ */
+bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
+    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
+{
+    DexClassLookup* pClassLookup = NULL;
+    IndexMapSet* pIndexMapSet = NULL;
+    RegisterMapBuilder* pRegMapBuilder = NULL;
+    bool doVerify, doOpt;
+    u4 headerFlags = 0;
+
+    if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
+        doVerify = false;
+    else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
+        doVerify = !isBootstrap;
+    else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
+        doVerify = true;
+
+    if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
+        doOpt = false;
+    else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
+        doOpt = doVerify;
+    else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
+        doOpt = true;
+
+    LOGV("Continuing optimization (%s, isb=%d, vfy=%d, opt=%d)\n",
+        fileName, isBootstrap, doVerify, doOpt);
+
+    assert(dexOffset >= 0);
+
+    /* quick test so we don't blow up on empty file */
+    if (dexLength < (int) sizeof(DexHeader)) {
+        LOGE("too small to be DEX\n");
+        return false;
+    }
+    if (dexOffset < (int) sizeof(DexOptHeader)) {
+        LOGE("not enough room for opt header\n");
+        return false;
+    }
+
+    bool result = false;
+
+    /*
+     * Drop this into a global so we don't have to pass it around.  We could
+     * also add a field to DexFile, but since it only pertains to DEX
+     * creation that probably doesn't make sense.
+     */
+    gDvm.optimizingBootstrapClass = isBootstrap;
+
+    {
+        /*
+         * Map the entire file (so we don't have to worry about page
+         * alignment).  The expectation is that the output file contains
+         * our DEX data plus room for a small header.
+         */
+        bool success;
+        void* mapAddr;
+        mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,
+                    MAP_SHARED, fd, 0);
+        if (mapAddr == MAP_FAILED) {
+            LOGE("unable to mmap DEX cache: %s\n", strerror(errno));
+            goto bail;
+        }
+
+        /*
+         * Rewrite the file.  Byte reordering, structure realigning,
+         * class verification, and bytecode optimization are all performed
+         * here.
+         *
+         * In theory the file could change size and bits could shift around.
+         * In practice this would be annoying to deal with, so the file
+         * layout is designed so that it can always be rewritten in place.
+         *
+         * This sets "headerFlags" and creates the class lookup table as
+         * part of doing the processing.
+         */
+        success = dvmRewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
+                    doVerify, doOpt, &headerFlags, &pClassLookup);
+
+        if (success) {
+            DvmDex* pDvmDex = NULL;
+            u1* dexAddr = ((u1*) mapAddr) + dexOffset;
+
+            if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
+                LOGE("Unable to create DexFile\n");
+                success = false;
+            } else {
+                /*
+                 * If configured to do so, scan the instructions, looking
+                 * for ways to reduce the size of the resolved-constant table.
+                 * This is done post-optimization, across the instructions
+                 * in all methods in all classes (even the ones that failed
+                 * to load).
+                 */
+                pIndexMapSet = dvmRewriteConstants(pDvmDex);
+
+                /*
+                 * If configured to do so, generate a full set of register
+                 * maps for all verified classes.
+                 */
+                if (gDvm.generateRegisterMaps) {
+                    pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);
+                    if (pRegMapBuilder == NULL) {
+                        LOGE("Failed generating register maps\n");
+                        success = false;
+                    }
+                }
+
+                DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;
+                updateChecksum(dexAddr, dexLength, pHeader);
+
+                dvmDexFileFree(pDvmDex);
+            }
+        }
+
+        /* unmap the read-write version, forcing writes to disk */
+        if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) {
+            LOGW("msync failed: %s\n", strerror(errno));
+            // weird, but keep going
+        }
+#if 1
+        /*
+         * This causes clean shutdown to fail, because we have loaded classes
+         * that point into it.  For the optimizer this isn't a problem,
+         * because it's more efficient for the process to simply exit.
+         * Exclude this code when doing clean shutdown for valgrind.
+         */
+        if (munmap(mapAddr, dexOffset + dexLength) != 0) {
+            LOGE("munmap failed: %s\n", strerror(errno));
+            goto bail;
+        }
+#endif
+
+        if (!success)
+            goto bail;
+    }
+
+    /* get start offset, and adjust deps start for 64-bit alignment */
+    off_t depsOffset, auxOffset, endOffset, adjOffset;
+    int depsLength, auxLength;
+    u4 optChecksum;
+
+    depsOffset = lseek(fd, 0, SEEK_END);
+    if (depsOffset < 0) {
+        LOGE("lseek to EOF failed: %s\n", strerror(errno));
+        goto bail;
+    }
+    adjOffset = (depsOffset + 7) & ~(0x07);
+    if (adjOffset != depsOffset) {
+        LOGV("Adjusting deps start from %d to %d\n",
+            (int) depsOffset, (int) adjOffset);
+        depsOffset = adjOffset;
+        lseek(fd, depsOffset, SEEK_SET);
+    }
+
+    /*
+     * Append the dependency list.
+     */
+    if (writeDependencies(fd, modWhen, crc) != 0) {
+        LOGW("Failed writing dependencies\n");
+        goto bail;
+    }
+
+    /* compute deps length, then adjust aux start for 64-bit alignment */
+    auxOffset = lseek(fd, 0, SEEK_END);
+    depsLength = auxOffset - depsOffset;
+
+    adjOffset = (auxOffset + 7) & ~(0x07);
+    if (adjOffset != auxOffset) {
+        LOGV("Adjusting aux start from %d to %d\n",
+            (int) auxOffset, (int) adjOffset);
+        auxOffset = adjOffset;
+        lseek(fd, auxOffset, SEEK_SET);
+    }
+
+    /*
+     * Append any auxillary pre-computed data structures.
+     */
+    if (!writeAuxData(fd, pClassLookup, pIndexMapSet, pRegMapBuilder)) {
+        LOGW("Failed writing aux data\n");
+        goto bail;
+    }
+
+    endOffset = lseek(fd, 0, SEEK_END);
+    auxLength = endOffset - auxOffset;
+
+    /* compute checksum from start of deps to end of aux area */
+    if (!computeFileChecksum(fd, depsOffset,
+            (auxOffset+auxLength) - depsOffset, &optChecksum))
+    {
+        goto bail;
+    }
+
+    /*
+     * Output the "opt" header with all values filled in and a correct
+     * magic number.
+     */
+    DexOptHeader optHdr;
+    memset(&optHdr, 0xff, sizeof(optHdr));
+    memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);
+    memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);
+    optHdr.dexOffset = (u4) dexOffset;
+    optHdr.dexLength = (u4) dexLength;
+    optHdr.depsOffset = (u4) depsOffset;
+    optHdr.depsLength = (u4) depsLength;
+    optHdr.auxOffset = (u4) auxOffset;
+    optHdr.auxLength = (u4) auxLength;
+
+    optHdr.flags = headerFlags;
+    optHdr.checksum = optChecksum;
+
+    ssize_t actual;
+    lseek(fd, 0, SEEK_SET);
+    actual = write(fd, &optHdr, sizeof(optHdr));
+    if (actual != sizeof(optHdr)) {
+        logFailedWrite(sizeof(optHdr), actual, "opt header", errno);
+        goto bail;
+    }
+
+    LOGV("Successfully wrote DEX header\n");
+    result = true;
+
+    //dvmRegisterMapDumpStats();
+
+bail:
+    dvmFreeIndexMapSet(pIndexMapSet);
+    dvmFreeRegisterMapBuilder(pRegMapBuilder);
+    free(pClassLookup);
+    return result;
+}
+
+
+/*
+ * Get the cache file name from a ClassPathEntry.
+ */
+static const char* getCacheFileName(const ClassPathEntry* cpe)
+{
+    switch (cpe->kind) {
+    case kCpeJar:
+        return dvmGetJarFileCacheFileName((JarFile*) cpe->ptr);
+    case kCpeDex:
+        return dvmGetRawDexFileCacheFileName((RawDexFile*) cpe->ptr);
+    default:
+        LOGE("DexOpt: unexpected cpe kind %d\n", cpe->kind);
+        dvmAbort();
+        return NULL;
+    }
+}
+
+/*
+ * Get the SHA-1 signature.
+ */
+static const u1* getSignature(const ClassPathEntry* cpe)
+{
+    DvmDex* pDvmDex;
+
+    switch (cpe->kind) {
+    case kCpeJar:
+        pDvmDex = dvmGetJarFileDex((JarFile*) cpe->ptr);
+        break;
+    case kCpeDex:
+        pDvmDex = dvmGetRawDexFileDex((RawDexFile*) cpe->ptr);
+        break;
+    default:
+        LOGE("unexpected cpe kind %d\n", cpe->kind);
+        dvmAbort();
+        pDvmDex = NULL;         // make gcc happy
+    }
+
+    assert(pDvmDex != NULL);
+    return pDvmDex->pDexFile->pHeader->signature;
+}
+
+
+/*
+ * Dependency layout:
+ *  4b  Source file modification time, in seconds since 1970 UTC
+ *  4b  CRC-32 from Zip entry, or Adler32 from source DEX header
+ *  4b  Dalvik VM build number
+ *  4b  Number of dependency entries that follow
+ *  Dependency entries:
+ *    4b  Name length (including terminating null)
+ *    var Full path of cache entry (null terminated)
+ *    20b SHA-1 signature from source DEX file
+ *
+ * If this changes, update DEX_OPT_MAGIC_VERS.
+ */
+static const size_t kMinDepSize = 4 * 4;
+static const size_t kMaxDepSize = 4 * 4 + 1024;     // sanity check
+
+/*
+ * Read the "opt" header, verify it, then read the dependencies section
+ * and verify that data as well.
+ *
+ * If "sourceAvail" is "true", this will verify that "modWhen" and "crc"
+ * match up with what is stored in the header.  If they don't, we reject
+ * the file so that it can be recreated from the updated original.  If
+ * "sourceAvail" isn't set, e.g. for a .odex file, we ignore these arguments.
+ *
+ * On successful return, the file will be seeked immediately past the
+ * "opt" header.
+ */
+bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
+    u4 crc, bool expectVerify, bool expectOpt)
+{
+    DexOptHeader optHdr;
+    u1* depData = NULL;
+    const u1* magic;
+    off_t posn;
+    int result = false;
+    ssize_t actual;
+
+    /*
+     * Start at the start.  The "opt" header, when present, will always be
+     * the first thing in the file.
+     */
+    if (lseek(fd, 0, SEEK_SET) != 0) {
+        LOGE("DexOpt: failed to seek to start of file: %s\n", strerror(errno));
+        goto bail;
+    }
+
+    /*
+     * Read and do trivial verification on the opt header.  The header is
+     * always in host byte order.
+     */
+    if (read(fd, &optHdr, sizeof(optHdr)) != sizeof(optHdr)) {
+        LOGE("DexOpt: failed reading opt header: %s\n", strerror(errno));
+        goto bail;
+    }
+
+    magic = optHdr.magic;
+    if (memcmp(magic, DEX_OPT_MAGIC, 4) != 0) {
+        /* not a DEX file, or previous attempt was interrupted */
+        LOGD("DexOpt: incorrect opt magic number (0x%02x %02x %02x %02x)\n",
+            magic[0], magic[1], magic[2], magic[3]);
+        goto bail;
+    }
+    if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
+        LOGW("DexOpt: stale opt version (0x%02x %02x %02x %02x)\n",
+            magic[4], magic[5], magic[6], magic[7]);
+        goto bail;
+    }
+    if (optHdr.depsLength < kMinDepSize || optHdr.depsLength > kMaxDepSize) {
+        LOGW("DexOpt: weird deps length %d, bailing\n", optHdr.depsLength);
+        goto bail;
+    }
+
+    /*
+     * Do the header flags match up with what we want?
+     *
+     * This is useful because it allows us to automatically regenerate
+     * a file when settings change (e.g. verification is now mandatory),
+     * but can cause difficulties if the bootstrap classes we depend upon
+     * were handled differently than the current options specify.  We get
+     * upset because they're not verified or optimized, but we're not able
+     * to regenerate them because the installer won't let us.
+     *
+     * (This is also of limited value when !sourceAvail.)
+     *
+     * So, for now, we essentially ignore "expectVerify" and "expectOpt"
+     * by limiting the match mask.
+     *
+     * The only thing we really can't handle is incorrect byte-ordering.
+     */
+    const u4 matchMask = DEX_OPT_FLAG_BIG;
+    u4 expectedFlags = 0;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+    expectedFlags |= DEX_OPT_FLAG_BIG;
+#endif
+    if (expectVerify)
+        expectedFlags |= DEX_FLAG_VERIFIED;
+    if (expectOpt)
+        expectedFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
+    if ((expectedFlags & matchMask) != (optHdr.flags & matchMask)) {
+        LOGI("DexOpt: header flag mismatch (0x%02x vs 0x%02x, mask=0x%02x)\n",
+            expectedFlags, optHdr.flags, matchMask);
+        goto bail;
+    }
+
+    posn = lseek(fd, optHdr.depsOffset, SEEK_SET);
+    if (posn < 0) {
+        LOGW("DexOpt: seek to deps failed: %s\n", strerror(errno));
+        goto bail;
+    }
+
+    /*
+     * Read all of the dependency stuff into memory.
+     */
+    depData = (u1*) malloc(optHdr.depsLength);
+    if (depData == NULL) {
+        LOGW("DexOpt: unable to allocate %d bytes for deps\n",
+            optHdr.depsLength);
+        goto bail;
+    }
+    actual = read(fd, depData, optHdr.depsLength);
+    if (actual != (ssize_t) optHdr.depsLength) {
+        LOGW("DexOpt: failed reading deps: %d of %d (err=%s)\n",
+            (int) actual, optHdr.depsLength, strerror(errno));
+        goto bail;
+    }
+
+    /*
+     * Verify simple items.
+     */
+    const u1* ptr;
+    u4 val;
+
+    ptr = depData;
+    val = read4LE(&ptr);
+    if (sourceAvail && val != modWhen) {
+        LOGI("DexOpt: source file mod time mismatch (%08x vs %08x)\n",
+            val, modWhen);
+        goto bail;
+    }
+    val = read4LE(&ptr);
+    if (sourceAvail && val != crc) {
+        LOGI("DexOpt: source file CRC mismatch (%08x vs %08x)\n", val, crc);
+        goto bail;
+    }
+    val = read4LE(&ptr);
+    if (val != DALVIK_VM_BUILD) {
+        LOGD("DexOpt: VM build version mismatch (%d vs %d)\n",
+            val, DALVIK_VM_BUILD);
+        goto bail;
+    }
+
+    /*
+     * Verify dependencies on other cached DEX files.  It must match
+     * exactly with what is currently defined in the bootclasspath.
+     */
+    ClassPathEntry* cpe;
+    u4 numDeps;
+
+    numDeps = read4LE(&ptr);
+    LOGV("+++ DexOpt: numDeps = %d\n", numDeps);
+    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+        const char* cacheFileName = getCacheFileName(cpe);
+        const u1* signature = getSignature(cpe);
+        size_t len = strlen(cacheFileName) +1;
+        u4 storedStrLen;
+
+        if (numDeps == 0) {
+            /* more entries in bootclasspath than in deps list */
+            LOGI("DexOpt: not all deps represented\n");
+            goto bail;
+        }
+
+        storedStrLen = read4LE(&ptr);
+        if (len != storedStrLen ||
+            strcmp(cacheFileName, (const char*) ptr) != 0)
+        {
+            LOGI("DexOpt: mismatch dep name: '%s' vs. '%s'\n",
+                cacheFileName, ptr);
+            goto bail;
+        }
+
+        ptr += storedStrLen;
+
+        if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
+            LOGI("DexOpt: mismatch dep signature for '%s'\n", cacheFileName);
+            goto bail;
+        }
+        ptr += kSHA1DigestLen;
+
+        LOGV("DexOpt: dep match on '%s'\n", cacheFileName);
+
+        numDeps--;
+    }
+
+    if (numDeps != 0) {
+        /* more entries in deps list than in classpath */
+        LOGI("DexOpt: Some deps went away\n");
+        goto bail;
+    }
+
+    // consumed all data and no more?
+    if (ptr != depData + optHdr.depsLength) {
+        LOGW("DexOpt: Spurious dep data? %d vs %d\n",
+            (int) (ptr - depData), optHdr.depsLength);
+        assert(false);
+    }
+
+    result = true;
+
+bail:
+    free(depData);
+    return result;
+}
+
+/*
+ * Write the dependency info to "fd" at the current file position.
+ */
+static int writeDependencies(int fd, u4 modWhen, u4 crc)
+{
+    u1* buf = NULL;
+    ssize_t actual;
+    int result = -1;
+    ssize_t bufLen;
+    ClassPathEntry* cpe;
+    int i, numDeps;
+
+    /*
+     * Count up the number of completed entries in the bootclasspath.
+     */
+    numDeps = 0;
+    bufLen = 0;
+    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+        const char* cacheFileName = getCacheFileName(cpe);
+        LOGV("+++ DexOpt: found dep '%s'\n", cacheFileName);
+
+        numDeps++;
+        bufLen += strlen(cacheFileName) +1;
+    }
+
+    bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
+
+    buf = malloc(bufLen);
+
+    set4LE(buf+0, modWhen);
+    set4LE(buf+4, crc);
+    set4LE(buf+8, DALVIK_VM_BUILD);
+    set4LE(buf+12, numDeps);
+
+    // TODO: do we want to add dvmGetInlineOpsTableLength() here?  Won't
+    // help us if somebody replaces an existing entry, but it'd catch
+    // additions/removals.
+
+    u1* ptr = buf + 4*4;
+    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+        const char* cacheFileName = getCacheFileName(cpe);
+        const u1* signature = getSignature(cpe);
+        int len = strlen(cacheFileName) +1;
+
+        if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
+            LOGE("DexOpt: overran buffer\n");
+            dvmAbort();
+        }
+
+        set4LE(ptr, len);
+        ptr += 4;
+        memcpy(ptr, cacheFileName, len);
+        ptr += len;
+        memcpy(ptr, signature, kSHA1DigestLen);
+        ptr += kSHA1DigestLen;
+    }
+
+    assert(ptr == buf + bufLen);
+
+    actual = write(fd, buf, bufLen);
+    if (actual != bufLen) {
+        result = (errno != 0) ? errno : -1;
+        logFailedWrite(bufLen, actual, "dep info", errno);
+    } else {
+        result = 0;
+    }
+
+    free(buf);
+    return result;
+}
+
+
+/*
+ * Write a block of data in "chunk" format.
+ *
+ * The chunk header fields are always in "native" byte order.  If "size"
+ * is not a multiple of 8 bytes, the data area is padded out.
+ */
+static bool writeChunk(int fd, u4 type, const void* data, size_t size)
+{
+    ssize_t actual;
+    union {             /* save a syscall by grouping these together */
+        char raw[8];
+        struct {
+            u4 type;
+            u4 size;
+        } ts;
+    } header;
+
+    assert(sizeof(header) == 8);
+
+    LOGV("Writing chunk, type=%.4s size=%d\n", (char*) &type, size);
+
+    header.ts.type = type;
+    header.ts.size = (u4) size;
+    actual = write(fd, &header, sizeof(header));
+    if (actual != sizeof(header)) {
+        logFailedWrite(size, actual, "aux chunk header write", errno);
+        return false;
+    }
+
+    if (size > 0) {
+        actual = write(fd, data, size);
+        if (actual != (ssize_t) size) {
+            logFailedWrite(size, actual, "aux chunk write", errno);
+            return false;
+        }
+    }
+
+    /* if necessary, pad to 64-bit alignment */
+    if ((size & 7) != 0) {
+        int padSize = 8 - (size & 7);
+        LOGV("size was %d, inserting %d pad bytes\n", size, padSize);
+        lseek(fd, padSize, SEEK_CUR);
+    }
+
+    assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
+
+    return true;
+}
+
+/*
+ * Write aux data.
+ *
+ * We have different pieces, some of which may be optional.  To make the
+ * most effective use of space, we use a "chunk" format, with a 4-byte
+ * type and a 4-byte length.  We guarantee 64-bit alignment for the data,
+ * so it can be used directly when the file is mapped for reading.
+ */
+static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,
+    const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder)
+{
+    /* pre-computed class lookup hash table */
+    if (!writeChunk(fd, (u4) kDexChunkClassLookup,
+            pClassLookup, pClassLookup->size))
+    {
+        return false;
+    }
+
+    /* remapped constants (optional) */
+    if (pIndexMapSet != NULL) {
+        if (!writeChunk(fd, pIndexMapSet->chunkType,
+                pIndexMapSet->chunkData, pIndexMapSet->chunkDataLen))
+        {
+            return false;
+        }
+    }
+
+    /* register maps (optional) */
+    if (pRegMapBuilder != NULL) {
+        if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
+                pRegMapBuilder->data, pRegMapBuilder->size))
+        {
+            return false;
+        }
+    }
+
+    /* write the end marker */
+    if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Log a failed write.
+ */
+static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
+    int err)
+{
+    LOGE("Write failed: %s (%d of %d): %s\n",
+        msg, (int)actual, (int)expected, strerror(err));
+}
+
+/*
+ * Compute a checksum on a piece of an open file.
+ *
+ * File will be positioned at end of checksummed area.
+ *
+ * Returns "true" on success.
+ */
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum)
+{
+    unsigned char readBuf[8192];
+    ssize_t actual;
+    uLong adler;
+
+    if (lseek(fd, start, SEEK_SET) != start) {
+        LOGE("Unable to seek to start of checksum area (%ld): %s\n",
+            (long) start, strerror(errno));
+        return false;
+    }
+
+    adler = adler32(0L, Z_NULL, 0);
+
+    while (length != 0) {
+        size_t wanted = (length < sizeof(readBuf)) ? length : sizeof(readBuf);
+        actual = read(fd, readBuf, wanted);
+        if (actual <= 0) {
+            LOGE("Read failed (%d) while computing checksum (len=%zu): %s\n",
+                (int) actual, length, strerror(errno));
+            return false;
+        }
+
+        adler = adler32(adler, readBuf, actual);
+
+        length -= actual;
+    }
+
+    *pSum = adler;
+    return true;
+}
+
+/*
+ * Update the Adler-32 checksum stored in the DEX file.  This covers the
+ * swapped and optimized DEX data, but does not include the opt header
+ * or auxillary data.
+ */
+static void updateChecksum(u1* addr, int len, DexHeader* pHeader)
+{
+    /*
+     * Rewrite the checksum.  We leave the SHA-1 signature alone.
+     */
+    uLong adler = adler32(0L, Z_NULL, 0);
+    const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+
+    adler = adler32(adler, addr + nonSum, len - nonSum);
+    pHeader->checksum = adler;
+}
+
diff --git a/vm/analysis/DexOptimize.h b/vm/analysis/DexPrepare.h
similarity index 84%
rename from vm/analysis/DexOptimize.h
rename to vm/analysis/DexPrepare.h
index afcb3cb..ae94979 100644
--- a/vm/analysis/DexOptimize.h
+++ b/vm/analysis/DexPrepare.h
@@ -15,10 +15,10 @@
  */
 
 /*
- * DEX optimization declarations.
+ * DEX preparation declarations.
  */
-#ifndef _DALVIK_DEXOPTIMIZE
-#define _DALVIK_DEXOPTIMIZE
+#ifndef _DALVIK_DEXPREPARE
+#define _DALVIK_DEXPREPARE
 
 /*
  * Global DEX optimizer control.  Determines the circumstances in which we
@@ -112,18 +112,4 @@
 bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
     const char* fileName, u4 modWhen, u4 crc, bool isBootstrap);
 
-/*
- * Abbreviated resolution functions, for use by optimization and verification
- * code.
- */
-ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
-    VerifyError* pFailure);
-Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
-    MethodType methodType, VerifyError* pFailure);
-Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx);
-InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
-    VerifyError* pFailure);
-StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
-    VerifyError* pFailure);
-
-#endif /*_DALVIK_DEXOPTIMIZE*/
+#endif /*_DALVIK_DEXPREPARE*/
diff --git a/vm/analysis/Optimize.c b/vm/analysis/Optimize.c
new file mode 100644
index 0000000..7121229
--- /dev/null
+++ b/vm/analysis/Optimize.c
@@ -0,0 +1,1199 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Perform some simple bytecode optimizations, chiefly "quickening" of
+ * opcodes.
+ */
+#include "Dalvik.h"
+#include "libdex/InstrUtils.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+
+/*
+ * Virtual/direct calls to "method" are replaced with an execute-inline
+ * instruction with index "idx".
+ */
+typedef struct InlineSub {
+    Method* method;
+    int     inlineIdx;
+} InlineSub;
+
+
+/* fwd */
+static bool loadAllClasses(DvmDex* pDvmDex);
+static void optimizeLoadedClasses(DexFile* pDexFile);
+static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs);
+static bool optimizeMethod(Method* method, const InlineSub* inlineSubs);
+static void rewriteInstField(Method* method, u2* insns, OpCode newOpc);
+static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc);
+static bool rewriteEmptyDirectInvoke(Method* method, u2* insns);
+static bool rewriteExecuteInline(Method* method, u2* insns,
+    MethodType methodType, const InlineSub* inlineSubs);
+static bool rewriteExecuteInlineRange(Method* method, u2* insns,
+    MethodType methodType, const InlineSub* inlineSubs);
+
+
+/*
+ * Perform in-place rewrites on a memory-mapped DEX file.
+ *
+ * This happens in a short-lived child process, so we can go nutty with
+ * loading classes and allocating memory.
+ */
+bool dvmRewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
+    u4* pHeaderFlags, DexClassLookup** ppClassLookup)
+{
+    u8 prepWhen, loadWhen, verifyWhen, optWhen;
+    DvmDex* pDvmDex = NULL;
+    bool result = false;
+
+    *pHeaderFlags = 0;
+
+    LOGV("+++ swapping bytes\n");
+    if (dexFixByteOrdering(addr, len) != 0)
+        goto bail;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+    *pHeaderFlags |= DEX_OPT_FLAG_BIG;
+#endif
+
+    /*
+     * Now that the DEX file can be read directly, create a DexFile for it.
+     */
+    if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
+        LOGE("Unable to create DexFile\n");
+        goto bail;
+    }
+
+    /*
+     * Create the class lookup table.
+     */
+    //startWhen = dvmGetRelativeTimeUsec();
+    *ppClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
+    if (*ppClassLookup == NULL)
+        goto bail;
+
+    /*
+     * Bail out early if they don't want The Works.  The current implementation
+     * doesn't fork a new process if this flag isn't set, so we really don't
+     * want to continue on with the crazy class loading.
+     */
+    if (!doVerify && !doOpt) {
+        result = true;
+        goto bail;
+    }
+
+    /* this is needed for the next part */
+    pDvmDex->pDexFile->pClassLookup = *ppClassLookup;
+
+    prepWhen = dvmGetRelativeTimeUsec();
+
+    /*
+     * Load all classes found in this DEX file.  If they fail to load for
+     * some reason, they won't get verified (which is as it should be).
+     */
+    if (!loadAllClasses(pDvmDex))
+        goto bail;
+    loadWhen = dvmGetRelativeTimeUsec();
+
+    /*
+     * Verify all classes in the DEX file.  Export the "is verified" flag
+     * to the DEX file we're creating.
+     */
+    if (doVerify) {
+        dvmVerifyAllClasses(pDvmDex->pDexFile);
+        *pHeaderFlags |= DEX_FLAG_VERIFIED;
+    }
+    verifyWhen = dvmGetRelativeTimeUsec();
+
+    /*
+     * Optimize the classes we successfully loaded.  If the opt mode is
+     * OPTIMIZE_MODE_VERIFIED, each class must have been successfully
+     * verified or we'll skip it.
+     */
+#ifndef PROFILE_FIELD_ACCESS
+    if (doOpt) {
+        optimizeLoadedClasses(pDvmDex->pDexFile);
+        *pHeaderFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
+    }
+#endif
+    optWhen = dvmGetRelativeTimeUsec();
+
+    LOGD("DexOpt: load %dms, verify %dms, opt %dms\n",
+        (int) (loadWhen - prepWhen) / 1000,
+        (int) (verifyWhen - loadWhen) / 1000,
+        (int) (optWhen - verifyWhen) / 1000);
+
+    result = true;
+
+bail:
+    /* free up storage */
+    dvmDexFileFree(pDvmDex);
+
+    return result;
+}
+
+/*
+ * Try to load all classes in the specified DEX.  If they have some sort
+ * of broken dependency, e.g. their superclass lives in a different DEX
+ * that wasn't previously loaded into the bootstrap class path, loading
+ * will fail.  This is the desired behavior.
+ *
+ * We have no notion of class loader at this point, so we load all of
+ * the classes with the bootstrap class loader.  It turns out this has
+ * exactly the behavior we want, and has no ill side effects because we're
+ * running in a separate process and anything we load here will be forgotten.
+ *
+ * We set the CLASS_MULTIPLE_DEFS flag here if we see multiple definitions.
+ * This works because we only call here as part of optimization / pre-verify,
+ * not during verification as part of loading a class into a running VM.
+ *
+ * This returns "false" if the world is too screwed up to do anything
+ * useful at all.
+ */
+static bool loadAllClasses(DvmDex* pDvmDex)
+{
+    u4 count = pDvmDex->pDexFile->pHeader->classDefsSize;
+    u4 idx;
+    int loaded = 0;
+
+    LOGV("DexOpt: +++ trying to load %d classes\n", count);
+
+    dvmSetBootPathExtraDex(pDvmDex);
+
+    /*
+     * We have some circularity issues with Class and Object that are most
+     * easily avoided by ensuring that Object is never the first thing we
+     * try to find.  Take care of that here.  (We only need to do this when
+     * loading classes from the DEX file that contains Object, and only
+     * when Object comes first in the list, but it costs very little to
+     * do it in all cases.)
+     */
+    if (dvmFindSystemClass("Ljava/lang/Class;") == NULL) {
+        LOGE("ERROR: java.lang.Class does not exist!\n");
+        return false;
+    }
+
+    for (idx = 0; idx < count; idx++) {
+        const DexClassDef* pClassDef;
+        const char* classDescriptor;
+        ClassObject* newClass;
+
+        pClassDef = dexGetClassDef(pDvmDex->pDexFile, idx);
+        classDescriptor =
+            dexStringByTypeIdx(pDvmDex->pDexFile, pClassDef->classIdx);
+
+        LOGV("+++  loading '%s'", classDescriptor);
+        //newClass = dvmDefineClass(pDexFile, classDescriptor,
+        //        NULL);
+        newClass = dvmFindSystemClassNoInit(classDescriptor);
+        if (newClass == NULL) {
+            LOGV("DexOpt: failed loading '%s'\n", classDescriptor);
+            dvmClearOptException(dvmThreadSelf());
+        } else if (newClass->pDvmDex != pDvmDex) {
+            /*
+             * We don't load the new one, and we tag the first one found
+             * with the "multiple def" flag so the resolver doesn't try
+             * to make it available.
+             */
+            LOGD("DexOpt: '%s' has an earlier definition; blocking out\n",
+                classDescriptor);
+            SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
+        } else {
+            loaded++;
+        }
+    }
+    LOGV("DexOpt: +++ successfully loaded %d classes\n", loaded);
+
+    dvmSetBootPathExtraDex(NULL);
+    return true;
+}
+
+
+/*
+ * Create a table of inline substitutions.
+ *
+ * TODO: this is currently just a linear array.  We will want to put this
+ * into a hash table as the list size increases.
+ */
+static InlineSub* createInlineSubsTable(void)
+{
+    const InlineOperation* ops = dvmGetInlineOpsTable();
+    const int count = dvmGetInlineOpsTableLength();
+    InlineSub* table;
+    Method* method;
+    ClassObject* clazz;
+    int i, tableIndex;
+
+    /*
+     * Allocate for optimism: one slot per entry, plus an end-of-list marker.
+     */
+    table = malloc(sizeof(InlineSub) * (count+1));
+
+    tableIndex = 0;
+    for (i = 0; i < count; i++) {
+        clazz = dvmFindClassNoInit(ops[i].classDescriptor, NULL);
+        if (clazz == NULL) {
+            LOGV("DexOpt: can't inline for class '%s': not found\n",
+                ops[i].classDescriptor);
+            dvmClearOptException(dvmThreadSelf());
+        } else {
+            /*
+             * Method could be virtual or direct.  Try both.  Don't use
+             * the "hier" versions.
+             */
+            method = dvmFindDirectMethodByDescriptor(clazz, ops[i].methodName,
+                        ops[i].methodSignature);
+            if (method == NULL)
+                method = dvmFindVirtualMethodByDescriptor(clazz, ops[i].methodName,
+                        ops[i].methodSignature);
+            if (method == NULL) {
+                LOGW("DexOpt: can't inline %s.%s %s: method not found\n",
+                    ops[i].classDescriptor, ops[i].methodName,
+                    ops[i].methodSignature);
+            } else {
+                if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
+                    LOGW("DexOpt: WARNING: inline op on non-final class/method "
+                         "%s.%s\n",
+                        clazz->descriptor, method->name);
+                    /* fail? */
+                }
+                if (dvmIsSynchronizedMethod(method) ||
+                    dvmIsDeclaredSynchronizedMethod(method))
+                {
+                    LOGW("DexOpt: WARNING: inline op on synchronized method "
+                         "%s.%s\n",
+                        clazz->descriptor, method->name);
+                    /* fail? */
+                }
+
+                table[tableIndex].method = method;
+                table[tableIndex].inlineIdx = i;
+                tableIndex++;
+
+                LOGV("DexOpt: will inline %d: %s.%s %s\n", i,
+                    ops[i].classDescriptor, ops[i].methodName,
+                    ops[i].methodSignature);
+            }
+        }
+    }
+
+    /* mark end of table */
+    table[tableIndex].method = NULL;
+    LOGV("DexOpt: inline table has %d entries\n", tableIndex);
+
+    return table;
+}
+
+/*
+ * Run through all classes that were successfully loaded from this DEX
+ * file and optimize their code sections.
+ */
+static void optimizeLoadedClasses(DexFile* pDexFile)
+{
+    u4 count = pDexFile->pHeader->classDefsSize;
+    u4 idx;
+    InlineSub* inlineSubs = NULL;
+
+    LOGV("DexOpt: +++ optimizing up to %d classes\n", count);
+    assert(gDvm.dexOptMode != OPTIMIZE_MODE_NONE);
+
+    inlineSubs = createInlineSubsTable();
+
+    for (idx = 0; idx < count; idx++) {
+        const DexClassDef* pClassDef;
+        const char* classDescriptor;
+        ClassObject* clazz;
+
+        pClassDef = dexGetClassDef(pDexFile, idx);
+        classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+        /* all classes are loaded into the bootstrap class loader */
+        clazz = dvmLookupClass(classDescriptor, NULL, false);
+        if (clazz != NULL) {
+            if ((pClassDef->accessFlags & CLASS_ISPREVERIFIED) == 0 &&
+                gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
+            {
+                LOGV("DexOpt: not optimizing '%s': not verified\n",
+                    classDescriptor);
+            } else if (clazz->pDvmDex->pDexFile != pDexFile) {
+                /* shouldn't be here -- verifier should have caught */
+                LOGD("DexOpt: not optimizing '%s': multiple definitions\n",
+                    classDescriptor);
+            } else {
+                optimizeClass(clazz, inlineSubs);
+
+                /* set the flag whether or not we actually did anything */
+                ((DexClassDef*)pClassDef)->accessFlags |=
+                    CLASS_ISOPTIMIZED;
+            }
+        } else {
+            LOGV("DexOpt: not optimizing unavailable class '%s'\n",
+                classDescriptor);
+        }
+    }
+
+    free(inlineSubs);
+}
+
+/*
+ * Optimize the specified class.
+ */
+static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs)
+{
+    int i;
+
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        if (!optimizeMethod(&clazz->directMethods[i], inlineSubs))
+            goto fail;
+    }
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        if (!optimizeMethod(&clazz->virtualMethods[i], inlineSubs))
+            goto fail;
+    }
+
+    return;
+
+fail:
+    LOGV("DexOpt: ceasing optimization attempts on %s\n", clazz->descriptor);
+}
+
+/*
+ * Optimize instructions in a method.
+ *
+ * Returns "true" if all went well, "false" if we bailed out early when
+ * something failed.
+ */
+static bool optimizeMethod(Method* method, const InlineSub* inlineSubs)
+{
+    u4 insnsSize;
+    u2* insns;
+    u2 inst;
+
+    if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
+        return true;
+
+    insns = (u2*) method->insns;
+    assert(insns != NULL);
+    insnsSize = dvmGetMethodInsnsSize(method);
+
+    while (insnsSize > 0) {
+        int width;
+
+        inst = *insns & 0xff;
+
+        switch (inst) {
+        case OP_IGET:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+            rewriteInstField(method, insns, OP_IGET_QUICK);
+            break;
+        case OP_IGET_WIDE:
+            rewriteInstField(method, insns, OP_IGET_WIDE_QUICK);
+            break;
+        case OP_IGET_OBJECT:
+            rewriteInstField(method, insns, OP_IGET_OBJECT_QUICK);
+            break;
+        case OP_IPUT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT:
+            rewriteInstField(method, insns, OP_IPUT_QUICK);
+            break;
+        case OP_IPUT_WIDE:
+            rewriteInstField(method, insns, OP_IPUT_WIDE_QUICK);
+            break;
+        case OP_IPUT_OBJECT:
+            rewriteInstField(method, insns, OP_IPUT_OBJECT_QUICK);
+            break;
+
+        case OP_INVOKE_VIRTUAL:
+            if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL,inlineSubs))
+            {
+                if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_VIRTUAL_QUICK))
+                    return false;
+            }
+            break;
+        case OP_INVOKE_VIRTUAL_RANGE:
+            if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL,
+                    inlineSubs))
+            {
+                if (!rewriteVirtualInvoke(method, insns,
+                        OP_INVOKE_VIRTUAL_QUICK_RANGE))
+                {
+                    return false;
+                }
+            }
+            break;
+        case OP_INVOKE_SUPER:
+            if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK))
+                return false;
+            break;
+        case OP_INVOKE_SUPER_RANGE:
+            if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE))
+                return false;
+            break;
+
+        case OP_INVOKE_DIRECT:
+            if (!rewriteExecuteInline(method, insns, METHOD_DIRECT, inlineSubs))
+            {
+                if (!rewriteEmptyDirectInvoke(method, insns))
+                    return false;
+            }
+            break;
+        case OP_INVOKE_DIRECT_RANGE:
+            rewriteExecuteInlineRange(method, insns, METHOD_DIRECT, inlineSubs);
+            break;
+
+        case OP_INVOKE_STATIC:
+            rewriteExecuteInline(method, insns, METHOD_STATIC, inlineSubs);
+            break;
+        case OP_INVOKE_STATIC_RANGE:
+            rewriteExecuteInlineRange(method, insns, METHOD_STATIC, inlineSubs);
+            break;
+
+        default:
+            // ignore this instruction
+            ;
+        }
+
+        if (*insns == kPackedSwitchSignature) {
+            width = 4 + insns[1] * 2;
+        } else if (*insns == kSparseSwitchSignature) {
+            width = 2 + insns[1] * 4;
+        } else if (*insns == kArrayDataSignature) {
+            u2 elemWidth = insns[1];
+            u4 len = insns[2] | (((u4)insns[3]) << 16);
+            width = 4 + (elemWidth * len + 1) / 2;
+        } else {
+            width = dexGetInstrWidthAbs(gDvm.instrWidth, inst);
+        }
+        assert(width > 0);
+
+        insns += width;
+        insnsSize -= width;
+    }
+
+    assert(insnsSize == 0);
+    return true;
+}
+
+
+/*
+ * If "referrer" and "resClass" don't come from the same DEX file, and
+ * the DEX we're working on is not destined for the bootstrap class path,
+ * tweak the class loader so package-access checks work correctly.
+ *
+ * Only do this if we're doing pre-verification or optimization.
+ */
+static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
+{
+    if (!gDvm.optimizing)
+        return;
+    assert(referrer->classLoader == NULL);
+    assert(resClass->classLoader == NULL);
+
+    if (!gDvm.optimizingBootstrapClass) {
+        /* class loader for an array class comes from element type */
+        if (dvmIsArrayClass(resClass))
+            resClass = resClass->elementClass;
+        if (referrer->pDvmDex != resClass->pDvmDex)
+            resClass->classLoader = (Object*) 0xdead3333;
+    }
+}
+
+/*
+ * Undo the effects of tweakLoader.
+ */
+static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
+{
+    if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
+        return;
+
+    if (dvmIsArrayClass(resClass))
+        resClass = resClass->elementClass;
+    resClass->classLoader = NULL;
+}
+
+
+/*
+ * Alternate version of dvmResolveClass for use with verification and
+ * optimization.  Performs access checks on every resolve, and refuses
+ * to acknowledge the existence of classes defined in more than one DEX
+ * file.
+ *
+ * Exceptions caused by failures are cleared before returning.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
+    VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+
+    /*
+     * Check the table first.  If not there, do the lookup by name.
+     */
+    resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
+    if (resClass == NULL) {
+        const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
+        if (className[0] != '\0' && className[1] == '\0') {
+            /* primitive type */
+            resClass = dvmFindPrimitiveClass(className[0]);
+        } else {
+            resClass = dvmFindClassNoInit(className, referrer->classLoader);
+        }
+        if (resClass == NULL) {
+            /* not found, exception should be raised */
+            LOGV("DexOpt: class %d (%s) not found\n",
+                classIdx,
+                dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
+            if (pFailure != NULL) {
+                /* dig through the wrappers to find the original failure */
+                Object* excep = dvmGetException(dvmThreadSelf());
+                while (true) {
+                    Object* cause = dvmGetExceptionCause(excep);
+                    if (cause == NULL)
+                        break;
+                    excep = cause;
+                }
+                if (strcmp(excep->clazz->descriptor,
+                    "Ljava/lang/IncompatibleClassChangeError;") == 0)
+                {
+                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+                } else {
+                    *pFailure = VERIFY_ERROR_NO_CLASS;
+                }
+            }
+            dvmClearOptException(dvmThreadSelf());
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         */
+        dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
+    }
+
+    /* multiple definitions? */
+    if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
+        LOGI("DexOpt: not resolving ambiguous class '%s'\n",
+            resClass->descriptor);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_NO_CLASS;
+        return NULL;
+    }
+
+    /* access allowed? */
+    tweakLoader(referrer, resClass);
+    bool allowed = dvmCheckClassAccess(referrer, resClass);
+    untweakLoader(referrer, resClass);
+    if (!allowed) {
+        LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
+            referrer->descriptor, resClass->descriptor);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_CLASS;
+        return NULL;
+    }
+
+    return resClass;
+}
+
+/*
+ * Alternate version of dvmResolveInstField().
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
+    VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    InstField* resField;
+
+    resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
+    if (resField == NULL) {
+        const DexFieldId* pFieldId;
+        ClassObject* resClass;
+
+        pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
+
+        /*
+         * Find the field's class.
+         */
+        resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
+        if (resClass == NULL) {
+            //dvmClearOptException(dvmThreadSelf());
+            assert(!dvmCheckException(dvmThreadSelf()));
+            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+            return NULL;
+        }
+
+        resField = (InstField*)dvmFindFieldHier(resClass,
+            dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+            dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+        if (resField == NULL) {
+            LOGD("DexOpt: couldn't find field %s.%s\n",
+                resClass->descriptor,
+                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_FIELD;
+            return NULL;
+        }
+        if (dvmIsStaticField(&resField->field)) {
+            LOGD("DexOpt: wanted instance, got static for field %s.%s\n",
+                resClass->descriptor,
+                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         */
+        dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
+    }
+
+    /* access allowed? */
+    tweakLoader(referrer, resField->field.clazz);
+    bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+    untweakLoader(referrer, resField->field.clazz);
+    if (!allowed) {
+        LOGI("DexOpt: access denied from %s to field %s.%s\n",
+            referrer->descriptor, resField->field.clazz->descriptor,
+            resField->field.name);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+        return NULL;
+    }
+
+    return resField;
+}
+
+/*
+ * Alternate version of dvmResolveStaticField().
+ *
+ * Does not force initialization of the resolved field's class.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
+    VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    StaticField* resField;
+
+    resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
+    if (resField == NULL) {
+        const DexFieldId* pFieldId;
+        ClassObject* resClass;
+
+        pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
+
+        /*
+         * Find the field's class.
+         */
+        resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
+        if (resClass == NULL) {
+            //dvmClearOptException(dvmThreadSelf());
+            assert(!dvmCheckException(dvmThreadSelf()));
+            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+            return NULL;
+        }
+
+        resField = (StaticField*)dvmFindFieldHier(resClass,
+                    dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+                    dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+        if (resField == NULL) {
+            LOGD("DexOpt: couldn't find static field\n");
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_FIELD;
+            return NULL;
+        }
+        if (!dvmIsStaticField(&resField->field)) {
+            LOGD("DexOpt: wanted static, got instance for field %s.%s\n",
+                resClass->descriptor,
+                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         *
+         * We can only do this if we're in "dexopt", because the presence
+         * of a valid value in the resolution table implies that the class
+         * containing the static field has been initialized.
+         */
+        if (gDvm.optimizing)
+            dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
+    }
+
+    /* access allowed? */
+    tweakLoader(referrer, resField->field.clazz);
+    bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+    untweakLoader(referrer, resField->field.clazz);
+    if (!allowed) {
+        LOGI("DexOpt: access denied from %s to field %s.%s\n",
+            referrer->descriptor, resField->field.clazz->descriptor,
+            resField->field.name);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+        return NULL;
+    }
+
+    return resField;
+}
+
+
+/*
+ * Rewrite an iget/iput instruction.  These all have the form:
+ *   op vA, vB, field@CCCC
+ *
+ * Where vA holds the value, vB holds the object reference, and CCCC is
+ * the field reference constant pool offset.  We want to replace CCCC
+ * with the byte offset from the start of the object.
+ *
+ * "clazz" is the referring class.  We need this because we verify
+ * access rights here.
+ */
+static void rewriteInstField(Method* method, u2* insns, OpCode newOpc)
+{
+    ClassObject* clazz = method->clazz;
+    u2 fieldIdx = insns[1];
+    InstField* field;
+    int byteOffset;
+
+    field = dvmOptResolveInstField(clazz, fieldIdx, NULL);
+    if (field == NULL) {
+        LOGI("DexOpt: unable to optimize field ref 0x%04x at 0x%02x in %s.%s\n",
+            fieldIdx, (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return;
+    }
+
+    if (field->byteOffset >= 65536) {
+        LOGI("DexOpt: field offset exceeds 64K (%d)\n", field->byteOffset);
+        return;
+    }
+
+    insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
+    insns[1] = (u2) field->byteOffset;
+    LOGVV("DexOpt: rewrote access to %s.%s --> %d\n",
+        field->field.clazz->descriptor, field->field.name,
+        field->byteOffset);
+}
+
+/*
+ * Alternate version of dvmResolveMethod().
+ *
+ * Doesn't throw exceptions, and checks access on every lookup.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
+    MethodType methodType, VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    Method* resMethod;
+
+    assert(methodType == METHOD_DIRECT ||
+           methodType == METHOD_VIRTUAL ||
+           methodType == METHOD_STATIC);
+
+    LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx,
+        referrer->descriptor);
+
+    resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+    if (resMethod == NULL) {
+        const DexMethodId* pMethodId;
+        ClassObject* resClass;
+
+        pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+        resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
+        if (resClass == NULL) {
+            /*
+             * Can't find the class that the method is a part of, or don't
+             * have permission to access the class.
+             */
+            LOGV("DexOpt: can't find called method's class (?.%s)\n",
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
+            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+            return NULL;
+        }
+        if (dvmIsInterfaceClass(resClass)) {
+            /* method is part of an interface; this is wrong method for that */
+            LOGW("DexOpt: method is in an interface\n");
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_GENERIC;
+            return NULL;
+        }
+
+        /*
+         * We need to chase up the class hierarchy to find methods defined
+         * in super-classes.  (We only want to check the current class
+         * if we're looking for a constructor.)
+         */
+        DexProto proto;
+        dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+        if (methodType == METHOD_DIRECT) {
+            resMethod = dvmFindDirectMethod(resClass,
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
+        } else {
+            /* METHOD_STATIC or METHOD_VIRTUAL */
+            resMethod = dvmFindMethodHier(resClass,
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
+        }
+
+        if (resMethod == NULL) {
+            LOGV("DexOpt: couldn't find method '%s'\n",
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_METHOD;
+            return NULL;
+        }
+        if (methodType == METHOD_STATIC) {
+            if (!dvmIsStaticMethod(resMethod)) {
+                LOGD("DexOpt: wanted static, got instance for method %s.%s\n",
+                    resClass->descriptor, resMethod->name);
+                if (pFailure != NULL)
+                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+                return NULL;
+            }
+        } else if (methodType == METHOD_VIRTUAL) {
+            if (dvmIsStaticMethod(resMethod)) {
+                LOGD("DexOpt: wanted instance, got static for method %s.%s\n",
+                    resClass->descriptor, resMethod->name);
+                if (pFailure != NULL)
+                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+                return NULL;
+            }
+        }
+
+        /* see if this is a pure-abstract method */
+        if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
+            LOGW("DexOpt: pure-abstract method '%s' in %s\n",
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
+                resClass->descriptor);
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_GENERIC;
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         *
+         * We can only do this for static methods if we're not in "dexopt",
+         * because the presence of a valid value in the resolution table
+         * implies that the class containing the static field has been
+         * initialized.
+         */
+        if (methodType != METHOD_STATIC || gDvm.optimizing)
+            dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+    }
+
+    LOGVV("--- found method %d (%s.%s)\n",
+        methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+    /* access allowed? */
+    tweakLoader(referrer, resMethod->clazz);
+    bool allowed = dvmCheckMethodAccess(referrer, resMethod);
+    untweakLoader(referrer, resMethod->clazz);
+    if (!allowed) {
+        IF_LOGI() {
+            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+            LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
+                resMethod->clazz->descriptor, resMethod->name, desc,
+                referrer->descriptor);
+            free(desc);
+        }
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_METHOD;
+        return NULL;
+    }
+
+    return resMethod;
+}
+
+/*
+ * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
+ * invoke-super/range.  These all have the form:
+ *   op vAA, meth@BBBB, reg stuff @CCCC
+ *
+ * We want to replace the method constant pool index BBBB with the
+ * vtable index.
+ */
+static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc)
+{
+    ClassObject* clazz = method->clazz;
+    Method* baseMethod;
+    u2 methodIdx = insns[1];
+
+    baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
+    if (baseMethod == NULL) {
+        LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n",
+            methodIdx,
+            (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return false;
+    }
+
+    assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
+           (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
+           (insns[0] & 0xff) == OP_INVOKE_SUPER ||
+           (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
+
+    /*
+     * Note: Method->methodIndex is a u2 and is range checked during the
+     * initial load.
+     */
+    insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
+    insns[1] = baseMethod->methodIndex;
+
+    //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
+    //    method->clazz->descriptor, method->name,
+    //    baseMethod->clazz->descriptor, baseMethod->name);
+
+    return true;
+}
+
+/*
+ * Rewrite invoke-direct, which has the form:
+ *   op vAA, meth@BBBB, reg stuff @CCCC
+ *
+ * There isn't a lot we can do to make this faster, but in some situations
+ * we can make it go away entirely.
+ *
+ * This must only be used when the invoked method does nothing and has
+ * no return value (the latter being very important for verification).
+ */
+static bool rewriteEmptyDirectInvoke(Method* method, u2* insns)
+{
+    ClassObject* clazz = method->clazz;
+    Method* calledMethod;
+    u2 methodIdx = insns[1];
+
+    calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
+    if (calledMethod == NULL) {
+        LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n",
+            methodIdx,
+            (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return false;
+    }
+
+    /* TODO: verify that java.lang.Object() is actually empty! */
+    if (calledMethod->clazz == gDvm.classJavaLangObject &&
+        dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
+    {
+        /*
+         * Replace with "empty" instruction.  DO NOT disturb anything
+         * else about it, as we want it to function the same as
+         * OP_INVOKE_DIRECT when debugging is enabled.
+         */
+        assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
+        insns[0] = (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY;
+
+        //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
+        //    method->clazz->descriptor, method->name,
+        //    calledMethod->clazz->descriptor, calledMethod->name);
+    }
+
+    return true;
+}
+
+/*
+ * Resolve an interface method reference.
+ *
+ * No method access check here -- interface methods are always public.
+ *
+ * Returns NULL if the method was not found.  Does not throw an exception.
+ */
+Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    Method* resMethod;
+    int i;
+
+    LOGVV("--- resolving interface method %d (referrer=%s)\n",
+        methodIdx, referrer->descriptor);
+
+    resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+    if (resMethod == NULL) {
+        const DexMethodId* pMethodId;
+        ClassObject* resClass;
+
+        pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+        resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
+        if (resClass == NULL) {
+            /* can't find the class that the method is a part of */
+            dvmClearOptException(dvmThreadSelf());
+            return NULL;
+        }
+        if (!dvmIsInterfaceClass(resClass)) {
+            /* whoops */
+            LOGI("Interface method not part of interface class\n");
+            return NULL;
+        }
+
+        const char* methodName =
+            dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+        DexProto proto;
+        dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+        LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
+            methodName, methodSig, resClass->descriptor);
+        resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
+        if (resMethod == NULL) {
+            /* scan superinterfaces and superclass interfaces */
+            LOGVV("+++ did not resolve immediately\n");
+            for (i = 0; i < resClass->iftableCount; i++) {
+                resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
+                                methodName, &proto);
+                if (resMethod != NULL)
+                    break;
+            }
+
+            if (resMethod == NULL) {
+                LOGVV("+++ unable to resolve method %s\n", methodName);
+                return NULL;
+            }
+        } else {
+            LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
+                resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
+        }
+
+        /* we're expecting this to be abstract */
+        if (!dvmIsAbstractMethod(resMethod)) {
+            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+            LOGW("Found non-abstract interface method %s.%s %s\n",
+                resMethod->clazz->descriptor, resMethod->name, desc);
+            free(desc);
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         */
+        dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+    }
+
+    LOGVV("--- found interface method %d (%s.%s)\n",
+        methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+    /* interface methods are always public; no need to check access */
+
+    return resMethod;
+}
+
+/*
+ * See if the method being called can be rewritten as an inline operation.
+ * Works for invoke-virtual, invoke-direct, and invoke-static.
+ *
+ * Returns "true" if we replace it.
+ */
+static bool rewriteExecuteInline(Method* method, u2* insns,
+    MethodType methodType, const InlineSub* inlineSubs)
+{
+    ClassObject* clazz = method->clazz;
+    Method* calledMethod;
+    u2 methodIdx = insns[1];
+
+    //return false;
+
+    calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
+    if (calledMethod == NULL) {
+        LOGV("+++ DexOpt inline: can't find %d\n", methodIdx);
+        return false;
+    }
+
+    while (inlineSubs->method != NULL) {
+        /*
+        if (extra) {
+            LOGI("comparing %p vs %p %s.%s %s\n",
+                inlineSubs->method, calledMethod,
+                inlineSubs->method->clazz->descriptor,
+                inlineSubs->method->name,
+                inlineSubs->method->signature);
+        }
+        */
+        if (inlineSubs->method == calledMethod) {
+            assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
+                   (insns[0] & 0xff) == OP_INVOKE_STATIC ||
+                   (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
+            insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE;
+            insns[1] = (u2) inlineSubs->inlineIdx;
+
+            //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
+            //    method->clazz->descriptor, method->name,
+            //    calledMethod->clazz->descriptor, calledMethod->name);
+            return true;
+        }
+
+        inlineSubs++;
+    }
+
+    return false;
+}
+
+/*
+ * See if the method being called can be rewritten as an inline operation.
+ * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range.
+ *
+ * Returns "true" if we replace it.
+ */
+static bool rewriteExecuteInlineRange(Method* method, u2* insns,
+    MethodType methodType, const InlineSub* inlineSubs)
+{
+    ClassObject* clazz = method->clazz;
+    Method* calledMethod;
+    u2 methodIdx = insns[1];
+
+    calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
+    if (calledMethod == NULL) {
+        LOGV("+++ DexOpt inline/range: can't find %d\n", methodIdx);
+        return false;
+    }
+
+    while (inlineSubs->method != NULL) {
+        if (inlineSubs->method == calledMethod) {
+            assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
+                   (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
+                   (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
+            insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE;
+            insns[1] = (u2) inlineSubs->inlineIdx;
+
+            //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
+            //    method->clazz->descriptor, method->name,
+            //    calledMethod->clazz->descriptor, calledMethod->name);
+            return true;
+        }
+
+        inlineSubs++;
+    }
+
+    return false;
+}
+
diff --git a/vm/analysis/Optimize.h b/vm/analysis/Optimize.h
new file mode 100644
index 0000000..eb47793
--- /dev/null
+++ b/vm/analysis/Optimize.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Bytecode optimization declarations.
+ */
+#ifndef _DALVIK_OPTIMIZE
+#define _DALVIK_OPTIMIZE
+
+/*
+ * Entry point from DEX preparation.
+ */
+bool dvmRewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
+    u4* pHeaderFlags, DexClassLookup** ppClassLookup);
+
+/*
+ * Abbreviated resolution functions, for use by optimization and verification
+ * code.
+ */
+ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
+    VerifyError* pFailure);
+Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
+    MethodType methodType, VerifyError* pFailure);
+Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx);
+InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
+    VerifyError* pFailure);
+StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
+    VerifyError* pFailure);
+
+#endif /*_DALVIK_OPTIMIZE*/
diff --git a/vm/analysis/VerifySubs.h b/vm/analysis/VerifySubs.h
index a87c6f1..9e05e9d 100644
--- a/vm/analysis/VerifySubs.h
+++ b/vm/analysis/VerifySubs.h
@@ -22,11 +22,13 @@
 
 /*
  * InsnFlags is a 32-bit integer with the following layout:
- *  0-15  instruction length (or 0 if this address doesn't hold an opcode)
- *  16    opcode flag (indicating this address holds an opcode)
- *  17    try block (indicating exceptions thrown here may be caught locally)
- *  30    visited (verifier has examined this instruction at least once)
- *  31    changed (set/cleared as bytecode verifier runs)
+ *   0-15  instruction length (or 0 if this address doesn't hold an opcode)
+ *  16-31  single bit flags:
+ *    InTry: in "try" block; exceptions thrown here may be caught locally
+ *    BranchTarget: other instructions can branch to this instruction
+ *    GcPoint: this instruction is a GC safe point
+ *    Visited: verifier has examined this instruction at least once
+ *    Changed: set/cleared as bytecode verifier runs
  */
 typedef u4 InsnFlags;
 
diff --git a/vm/compiler/Compiler.c b/vm/compiler/Compiler.c
index 6cb1c77..9964285 100644
--- a/vm/compiler/Compiler.c
+++ b/vm/compiler/Compiler.c
@@ -590,7 +590,7 @@
                  * of the vm but this should be acceptable.
                  */
                 if (!gDvmJit.blockingMode)
-                    dvmCheckSuspendPending(NULL);
+                    dvmCheckSuspendPending(dvmThreadSelf());
                 /* Is JitTable filling up? */
                 if (gDvmJit.jitTableEntriesUsed >
                     (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {