Merge commit 'b75a263239ff48c87b826f5d62a53c0efd22b507' into HEAD

Change-Id: Ia37a6c18e09e6bbfab4beadc5ed86f92b766fe21
diff --git a/Android.mk b/Android.mk
index adde173..140509c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,7 +17,6 @@
 subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
 		libdex \
 		vm \
-		dalvikvm \
 		dexgen \
 		dexlist \
 		dexopt \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 3fbdc64..2894484 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -54,3 +54,4 @@
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
+$(call add-clean-step, rm -rf $(OUT)/obj/STATIC_LIBRARIES/libdex_intermediates/import_includes)
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
deleted file mode 100644
index e526098..0000000
--- a/dalvikvm/Android.mk
+++ /dev/null
@@ -1,74 +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.
-
-
-LOCAL_PATH:= $(call my-dir)
-
-#
-# Common definitions.
-#
-
-dalvikvm_src_files := \
-    Main.cpp
-
-dalvikvm_c_includes := \
-    dalvik/include
-
-
-#
-# Build for the target (device).
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(dalvikvm_src_files)
-LOCAL_C_INCLUDES := $(dalvikvm_c_includes)
-
-LOCAL_SHARED_LIBRARIES := \
-    libdvm \
-    libssl \
-    libz
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := dalvikvm
-
-include $(BUILD_EXECUTABLE)
-
-
-#
-# Build for the host.
-#
-
-ifeq ($(WITH_HOST_DALVIK),true)
-
-    include $(CLEAR_VARS)
-    LOCAL_SRC_FILES := $(dalvikvm_src_files)
-    LOCAL_C_INCLUDES := $(dalvikvm_c_includes)
-
-    ifeq ($(HOST_OS)-$(HOST_ARCH),darwin-x86)
-        # OS X comes with all these libraries, so there is no need
-        # to build any of them. Note: OpenSSL consists of libssl
-        # and libcrypto.
-        LOCAL_LDLIBS := -lffi -lssl -lcrypto -lz
-    else
-        LOCAL_LDLIBS += -ldl -lpthread
-        LOCAL_SHARED_LIBRARIES += libdvm libcrypto-host libicuuc-host libicui18n-host libssl-host
-    endif
-
-    LOCAL_MODULE_TAGS := optional
-    LOCAL_MODULE := dalvikvm
-
-    include $(BUILD_HOST_EXECUTABLE)
-
-endif
diff --git a/dalvikvm/Main.cpp b/dalvikvm/Main.cpp
deleted file mode 100644
index 4aa6e20..0000000
--- a/dalvikvm/Main.cpp
+++ /dev/null
@@ -1,301 +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.
- */
-/*
- * Command-line invocation of the Dalvik VM.
- */
-#include "jni.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <signal.h>
-#include <assert.h>
-
-
-/*
- * We want failed write() calls to just return with an error.
- */
-static void blockSigpipe()
-{
-    sigset_t mask;
-
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGPIPE);
-    if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0)
-        fprintf(stderr, "WARNING: SIGPIPE not blocked\n");
-}
-
-/*
- * Create a String[] and populate it with the contents of argv.
- */
-static jobjectArray createStringArray(JNIEnv* env, char* const argv[], int argc)
-{
-    jclass stringClass = NULL;
-    jobjectArray strArray = NULL;
-    jobjectArray result = NULL;
-    int i;
-
-    stringClass = env->FindClass("java/lang/String");
-    if (env->ExceptionCheck()) {
-        fprintf(stderr, "Got exception while finding class String\n");
-        goto bail;
-    }
-    assert(stringClass != NULL);
-    strArray = env->NewObjectArray(argc, stringClass, NULL);
-    if (env->ExceptionCheck()) {
-        fprintf(stderr, "Got exception while creating String array\n");
-        goto bail;
-    }
-    assert(strArray != NULL);
-
-    for (i = 0; i < argc; i++) {
-        jstring argStr;
-
-        argStr = env->NewStringUTF(argv[i]);
-        if (env->ExceptionCheck()) {
-            fprintf(stderr, "Got exception while allocating Strings\n");
-            goto bail;
-        }
-        assert(argStr != NULL);
-        env->SetObjectArrayElement(strArray, i, argStr);
-        env->DeleteLocalRef(argStr);
-    }
-
-    /* return the array, and ensure we don't delete the local ref to it */
-    result = strArray;
-    strArray = NULL;
-
-bail:
-    env->DeleteLocalRef(stringClass);
-    env->DeleteLocalRef(strArray);
-    return result;
-}
-
-/*
- * Determine whether or not the specified method is public.
- *
- * Returns JNI_TRUE on success, JNI_FALSE on failure.
- */
-static int methodIsPublic(JNIEnv* env, jclass clazz, jmethodID methodId)
-{
-    static const int PUBLIC = 0x0001;   // java.lang.reflect.Modifiers.PUBLIC
-    jobject refMethod = NULL;
-    jclass methodClass = NULL;
-    jmethodID getModifiersId;
-    int modifiers;
-    int result = JNI_FALSE;
-
-    refMethod = env->ToReflectedMethod(clazz, methodId, JNI_FALSE);
-    if (refMethod == NULL) {
-        fprintf(stderr, "Dalvik VM unable to get reflected method\n");
-        goto bail;
-    }
-
-    /*
-     * We now have a Method instance.  We need to call
-     * its getModifiers() method.
-     */
-    methodClass = env->FindClass("java/lang/reflect/Method");
-    if (methodClass == NULL) {
-        fprintf(stderr, "Dalvik VM unable to find class Method\n");
-        goto bail;
-    }
-    getModifiersId = env->GetMethodID(methodClass,
-                        "getModifiers", "()I");
-    if (getModifiersId == NULL) {
-        fprintf(stderr, "Dalvik VM unable to find reflect.Method.getModifiers\n");
-        goto bail;
-    }
-
-    modifiers = env->CallIntMethod(refMethod, getModifiersId);
-    if ((modifiers & PUBLIC) == 0) {
-        fprintf(stderr, "Dalvik VM: main() is not public\n");
-        goto bail;
-    }
-
-    result = JNI_TRUE;
-
-bail:
-    env->DeleteLocalRef(refMethod);
-    env->DeleteLocalRef(methodClass);
-    return result;
-}
-
-/*
- * Parse arguments.  Most of it just gets passed through to the VM.  The
- * JNI spec defines a handful of standard arguments.
- */
-int main(int argc, char* const argv[])
-{
-    JavaVM* vm = NULL;
-    JNIEnv* env = NULL;
-    JavaVMInitArgs initArgs;
-    JavaVMOption* options = NULL;
-    char* slashClass = NULL;
-    int optionCount, curOpt, i, argIdx;
-    int needExtra = JNI_FALSE;
-    int result = 1;
-
-    setvbuf(stdout, NULL, _IONBF, 0);
-
-    /* ignore argv[0] */
-    argv++;
-    argc--;
-
-    /*
-     * If we're adding any additional stuff, e.g. function hook specifiers,
-     * add them to the count here.
-     *
-     * We're over-allocating, because this includes the options to the VM
-     * plus the options to the program.
-     */
-    optionCount = argc;
-
-    options = (JavaVMOption*) malloc(sizeof(JavaVMOption) * optionCount);
-    memset(options, 0, sizeof(JavaVMOption) * optionCount);
-
-    /*
-     * Copy options over.  Everything up to the name of the class starts
-     * with a '-' (the function hook stuff is strictly internal).
-     *
-     * [Do we need to catch & handle "-jar" here?]
-     */
-    for (curOpt = argIdx = 0; argIdx < argc; argIdx++) {
-        if (argv[argIdx][0] != '-' && !needExtra)
-            break;
-        options[curOpt++].optionString = strdup(argv[argIdx]);
-
-        /* some options require an additional arg */
-        needExtra = JNI_FALSE;
-        if (strcmp(argv[argIdx], "-classpath") == 0 ||
-            strcmp(argv[argIdx], "-cp") == 0)
-            /* others? */
-        {
-            needExtra = JNI_TRUE;
-        }
-    }
-
-    if (needExtra) {
-        fprintf(stderr, "Dalvik VM requires value after last option flag\n");
-        goto bail;
-    }
-
-    /* insert additional internal options here */
-
-    assert(curOpt <= optionCount);
-
-    initArgs.version = JNI_VERSION_1_4;
-    initArgs.options = options;
-    initArgs.nOptions = curOpt;
-    initArgs.ignoreUnrecognized = JNI_FALSE;
-
-    //printf("nOptions = %d\n", initArgs.nOptions);
-
-    blockSigpipe();
-
-    /*
-     * Start VM.  The current thread becomes the main thread of the VM.
-     */
-    if (JNI_CreateJavaVM(&vm, &env, &initArgs) < 0) {
-        fprintf(stderr, "Dalvik VM init failed (check log file)\n");
-        goto bail;
-    }
-
-    /*
-     * Make sure they provided a class name.  We do this after VM init
-     * so that things like "-Xrunjdwp:help" have the opportunity to emit
-     * a usage statement.
-     */
-    if (argIdx == argc) {
-        fprintf(stderr, "Dalvik VM requires a class name\n");
-        goto bail;
-    }
-
-    /*
-     * We want to call main() with a String array with our arguments in it.
-     * Create an array and populate it.  Note argv[0] is not included.
-     */
-    jobjectArray strArray;
-    strArray = createStringArray(env, &argv[argIdx+1], argc-argIdx-1);
-    if (strArray == NULL)
-        goto bail;
-
-    /*
-     * Find [class].main(String[]).
-     */
-    jclass startClass;
-    jmethodID startMeth;
-    char* cp;
-
-    /* convert "com.android.Blah" to "com/android/Blah" */
-    slashClass = strdup(argv[argIdx]);
-    for (cp = slashClass; *cp != '\0'; cp++)
-        if (*cp == '.')
-            *cp = '/';
-
-    startClass = env->FindClass(slashClass);
-    if (startClass == NULL) {
-        fprintf(stderr, "Dalvik VM unable to locate class '%s'\n", slashClass);
-        goto bail;
-    }
-
-    startMeth = env->GetStaticMethodID(startClass,
-                    "main", "([Ljava/lang/String;)V");
-    if (startMeth == NULL) {
-        fprintf(stderr, "Dalvik VM unable to find static main(String[]) in '%s'\n",
-            slashClass);
-        goto bail;
-    }
-
-    /*
-     * Make sure the method is public.  JNI doesn't prevent us from calling
-     * a private method, so we have to check it explicitly.
-     */
-    if (!methodIsPublic(env, startClass, startMeth))
-        goto bail;
-
-    /*
-     * Invoke main().
-     */
-    env->CallStaticVoidMethod(startClass, startMeth, strArray);
-
-    if (!env->ExceptionCheck())
-        result = 0;
-
-bail:
-    /*printf("Shutting down Dalvik VM\n");*/
-    if (vm != NULL) {
-        /*
-         * This allows join() and isAlive() on the main thread to work
-         * correctly, and also provides uncaught exception handling.
-         */
-        if (vm->DetachCurrentThread() != JNI_OK) {
-            fprintf(stderr, "Warning: unable to detach main thread\n");
-            result = 1;
-        }
-
-        if (vm->DestroyJavaVM() != 0)
-            fprintf(stderr, "Warning: Dalvik VM did not shut down cleanly\n");
-        /*printf("\nDalvik VM has exited\n");*/
-    }
-
-    for (i = 0; i < optionCount; i++)
-        free((char*) options[i].optionString);
-    free(options);
-    free(slashClass);
-    /*printf("--- VM is down, process exiting\n");*/
-    return result;
-}
diff --git a/dx/src/com/android/dx/Version.java b/dx/src/com/android/dx/Version.java
index 130025a..e252996 100644
--- a/dx/src/com/android/dx/Version.java
+++ b/dx/src/com/android/dx/Version.java
@@ -21,5 +21,5 @@
  */
 public class Version {
     /** {@code non-null;} version string */
-    public static final String VERSION = "1.7";
+    public static final String VERSION = "1.8";
 }
diff --git a/dx/src/com/android/dx/cf/direct/ClassPathOpener.java b/dx/src/com/android/dx/cf/direct/ClassPathOpener.java
index e2e2cfb..c9fe275 100644
--- a/dx/src/com/android/dx/cf/direct/ClassPathOpener.java
+++ b/dx/src/com/android/dx/cf/direct/ClassPathOpener.java
@@ -17,6 +17,7 @@
 package com.android.dx.cf.direct;
 
 import com.android.dex.util.FileUtils;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
@@ -45,6 +46,7 @@
      * package.
      */
     private final boolean sort;
+    private FileNameFilter filter;
 
     /**
      * Callback interface for {@code ClassOpener}.
@@ -82,6 +84,25 @@
     }
 
     /**
+     * Filter interface for {@code ClassOpener}.
+     */
+    public interface FileNameFilter {
+
+        boolean accept(String path);
+    }
+
+    /**
+     * An accept all filter.
+     */
+    public static final FileNameFilter acceptAll = new FileNameFilter() {
+
+        @Override
+        public boolean accept(String path) {
+            return true;
+        }
+    };
+
+    /**
      * Constructs an instance.
      *
      * @param pathname {@code non-null;} path element to process
@@ -91,9 +112,24 @@
      * @param consumer {@code non-null;} callback interface
      */
     public ClassPathOpener(String pathname, boolean sort, Consumer consumer) {
+        this(pathname, sort, acceptAll, consumer);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param pathname {@code non-null;} path element to process
+     * @param sort if true, sort such that classes appear before their inner
+     * classes and "package-info" occurs before all other classes in that
+     * package.
+     * @param consumer {@code non-null;} callback interface
+     */
+    public ClassPathOpener(String pathname, boolean sort, FileNameFilter filter,
+            Consumer consumer) {
         this.pathname = pathname;
         this.sort = sort;
         this.consumer = consumer;
+        this.filter = filter;
     }
 
     /**
@@ -129,9 +165,12 @@
                     path.endsWith(".apk")) {
                 return processArchive(file);
             }
-
-            byte[] bytes = FileUtils.readFile(file);
-            return consumer.processFileBytes(path, file.lastModified(), bytes);
+            if (filter.accept(path)) {
+                byte[] bytes = FileUtils.readFile(file);
+                return consumer.processFileBytes(path, file.lastModified(), bytes);
+            } else {
+                return false;
+            }
         } catch (Exception ex) {
             consumer.onException(ex);
             return false;
@@ -226,22 +265,24 @@
             }
 
             String path = one.getName();
-            InputStream in = zip.getInputStream(one);
+            if (filter.accept(path)) {
+                InputStream in = zip.getInputStream(one);
 
-            baos.reset();
-            for (;;) {
-                int amt = in.read(buf);
-                if (amt < 0) {
-                    break;
+                baos.reset();
+                for (;;) {
+                    int amt = in.read(buf);
+                    if (amt < 0) {
+                        break;
+                    }
+
+                    baos.write(buf, 0, amt);
                 }
 
-                baos.write(buf, 0, amt);
+                in.close();
+
+                byte[] bytes = baos.toByteArray();
+                any |= consumer.processFileBytes(path, one.getTime(), bytes);
             }
-
-            in.close();
-
-            byte[] bytes = baos.toByteArray();
-            any |= consumer.processFileBytes(path, one.getTime(), bytes);
         }
 
         zip.close();
diff --git a/dx/src/com/android/dx/cf/direct/DirectClassFile.java b/dx/src/com/android/dx/cf/direct/DirectClassFile.java
index 2af2efe..f908547 100644
--- a/dx/src/com/android/dx/cf/direct/DirectClassFile.java
+++ b/dx/src/com/android/dx/cf/direct/DirectClassFile.java
@@ -229,6 +229,15 @@
     }
 
     /**
+     * Gets the path where this class file is located.
+     *
+     * @return {@code non-null;} the filePath
+     */
+    public String getFilePath() {
+      return filePath;
+    }
+
+    /**
      * Gets the {@link ByteArray} that this instance's data comes from.
      *
      * @return {@code non-null;} the bytes
diff --git a/dx/src/com/android/dx/command/Main.java b/dx/src/com/android/dx/command/Main.java
index 6540e35..1132edb 100644
--- a/dx/src/com/android/dx/command/Main.java
+++ b/dx/src/com/android/dx/command/Main.java
@@ -34,12 +34,24 @@
         "  [--dump-method=<name>[*]] [--verbose-dump] [--no-files] " +
         "[--core-library]\n" +
         "  [--num-threads=<n>] [--incremental] [--force-jumbo]\n" +
+        "  [--multi-dex [--main-dex-list=<file> [--minimal-main-dex]]\n" +
         "  [<file>.class | <file>.{zip,jar,apk} | <directory>] ...\n" +
         "    Convert a set of classfiles into a dex file, optionally " +
         "embedded in a\n" +
         "    jar/zip. Output name must end with one of: .dex .jar " +
-        ".zip .apk. Positions\n" +
-        "    options: none, important, lines.\n" +
+        ".zip .apk or be a directory.\n" +
+        "    Positions options: none, important, lines.\n" +
+        "    --multi-dex: allows to generate several dex files if needed. " +
+        "This option is \n" +
+        "    exclusive with --incremental, causes --num-threads to be ignored " +
+        "and only\n" +
+        "    supports folder or archive output.\n" +
+        "    --main-dex-list=<file>: <file> is a list of class file names, " +
+        "classes defined by\n" +
+        "    those class files are put in classes.dex.\n" +
+        "    --minimal-main-dex: only classes selected by --main-dex-list are " +
+        "to be put in\n" +
+        "    the main dex.\n" +
         "  dx --annotool --annotation=<class> [--element=<element types>]\n" +
         "  [--print=<print types>]\n" +
         "  dx --dump [--debug] [--strict] [--bytes] [--optimize]\n" +
diff --git a/dx/src/com/android/dx/command/dexer/Main.java b/dx/src/com/android/dx/command/dexer/Main.java
index a9d5a32..ab6d2f7 100644
--- a/dx/src/com/android/dx/command/dexer/Main.java
+++ b/dx/src/com/android/dx/command/dexer/Main.java
@@ -17,11 +17,15 @@
 package com.android.dx.command.dexer;
 
 import com.android.dex.Dex;
+import com.android.dex.DexException;
 import com.android.dex.DexFormat;
 import com.android.dex.util.FileUtils;
 import com.android.dx.Version;
 import com.android.dx.cf.code.SimException;
 import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.cf.direct.ClassPathOpener.FileNameFilter;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
 import com.android.dx.cf.iface.ParseException;
 import com.android.dx.command.DxConsole;
 import com.android.dx.command.UsageException;
@@ -30,6 +34,7 @@
 import com.android.dx.dex.cf.CfTranslator;
 import com.android.dx.dex.cf.CodeStatistics;
 import com.android.dx.dex.code.PositionList;
+import com.android.dx.dex.file.AnnotationUtils;
 import com.android.dx.dex.file.ClassDefItem;
 import com.android.dx.dex.file.DexFile;
 import com.android.dx.dex.file.EncodedMethod;
@@ -40,18 +45,24 @@
 import com.android.dx.rop.annotation.AnnotationsList;
 import com.android.dx.rop.cst.CstNat;
 import com.android.dx.rop.cst.CstString;
+
+import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -66,6 +77,17 @@
  */
 public class Main {
     /**
+     * File extension of a {@code .dex} file.
+     */
+    private static final String DEX_EXTENSION = ".dex";
+
+    /**
+     * File name prefix of a {@code .dex} file automatically loaded in an
+     * archive.
+     */
+    private static final String DEX_PREFIX = "classes";
+
+    /**
      * {@code non-null;} the lengthy message that tries to discourage
      * people from defining core classes in applications
      */
@@ -153,6 +175,12 @@
     /** class files older than this must be defined in the target dex file. */
     private static long minimumFileAge = 0;
 
+    private static Set<String> classesInMainDex = null;
+
+    private static List<byte[]> dexOutputArrays = new ArrayList<byte[]>();
+
+    private static OutputStreamWriter humanOutWriter = null;
+
     /**
      * This class is uninstantiable.
      */
@@ -189,6 +217,37 @@
         args = arguments;
         args.makeOptionsObjects();
 
+        OutputStream humanOutRaw = null;
+        if (args.humanOutName != null) {
+            humanOutRaw = openOutput(args.humanOutName);
+            humanOutWriter = new OutputStreamWriter(humanOutRaw);
+        }
+
+        try {
+            if (args.multiDex) {
+                return runMultiDex();
+            } else {
+                return runMonoDex();
+            }
+        } finally {
+            closeOutput(humanOutRaw);
+        }
+    }
+
+    /**
+     * {@code non-null;} Error message for too many method/field/type ids.
+     */
+    public static String getTooManyIdsErrorMessage() {
+        if (args.multiDex) {
+            return "The list of classes given in " + Arguments.MAIN_DEX_LIST_OPTION +
+                   " is too big and does not fit in the main dex.";
+        } else {
+            return "You may try using " + Arguments.MULTI_DEX_OPTION + " option.";
+        }
+    }
+
+    private static int runMonoDex() throws IOException {
+
         File incrementalOutFile = null;
         if (args.incremental) {
             if (args.outName == null) {
@@ -231,7 +290,10 @@
             // Effectively free up the (often massive) DexFile memory.
             outputDex = null;
 
-            if (!createJar(args.outName, outArray)) {
+            if (outArray != null) {
+                outputResources.put(DexFormat.DEX_IN_JAR_NAME, outArray);
+            }
+            if (!createJar(args.outName)) {
                 return 3;
             }
         } else if (outArray != null && args.outName != null) {
@@ -243,6 +305,87 @@
         return 0;
     }
 
+    private static int runMultiDex() throws IOException {
+
+        assert !args.incremental;
+        assert args.numThreads == 1;
+
+        if (args.mainDexListFile != null) {
+            classesInMainDex = loadMainDexListFile(args.mainDexListFile);
+        }
+
+        if (!processAllFiles()) {
+            return 1;
+        }
+
+        if (!libraryDexBuffers.isEmpty()) {
+            throw new DexException("Library dex files are not supported in multi-dex mode");
+        }
+
+        if (outputDex != null) {
+            // this array is null if no classes were defined
+            dexOutputArrays.add(writeDex());
+
+            // Effectively free up the (often massive) DexFile memory.
+            outputDex = null;
+        }
+
+        if (args.jarOutput) {
+
+            for (int i = 0; i < dexOutputArrays.size(); i++) {
+                outputResources.put(getDexFileName(i),
+                        dexOutputArrays.get(i));
+            }
+
+            if (!createJar(args.outName)) {
+                return 3;
+            }
+        } else if (args.outName != null) {
+            File outDir = new File(args.outName);
+            assert outDir.isDirectory();
+            for (int i = 0; i < dexOutputArrays.size(); i++) {
+                OutputStream out = new FileOutputStream(new File(outDir, getDexFileName(i)));
+                try {
+                    out.write(dexOutputArrays.get(i));
+                } finally {
+                    closeOutput(out);
+                }
+            }
+
+        }
+
+        return 0;
+    }
+
+    private static String getDexFileName(int i) {
+        if (i == 0) {
+            return DexFormat.DEX_IN_JAR_NAME;
+        } else {
+            return DEX_PREFIX + (i + 1) + DEX_EXTENSION;
+        }
+    }
+
+    private static Set<String> loadMainDexListFile(String mainDexListFile) throws IOException {
+        Set<String> mainDexList = new HashSet<String>();
+        BufferedReader bfr = null;
+        try {
+            FileReader fr = new FileReader(mainDexListFile);
+            bfr = new BufferedReader(fr);
+
+            String line;
+
+            while (null != (line = bfr.readLine())) {
+                mainDexList.add(fixPath(line));
+            }
+
+        } finally {
+            if (bfr != null) {
+                bfr.close();
+            }
+        }
+        return mainDexList;
+    }
+
     /**
      * Merges the dex files {@code update} and {@code base}, preferring
      * {@code update}'s definition for types defined in both dex files.
@@ -307,16 +450,12 @@
      * @return whether processing was successful
      */
     private static boolean processAllFiles() {
-        outputDex = new DexFile(args.dexOptions);
+        createDexFile();
 
         if (args.jarOutput) {
             outputResources = new TreeMap<String, byte[]>();
         }
 
-        if (args.dumpWidth != 0) {
-            outputDex.setDumpWidth(args.dumpWidth);
-        }
-
         anyFilesProcessed = false;
         String[] fileNames = args.fileNames;
 
@@ -325,9 +464,40 @@
         }
 
         try {
-            for (int i = 0; i < fileNames.length; i++) {
-                if (processOne(fileNames[i])) {
-                    anyFilesProcessed = true;
+            if (args.mainDexListFile != null) {
+                // with --main-dex-list
+                FileNameFilter mainPassFilter = args.strictNameCheck ? new MainDexListFilter() :
+                    new BestEffortMainDexListFilter();
+
+                // forced in main dex
+                for (int i = 0; i < fileNames.length; i++) {
+                    if (processOne(fileNames[i], mainPassFilter)) {
+                        anyFilesProcessed = true;
+                    }
+                }
+
+                if (dexOutputArrays.size() > 1) {
+                    throw new DexException("Too many classes in " + Arguments.MAIN_DEX_LIST_OPTION
+                            + ", main dex capacity exceeded");
+                }
+
+                if (args.minimalMainDex) {
+                    // start second pass directly in a secondary dex file.
+                    createDexFile();
+                }
+
+                // remaining files
+                for (int i = 0; i < fileNames.length; i++) {
+                    if (processOne(fileNames[i], new NotFilter(mainPassFilter))) {
+                        anyFilesProcessed = true;
+                    }
+                }
+            } else {
+                // without --main-dex-list
+                for (int i = 0; i < fileNames.length; i++) {
+                    if (processOne(fileNames[i], ClassPathOpener.acceptAll)) {
+                        anyFilesProcessed = true;
+                    }
                 }
             }
         } catch (StopProcessing ex) {
@@ -368,18 +538,31 @@
         return true;
     }
 
+    private static void createDexFile() {
+        if (outputDex != null) {
+            dexOutputArrays.add(writeDex());
+        }
+
+        outputDex = new DexFile(args.dexOptions);
+
+        if (args.dumpWidth != 0) {
+            outputDex.setDumpWidth(args.dumpWidth);
+        }
+    }
+
     /**
      * Processes one pathname element.
      *
      * @param pathname {@code non-null;} the pathname to process. May
      * be the path of a class file, a jar file, or a directory
      * containing class files.
+     * @param filter {@code non-null;} A filter for excluding files.
      * @return whether any processing actually happened
      */
-    private static boolean processOne(String pathname) {
+    private static boolean processOne(String pathname, FileNameFilter filter) {
         ClassPathOpener opener;
 
-        opener = new ClassPathOpener(pathname, false,
+        opener = new ClassPathOpener(pathname, false, filter,
                 new ClassPathOpener.Consumer() {
             public boolean processFileBytes(String name, long lastModified, byte[] bytes) {
                 if (args.numThreads > 1) {
@@ -439,6 +622,7 @@
         String fixedName = fixPath(name);
 
         if (isClass) {
+
             if (keepResources && args.keepClassesInJar) {
                 synchronized (outputResources) {
                     outputResources.put(fixedName, bytes);
@@ -474,13 +658,34 @@
             checkClassName(name);
         }
 
+        DirectClassFile cf =
+            new DirectClassFile(bytes, name, args.cfOptions.strictNameCheck);
+
+        cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        cf.getMagic();
+
+        int numMethodIds = outputDex.getMethodIds().items().size();
+        int numFieldIds = outputDex.getFieldIds().items().size();
+        int numTypeIds = outputDex.getTypeIds().items().size();
+        int constantPoolSize = cf.getConstantPool().size();
+
+        if (args.multiDex && ((numMethodIds + constantPoolSize > args.maxNumberOfIdxPerDex) ||
+            (numFieldIds + constantPoolSize > args.maxNumberOfIdxPerDex) ||
+            (numTypeIds + constantPoolSize
+                    /* annotation added by dx are not counted in numTypeIds */
+                    + AnnotationUtils.DALVIK_ANNOTATION_NUMBER
+                    > args.maxNumberOfIdxPerDex))) {
+          createDexFile();
+        }
+
         try {
             ClassDefItem clazz =
-                CfTranslator.translate(name, bytes, args.cfOptions, args.dexOptions);
+                CfTranslator.translate(cf, bytes, args.cfOptions, args.dexOptions, outputDex);
             synchronized (outputDex) {
                 outputDex.add(clazz);
             }
             return true;
+
         } catch (ParseException ex) {
             DxConsole.err.println("\ntrouble processing:");
             if (args.debug) {
@@ -544,14 +749,7 @@
         byte[] outArray = null;
 
         try {
-            OutputStream humanOutRaw = null;
-            OutputStreamWriter humanOut = null;
             try {
-                if (args.humanOutName != null) {
-                    humanOutRaw = openOutput(args.humanOutName);
-                    humanOut = new OutputStreamWriter(humanOutRaw);
-                }
-
                 if (args.methodToDump != null) {
                     /*
                      * Simply dump the requested method. Note: The call
@@ -559,23 +757,22 @@
                      * structures ready.
                      */
                     outputDex.toDex(null, false);
-                    dumpMethod(outputDex, args.methodToDump, humanOut);
+                    dumpMethod(outputDex, args.methodToDump, humanOutWriter);
                 } else {
                     /*
                      * This is the usual case: Create an output .dex file,
                      * and write it, dump it, etc.
                      */
-                    outArray = outputDex.toDex(humanOut, args.verboseDump);
+                    outArray = outputDex.toDex(humanOutWriter, args.verboseDump);
                 }
 
                 if (args.statistics) {
                     DxConsole.out.println(outputDex.getStatistics().toHuman());
                 }
             } finally {
-                if (humanOut != null) {
-                    humanOut.flush();
+                if (humanOutWriter != null) {
+                    humanOutWriter.flush();
                 }
-                closeOutput(humanOutRaw);
             }
         } catch (Exception ex) {
             if (args.debug) {
@@ -592,14 +789,12 @@
     }
 
     /**
-     * Creates a jar file from the resources and given dex file array.
+     * Creates a jar file from the resources (including dex file arrays).
      *
      * @param fileName {@code non-null;} name of the file
-     * @param dexArray array containing the dex file to include, or null if the
-     *     output contains no class defs.
      * @return whether the creation was successful
      */
-    private static boolean createJar(String fileName, byte[] dexArray) {
+    private static boolean createJar(String fileName) {
         /*
          * Make or modify the manifest (as appropriate), put the dex
          * array into the resources map, and then process the entire
@@ -611,23 +806,19 @@
             OutputStream out = openOutput(fileName);
             JarOutputStream jarOut = new JarOutputStream(out, manifest);
 
-            if (dexArray != null) {
-                outputResources.put(DexFormat.DEX_IN_JAR_NAME, dexArray);
-            }
-
             try {
                 for (Map.Entry<String, byte[]> e :
                          outputResources.entrySet()) {
                     String name = e.getKey();
                     byte[] contents = e.getValue();
                     JarEntry entry = new JarEntry(name);
+                    int length = contents.length;
 
                     if (args.verbose) {
-                        DxConsole.out.println("writing " + name + "; size " +
-                                           contents.length + "...");
+                        DxConsole.out.println("writing " + name + "; size " + length + "...");
                     }
 
-                    entry.setSize(contents.length);
+                    entry.setSize(length);
                     jarOut.putNextEntry(entry);
                     jarOut.write(contents);
                     jarOut.closeEntry();
@@ -856,6 +1047,84 @@
         pw.flush();
     }
 
+    private static class NotFilter implements FileNameFilter {
+        private final FileNameFilter filter;
+
+        private NotFilter(FileNameFilter filter) {
+            this.filter = filter;
+        }
+
+        @Override
+        public boolean accept(String path) {
+            return !filter.accept(path);
+        }
+    }
+
+    /**
+     * A quick and accurate filter for when file path can be trusted.
+     */
+    private static class MainDexListFilter implements FileNameFilter {
+
+        @Override
+        public boolean accept(String fullPath) {
+            if (fullPath.endsWith(".class")) {
+                String path = fixPath(fullPath);
+                return classesInMainDex.contains(path);
+            } else {
+                return true;
+            }
+        }
+    }
+
+    /**
+     * A best effort conservative filter for when file path can <b>not</b> be trusted.
+     */
+    private static class BestEffortMainDexListFilter implements FileNameFilter {
+
+       Map<String, List<String>> map = new HashMap<String, List<String>>();
+
+       public BestEffortMainDexListFilter() {
+           for (String pathOfClass : classesInMainDex) {
+               String normalized = fixPath(pathOfClass);
+               String simple = getSimpleName(normalized);
+               List<String> fullPath = map.get(simple);
+               if (fullPath == null) {
+                   fullPath = new ArrayList<String>(1);
+                   map.put(simple, fullPath);
+               }
+               fullPath.add(normalized);
+           }
+        }
+
+        @Override
+        public boolean accept(String path) {
+            if (path.endsWith(".class")) {
+                String normalized = fixPath(path);
+                String simple = getSimpleName(normalized);
+                List<String> fullPaths = map.get(simple);
+                if (fullPaths != null) {
+                    for (String fullPath : fullPaths) {
+                        if (normalized.endsWith(fullPath)) {
+                            return true;
+                        }
+                    }
+                }
+                return false;
+            } else {
+                return true;
+            }
+        }
+
+        private static String getSimpleName(String path) {
+            int index = path.lastIndexOf('/');
+            if (index >= 0) {
+                return path.substring(index + 1);
+            } else {
+                return path;
+            }
+        }
+    }
+
     /**
      * Exception class used to halt processing prematurely.
      */
@@ -867,6 +1136,17 @@
      * Command-line argument parser and access.
      */
     public static class Arguments {
+
+        private static final String MINIMAL_MAIN_DEX_OPTION = "--minimal-main-dex";
+
+        private static final String MAIN_DEX_LIST_OPTION = "--main-dex-list";
+
+        private static final String MULTI_DEX_OPTION = "--multi-dex";
+
+        private static final String NUM_THREADS_OPTION = "--num-threads";
+
+        private static final String INCREMENTAL_OPTION = "--incremental";
+
         /** whether to run in debug mode */
         public boolean debug = false;
 
@@ -952,6 +1232,19 @@
         /** number of threads to run with */
         public int numThreads = 1;
 
+        /** generation of multiple dex is allowed */
+        public boolean multiDex = false;
+
+        /** Optional file containing a list of class files containing classes to be forced in main
+         * dex */
+        public String mainDexListFile = null;
+
+        /** Produce the smallest possible main dex. Ignored unless multiDex is true and
+         * mainDexListFile is specified and non empty. */
+        public boolean minimalMainDex = false;
+
+        private int maxNumberOfIdxPerDex = DexFormat.MAX_MEMBER_IDX + 1;
+
         private static class ArgumentsParser {
 
             /** The arguments to process. */
@@ -1061,6 +1354,9 @@
         public void parse(String[] args) {
             ArgumentsParser parser = new ArgumentsParser(args);
 
+            boolean outputIsDirectory = false;
+            boolean outputIsDirectDex = false;
+
             while(parser.getNext()) {
                 if (parser.isArg("--debug")) {
                     debug = true;
@@ -1098,11 +1394,15 @@
                     keepClassesInJar = true;
                 } else if (parser.isArg("--output=")) {
                     outName = parser.getLastValue();
-                    if (FileUtils.hasArchiveSuffix(outName)) {
+                    if (new File(outName).isDirectory()) {
+                        jarOutput = false;
+                        outputIsDirectory = true;
+                    } else if (FileUtils.hasArchiveSuffix(outName)) {
                         jarOutput = true;
                     } else if (outName.endsWith(".dex") ||
                                outName.equals("-")) {
                         jarOutput = false;
+                        outputIsDirectDex = true;
                     } else {
                         System.err.println("unknown output extension: " +
                                            outName);
@@ -1130,13 +1430,21 @@
                     }
                 } else if (parser.isArg("--no-locals")) {
                     localInfo = false;
-                } else if (parser.isArg("--num-threads=")) {
+                } else if (parser.isArg(NUM_THREADS_OPTION + "=")) {
                     numThreads = Integer.parseInt(parser.getLastValue());
-                } else if (parser.isArg("--incremental")) {
+                } else if (parser.isArg(INCREMENTAL_OPTION)) {
                     incremental = true;
                 } else if (parser.isArg("--force-jumbo")) {
                     forceJumbo = true;
-                } else {
+                } else if (parser.isArg(MULTI_DEX_OPTION)) {
+                    multiDex = true;
+                } else if (parser.isArg(MAIN_DEX_LIST_OPTION + "=")) {
+                    mainDexListFile = parser.getLastValue();
+                } else if (parser.isArg(MINIMAL_MAIN_DEX_OPTION)) {
+                    minimalMainDex = true;
+                } else if (parser.isArg("--set-max-idx-number=")) { // undocumented test option
+                    maxNumberOfIdxPerDex = Integer.parseInt(parser.getLastValue());
+              } else {
                     System.err.println("unknown option: " + parser.getCurrent());
                     throw new UsageException();
                 }
@@ -1156,6 +1464,39 @@
                 humanOutName = "-";
             }
 
+            if (mainDexListFile != null && !multiDex) {
+                System.err.println(MAIN_DEX_LIST_OPTION + " is only supported in combination with "
+                    + MULTI_DEX_OPTION);
+                throw new UsageException();
+            }
+
+            if (minimalMainDex && (mainDexListFile == null || !multiDex)) {
+                System.err.println(MINIMAL_MAIN_DEX_OPTION + " is only supported in combination with "
+                    + MULTI_DEX_OPTION + " and " + MAIN_DEX_LIST_OPTION);
+                throw new UsageException();
+            }
+
+            if (multiDex && numThreads != 1) {
+                System.out.println(NUM_THREADS_OPTION + "is ignored when used with "
+                    + MULTI_DEX_OPTION);
+            }
+
+            if (multiDex && incremental) {
+                System.err.println(INCREMENTAL_OPTION + " is not supported with "
+                    + MULTI_DEX_OPTION);
+                throw new UsageException();
+            }
+
+            if (multiDex && outputIsDirectDex) {
+                System.err.println("Unsupported output \"" + outName +"\". " + MULTI_DEX_OPTION +
+                        " supports only archive or directory output");
+                throw new UsageException();
+            }
+
+            if (outputIsDirectory && !multiDex) {
+                outName = new File(outName, DexFormat.DEX_IN_JAR_NAME).getPath();
+            }
+
             makeOptionsObjects();
         }
 
diff --git a/dx/src/com/android/dx/dex/cf/CfTranslator.java b/dx/src/com/android/dx/dex/cf/CfTranslator.java
index d4cda2a..8ab1c84 100644
--- a/dx/src/com/android/dx/dex/cf/CfTranslator.java
+++ b/dx/src/com/android/dx/dex/cf/CfTranslator.java
@@ -20,7 +20,6 @@
 import com.android.dx.cf.code.ConcreteMethod;
 import com.android.dx.cf.code.Ropper;
 import com.android.dx.cf.direct.DirectClassFile;
-import com.android.dx.cf.direct.StdAttributeFactory;
 import com.android.dx.cf.iface.Field;
 import com.android.dx.cf.iface.FieldList;
 import com.android.dx.cf.iface.Method;
@@ -30,8 +29,12 @@
 import com.android.dx.dex.code.PositionList;
 import com.android.dx.dex.code.RopTranslator;
 import com.android.dx.dex.file.ClassDefItem;
+import com.android.dx.dex.file.DexFile;
 import com.android.dx.dex.file.EncodedField;
 import com.android.dx.dex.file.EncodedMethod;
+import com.android.dx.dex.file.FieldIdsSection;
+import com.android.dx.dex.file.MethodIdsSection;
+import com.android.dx.dex.file.TypeIdsSection;
 import com.android.dx.rop.annotation.Annotations;
 import com.android.dx.rop.annotation.AnnotationsList;
 import com.android.dx.rop.code.AccessFlags;
@@ -41,11 +44,15 @@
 import com.android.dx.rop.code.RopMethod;
 import com.android.dx.rop.code.TranslationAdvice;
 import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstBaseMethodRef;
 import com.android.dx.rop.cst.CstBoolean;
 import com.android.dx.rop.cst.CstByte;
 import com.android.dx.rop.cst.CstChar;
+import com.android.dx.rop.cst.CstEnumRef;
 import com.android.dx.rop.cst.CstFieldRef;
 import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstInterfaceMethodRef;
 import com.android.dx.rop.cst.CstMethodRef;
 import com.android.dx.rop.cst.CstShort;
 import com.android.dx.rop.cst.CstString;
@@ -81,12 +88,12 @@
      * @param dexOptions options for dex output
      * @return {@code non-null;} the translated class
      */
-    public static ClassDefItem translate(String filePath, byte[] bytes,
-            CfOptions cfOptions, DexOptions dexOptions) {
+    public static ClassDefItem translate(DirectClassFile cf, byte[] bytes,
+            CfOptions cfOptions, DexOptions dexOptions, DexFile dexFile) {
         try {
-            return translate0(filePath, bytes, cfOptions, dexOptions);
+            return translate0(cf, bytes, cfOptions, dexOptions, dexFile);
         } catch (RuntimeException ex) {
-            String msg = "...while processing " + filePath;
+            String msg = "...while processing " + cf.getFilePath();
             throw ExceptionWithContext.withContext(ex, msg);
         }
     }
@@ -103,13 +110,8 @@
      * @param dexOptions options for dex output
      * @return {@code non-null;} the translated class
      */
-    private static ClassDefItem translate0(String filePath, byte[] bytes,
-            CfOptions cfOptions, DexOptions dexOptions) {
-        DirectClassFile cf =
-            new DirectClassFile(bytes, filePath, cfOptions.strictNameCheck);
-
-        cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
-        cf.getMagic();
+    private static ClassDefItem translate0(DirectClassFile cf, byte[] bytes,
+            CfOptions cfOptions, DexOptions dexOptions, DexFile dexFile) {
 
         OptimizerOptions.loadOptimizeLists(cfOptions.optimizeListFile,
                 cfOptions.dontOptimizeListFile);
@@ -130,8 +132,32 @@
             out.setClassAnnotations(classAnnotations);
         }
 
-        processFields(cf, out);
-        processMethods(cf, cfOptions, dexOptions, out);
+        FieldIdsSection fieldIdsSection = dexFile.getFieldIds();
+        MethodIdsSection methodIdsSection = dexFile.getMethodIds();
+        TypeIdsSection typeIdsSection = dexFile.getTypeIds();
+        processFields(cf, out, fieldIdsSection);
+        processMethods(cf, cfOptions, dexOptions, out, methodIdsSection);
+
+        // intern constant pool method, field and type references
+        ConstantPool constantPool = cf.getConstantPool();
+        int constantPoolSize = constantPool.size();
+
+        synchronized (dexFile) {
+            for (int i = 0; i < constantPoolSize; i++) {
+                Constant constant = constantPool.getOrNull(i);
+                if (constant instanceof CstMethodRef) {
+                    methodIdsSection.intern((CstBaseMethodRef) constant);
+                } else if (constant instanceof CstInterfaceMethodRef) {
+                    methodIdsSection.intern(((CstInterfaceMethodRef) constant).toMethodRef());
+                } else if (constant instanceof CstFieldRef) {
+                    fieldIdsSection.intern((CstFieldRef) constant);
+                } else if (constant instanceof CstEnumRef) {
+                    fieldIdsSection.intern(((CstEnumRef) constant).getFieldRef());
+                } else if (constant instanceof CstType) {
+                    typeIdsSection.intern((CstType) constant);
+                }
+            }
+        }
 
         return out;
     }
@@ -142,7 +168,8 @@
      * @param cf {@code non-null;} class being translated
      * @param out {@code non-null;} output class
      */
-    private static void processFields(DirectClassFile cf, ClassDefItem out) {
+    private static void processFields(
+            DirectClassFile cf, ClassDefItem out, FieldIdsSection fieldIdsSection) {
         CstType thisClass = cf.getThisClass();
         FieldList fields = cf.getFields();
         int sz = fields.size();
@@ -170,6 +197,9 @@
                 if (annotations.size() != 0) {
                     out.addFieldAnnotations(field, annotations);
                 }
+                synchronized (fieldIdsSection) {
+                    fieldIdsSection.intern(field);
+                }
             } catch (RuntimeException ex) {
                 String msg = "...while processing " + one.getName().toHuman() +
                     " " + one.getDescriptor().toHuman();
@@ -222,7 +252,7 @@
      * @param out {@code non-null;} output class
      */
     private static void processMethods(DirectClassFile cf, CfOptions cfOptions,
-            DexOptions dexOptions, ClassDefItem out) {
+            DexOptions dexOptions, ClassDefItem out, MethodIdsSection methodIds) {
         CstType thisClass = cf.getThisClass();
         MethodList methods = cf.getMethods();
         int sz = methods.size();
@@ -338,6 +368,9 @@
                 if (list.size() != 0) {
                     out.addParameterAnnotations(meth, list);
                 }
+                synchronized (methodIds) {
+                  methodIds.intern(meth);
+                }
             } catch (RuntimeException ex) {
                 String msg = "...while processing " + one.getName().toHuman() +
                     " " + one.getDescriptor().toHuman();
diff --git a/dx/src/com/android/dx/dex/file/AnnotationUtils.java b/dx/src/com/android/dx/dex/file/AnnotationUtils.java
index 935c250..f7d4492 100644
--- a/dx/src/com/android/dx/dex/file/AnnotationUtils.java
+++ b/dx/src/com/android/dx/dex/file/AnnotationUtils.java
@@ -35,6 +35,13 @@
  * Utility class for dealing with annotations.
  */
 public final class AnnotationUtils {
+
+    /**
+     * Number of annotation types that dx may add in the dex file that were
+     * not defined in the translated class file.
+     */
+    public static final int DALVIK_ANNOTATION_NUMBER = 7;
+
     /** {@code non-null;} type for {@code AnnotationDefault} annotations */
     private static final CstType ANNOTATION_DEFAULT_TYPE =
         CstType.intern(Type.intern("Ldalvik/annotation/AnnotationDefault;"));
diff --git a/dx/src/com/android/dx/dex/file/DexFile.java b/dx/src/com/android/dx/dex/file/DexFile.java
index 0d5b110..01a5e4b 100644
--- a/dx/src/com/android/dx/dex/file/DexFile.java
+++ b/dx/src/com/android/dx/dex/file/DexFile.java
@@ -18,7 +18,7 @@
 
 import com.android.dex.util.ExceptionWithContext;
 import com.android.dx.dex.DexOptions;
-import static com.android.dx.dex.file.MixedItemSection.SortType;
+import com.android.dx.dex.file.MixedItemSection.SortType;
 import com.android.dx.rop.cst.Constant;
 import com.android.dx.rop.cst.CstBaseMethodRef;
 import com.android.dx.rop.cst.CstEnumRef;
@@ -27,6 +27,7 @@
 import com.android.dx.rop.cst.CstType;
 import com.android.dx.rop.type.Type;
 import com.android.dx.util.ByteArrayAnnotatedOutput;
+
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.Writer;
@@ -241,7 +242,7 @@
      * @return {@code >= 0;} the total file size
      * @throws RuntimeException thrown if the file size is not yet known
      */
-    /*package*/ int getFileSize() {
+    public int getFileSize() {
         if (fileSize < 0) {
             throw new RuntimeException("file size not yet known");
         }
@@ -342,13 +343,13 @@
     /**
      * Gets the type identifiers section.
      *
-     * <p>This is package-scope in order to allow
+     * <p>This is public in order to allow
      * the various {@link Item} instances to add items to the
-     * instance.</p>
+     * instance and help early counting of type ids.</p>
      *
      * @return {@code non-null;} the class identifiers section
      */
-    /*package*/ TypeIdsSection getTypeIds() {
+    public TypeIdsSection getTypeIds() {
         return typeIds;
     }
 
@@ -368,26 +369,26 @@
     /**
      * Gets the field identifiers section.
      *
-     * <p>This is package-scope in order to allow
+     * <p>This is public in order to allow
      * the various {@link Item} instances to add items to the
-     * instance.</p>
+     * instance and help early counting of field ids.</p>
      *
      * @return {@code non-null;} the field identifiers section
      */
-    /*package*/ FieldIdsSection getFieldIds() {
+    public FieldIdsSection getFieldIds() {
         return fieldIds;
     }
 
     /**
      * Gets the method identifiers section.
      *
-     * <p>This is package-scope in order to allow
+     * <p>This is public in order to allow
      * the various {@link Item} instances to add items to the
-     * instance.</p>
+     * instance and help early counting of method ids.</p>
      *
      * @return {@code non-null;} the method identifiers section
      */
-    /*package*/ MethodIdsSection getMethodIds() {
+    public MethodIdsSection getMethodIds() {
         return methodIds;
     }
 
diff --git a/dx/src/com/android/dx/dex/file/MemberIdsSection.java b/dx/src/com/android/dx/dex/file/MemberIdsSection.java
index 9f82f41..bd15df7 100644
--- a/dx/src/com/android/dx/dex/file/MemberIdsSection.java
+++ b/dx/src/com/android/dx/dex/file/MemberIdsSection.java
@@ -17,6 +17,9 @@
 package com.android.dx.dex.file;
 
 import com.android.dex.DexException;
+import com.android.dex.DexFormat;
+import com.android.dx.command.dexer.Main;
+
 import java.util.Formatter;
 import java.util.Map;
 import java.util.TreeMap;
@@ -26,8 +29,6 @@
  * Member (field or method) refs list section of a {@code .dex} file.
  */
 public abstract class MemberIdsSection extends UniformItemSection {
-    /** The largest addressable member is 0xffff, in the dex spec as field@CCCC or meth@CCCC. */
-    private static final int MAX_MEMBERS = 0x10000;
 
     /**
      * Constructs an instance. The file offset is initially unknown.
@@ -45,8 +46,8 @@
     protected void orderItems() {
         int idx = 0;
 
-        if (items().size() > MAX_MEMBERS) {
-            throw new DexException(tooManyMembersMessage());
+        if (items().size() > DexFormat.MAX_MEMBER_IDX + 1) {
+            throw new DexException(getTooManyMembersMessage());
         }
 
         for (Object i : items()) {
@@ -55,7 +56,7 @@
         }
     }
 
-    private String tooManyMembersMessage() {
+    private String getTooManyMembersMessage() {
         Map<String, AtomicInteger> membersByPackage = new TreeMap<String, AtomicInteger>();
         for (Object member : items()) {
             String packageName = ((MemberIdItem) member).getDefiningClass().getPackageName();
@@ -68,12 +69,19 @@
         }
 
         Formatter formatter = new Formatter();
-        String memberType = this instanceof MethodIdsSection ? "methods" : "fields";
-        formatter.format("Too many %s: %d; max is %d. By package:",
-                memberType, items().size(), MAX_MEMBERS);
-        for (Map.Entry<String, AtomicInteger> entry : membersByPackage.entrySet()) {
-            formatter.format("%n%6d %s", entry.getValue().get(), entry.getKey());
+        try {
+            String memberType = this instanceof MethodIdsSection ? "method" : "field";
+            formatter.format("Too many %s references: %d; max is %d.%n" +
+                    Main.getTooManyIdsErrorMessage() + "%n" +
+                    "References by package:",
+                    memberType, items().size(), DexFormat.MAX_MEMBER_IDX + 1);
+            for (Map.Entry<String, AtomicInteger> entry : membersByPackage.entrySet()) {
+                formatter.format("%n%6d %s", entry.getValue().get(), entry.getKey());
+            }
+            return formatter.toString();
+        } finally {
+            formatter.close();
         }
-        return formatter.toString();
     }
+
 }
diff --git a/dx/src/com/android/dx/dex/file/TypeIdsSection.java b/dx/src/com/android/dx/dex/file/TypeIdsSection.java
index 1df4b46..ef47262 100644
--- a/dx/src/com/android/dx/dex/file/TypeIdsSection.java
+++ b/dx/src/com/android/dx/dex/file/TypeIdsSection.java
@@ -16,11 +16,15 @@
 
 package com.android.dx.dex.file;
 
+import com.android.dex.DexException;
+import com.android.dex.DexFormat;
+import com.android.dx.command.dexer.Main;
 import com.android.dx.rop.cst.Constant;
 import com.android.dx.rop.cst.CstType;
 import com.android.dx.rop.type.Type;
 import com.android.dx.util.AnnotatedOutput;
 import com.android.dx.util.Hex;
+
 import java.util.Collection;
 import java.util.TreeMap;
 
@@ -80,8 +84,10 @@
         int sz = typeIds.size();
         int offset = (sz == 0) ? 0 : getFileOffset();
 
-        if (sz > 65536) {
-            throw new UnsupportedOperationException("too many type ids");
+        if (sz > DexFormat.MAX_TYPE_IDX + 1) {
+            throw new DexException("Too many type references: " + sz +
+                    "; max is " + (DexFormat.MAX_TYPE_IDX + 1) + ".\n" +
+                    Main.getTooManyIdsErrorMessage());
         }
 
         if (out.annotates()) {
diff --git a/libdex/Android.mk b/libdex/Android.mk
index 48263c9..7ab0013 100644
--- a/libdex/Android.mk
+++ b/libdex/Android.mk
@@ -50,6 +50,7 @@
 #LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1
 LOCAL_SRC_FILES := $(dex_src_files)
 LOCAL_C_INCLUDES += $(dex_include_files)
+LOCAL_STATIC_LIBRARIES := liblog
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := libdex
 include $(BUILD_STATIC_LIBRARY)
@@ -65,6 +66,7 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(dex_src_files)
 LOCAL_C_INCLUDES += $(dex_include_files)
+LOCAL_STATIC_LIBRARIES := liblog
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := libdex
 include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/libdex/DexSwapVerify.cpp b/libdex/DexSwapVerify.cpp
index 6cc139d..ff47ab5 100644
--- a/libdex/DexSwapVerify.cpp
+++ b/libdex/DexSwapVerify.cpp
@@ -912,8 +912,9 @@
     SWAP_OFFSET4(item->classDataOff);
 
     if ((item->accessFlags & ~ACC_CLASS_MASK) != 0) {
-        ALOGE("Bogus class access flags %x", item->accessFlags);
-        return NULL;
+        // The VM specification says that unknown flags should be ignored.
+        ALOGV("Bogus class access flags %x", item->accessFlags);
+        item->accessFlags &= ACC_CLASS_MASK;
     }
 
     return item + 1;
@@ -1457,8 +1458,9 @@
         }
 
         if ((accessFlags & ~ACC_FIELD_MASK) != 0) {
-            ALOGE("Bogus field access flags %x @ %d", accessFlags, i);
-            return false;
+            // The VM specification says that unknown flags should be ignored.
+            ALOGV("Bogus field access flags %x @ %d", accessFlags, i);
+            field->accessFlags &= ACC_FIELD_MASK;
         }
     }
 
@@ -1487,12 +1489,17 @@
             return false;
         }
 
-        if (((accessFlags & ~ACC_METHOD_MASK) != 0)
-                || (isSynchronized && !allowSynchronized)) {
-            ALOGE("Bogus method access flags %x @ %d", accessFlags, i);
+        if (isSynchronized && !allowSynchronized) {
+            ALOGE("Bogus method access flags (synchronization) %x @ %d", accessFlags, i);
             return false;
         }
 
+        if ((accessFlags & ~ACC_METHOD_MASK) != 0) {
+            // The VM specification says that unknown flags should be ignored.
+            ALOGV("Bogus method access flags %x @ %d", accessFlags, i);
+            method->accessFlags &= ACC_METHOD_MASK;
+        }
+
         if (expectCode) {
             if (method->codeOff == 0) {
                 ALOGE("Unexpected zero code_off for access_flags %x",
diff --git a/libdex/ZipArchive.cpp b/libdex/ZipArchive.cpp
index 1cccc3f..f70a5df 100644
--- a/libdex/ZipArchive.cpp
+++ b/libdex/ZipArchive.cpp
@@ -28,6 +28,7 @@
 #include <errno.h>
 
 #include <JNIHelp.h>        // TEMP_FAILURE_RETRY may or may not be in unistd
+#include <utils/Compat.h>   // For off64_t and lseek64 on Mac
 
 #ifndef O_BINARY
 #define O_BINARY 0
@@ -36,37 +37,47 @@
 /*
  * Zip file constants.
  */
-#define kEOCDSignature      0x06054b50
-#define kEOCDLen            22
-#define kEOCDNumEntries     8               // offset to #of entries in file
-#define kEOCDSize           12              // size of the central directory
-#define kEOCDFileOffset     16              // offset to central directory
+#define kEOCDSignature       0x06054b50
+#define kEOCDLen             22
+#define kEOCDDiskNumber      4               // number of the current disk
+#define kEOCDDiskNumberForCD 6               // disk number with the Central Directory
+#define kEOCDNumEntries      8               // offset to #of entries in file
+#define kEOCDTotalNumEntries 10              // offset to total #of entries in spanned archives
+#define kEOCDSize            12              // size of the central directory
+#define kEOCDFileOffset      16              // offset to central directory
+#define kEOCDCommentSize     20              // offset to the length of the file comment
 
-#define kMaxCommentLen      65535           // longest possible in ushort
-#define kMaxEOCDSearch      (kMaxCommentLen + kEOCDLen)
+#define kMaxCommentLen       65535           // longest possible in ushort
+#define kMaxEOCDSearch       (kMaxCommentLen + kEOCDLen)
 
-#define kLFHSignature       0x04034b50
-#define kLFHLen             30              // excluding variable-len fields
-#define kLFHNameLen         26              // offset to filename length
-#define kLFHExtraLen        28              // offset to extra length
+#define kLFHSignature        0x04034b50
+#define kLFHLen              30              // excluding variable-len fields
+#define kLFHGPBFlags          6              // offset to GPB flags
+#define kLFHNameLen          26              // offset to filename length
+#define kLFHExtraLen         28              // offset to extra length
 
-#define kCDESignature       0x02014b50
-#define kCDELen             46              // excluding variable-len fields
-#define kCDEMethod          10              // offset to compression method
-#define kCDEModWhen         12              // offset to modification timestamp
-#define kCDECRC             16              // offset to entry CRC
-#define kCDECompLen         20              // offset to compressed length
-#define kCDEUncompLen       24              // offset to uncompressed length
-#define kCDENameLen         28              // offset to filename length
-#define kCDEExtraLen        30              // offset to extra length
-#define kCDECommentLen      32              // offset to comment length
-#define kCDELocalOffset     42              // offset to local hdr
+#define kCDESignature        0x02014b50
+#define kCDELen              46              // excluding variable-len fields
+#define kCDEGPBFlags          8              // offset to GPB flags
+#define kCDEMethod           10              // offset to compression method
+#define kCDEModWhen          12              // offset to modification timestamp
+#define kCDECRC              16              // offset to entry CRC
+#define kCDECompLen          20              // offset to compressed length
+#define kCDEUncompLen        24              // offset to uncompressed length
+#define kCDENameLen          28              // offset to filename length
+#define kCDEExtraLen         30              // offset to extra length
+#define kCDECommentLen       32              // offset to comment length
+#define kCDELocalOffset      42              // offset to local hdr
+
+/* General Purpose Bit Flag */
+#define kGPFEncryptedFlag    (1 << 0)
+#define kGPFUnsupportedMask  (kGPFEncryptedFlag)
 
 /*
- * The values we return for ZipEntry use 0 as an invalid value, so we
+ * The values we return for ZipEntryRO use 0 as an invalid value, so we
  * want to adjust the hash table index by a fixed amount.  Using a large
- * value helps insure that people don't mix & match arguments, e.g. with
- * entry indices.
+ * value helps insure that people don't mix & match arguments, e.g. to
+ * findEntryByIndex().
  */
 #define kZipEntryAdj        10000
 
@@ -142,20 +153,54 @@
 }
 
 static int mapCentralDirectory0(int fd, const char* debugFileName,
-        ZipArchive* pArchive, off_t fileLength, size_t readAmount, u1* scanBuf)
+        ZipArchive* pArchive, off64_t fileLength, size_t readAmount, u1* scanBuf)
 {
-    off_t searchStart = fileLength - readAmount;
+    /*
+     * Make sure this is a Zip archive.
+     */
+    if (lseek64(pArchive->mFd, 0, SEEK_SET) != 0) {
+        ALOGW("seek to start failed: %s", strerror(errno));
+        return false;
+    }
 
-    if (lseek(fd, searchStart, SEEK_SET) != searchStart) {
-        ALOGW("Zip: seek %ld failed: %s", (long) searchStart, strerror(errno));
-        return -1;
+    ssize_t actual = TEMP_FAILURE_RETRY(read(pArchive->mFd, scanBuf, sizeof(int32_t)));
+    if (actual != (ssize_t) sizeof(int32_t)) {
+        ALOGI("couldn't read first signature from zip archive: %s", strerror(errno));
+        return false;
     }
-    ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount));
+
+    unsigned int header = get4LE(scanBuf);
+    if (header != kLFHSignature) {
+        ALOGV("Not a Zip archive (found 0x%08x)\n", header);
+        return false;
+    }
+
+    /*
+     * Perform the traditional EOCD snipe hunt.
+     *
+     * We're searching for the End of Central Directory magic number,
+     * which appears at the start of the EOCD block.  It's followed by
+     * 18 bytes of EOCD stuff and up to 64KB of archive comment.  We
+     * need to read the last part of the file into a buffer, dig through
+     * it to find the magic number, parse some values out, and use those
+     * to determine the extent of the CD.
+     *
+     * We start by pulling in the last part of the file.
+     */
+    off64_t searchStart = fileLength - readAmount;
+
+    if (lseek64(pArchive->mFd, searchStart, SEEK_SET) != searchStart) {
+        ALOGW("seek %ld failed: %s\n",  (long) searchStart, strerror(errno));
+        return false;
+    }
+    actual = TEMP_FAILURE_RETRY(read(pArchive->mFd, scanBuf, readAmount));
     if (actual != (ssize_t) readAmount) {
-        ALOGW("Zip: read %zd failed: %s", readAmount, strerror(errno));
-        return -1;
+        ALOGW("Zip: read %zd, expected %zd. Failed: %s\n",
+            actual, readAmount, strerror(errno));
+        return false;
     }
 
+
     /*
      * Scan backward for the EOCD magic.  In an archive without a trailing
      * comment, we'll find it on the first try.  (We may want to consider
@@ -174,7 +219,7 @@
         return -1;
     }
 
-    off_t eocdOffset = searchStart + i;
+    off64_t eocdOffset = searchStart + i;
     const u1* eocdPtr = scanBuf + i;
 
     assert(eocdOffset < fileLength);
@@ -183,28 +228,43 @@
      * Grab the CD offset and size, and the number of entries in the
      * archive.  Verify that they look reasonable.
      */
+    u4 diskNumber = get2LE(eocdPtr + kEOCDDiskNumber);
+    u4 diskWithCentralDir = get2LE(eocdPtr + kEOCDDiskNumberForCD);
     u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries);
-    u4 dirSize = get4LE(eocdPtr + kEOCDSize);
-    u4 dirOffset = get4LE(eocdPtr + kEOCDFileOffset);
+    u4 totalNumEntries = get2LE(eocdPtr + kEOCDTotalNumEntries);
+    u4 centralDirSize = get4LE(eocdPtr + kEOCDSize);
+    u4 centralDirOffset = get4LE(eocdPtr + kEOCDFileOffset);
+    u4 commentSize = get2LE(eocdPtr + kEOCDCommentSize);
 
-    if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) {
-        ALOGW("Zip: bad offsets (dir %ld, size %u, eocd %ld)",
-            (long) dirOffset, dirSize, (long) eocdOffset);
-        return -1;
+    // Verify that they look reasonable.
+    if ((long long) centralDirOffset + (long long) centralDirSize > (long long) eocdOffset) {
+        ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n",
+            (long) centralDirOffset, centralDirSize, (long) eocdOffset);
+        return false;
     }
     if (numEntries == 0) {
-        ALOGW("Zip: empty archive?");
-        return -1;
+        ALOGW("empty archive?\n");
+        return false;
+    } else if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
+        ALOGW("spanned archives not supported");
+        return false;
     }
 
-    ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d",
-        numEntries, dirSize, dirOffset);
+    // Check to see if comment is a sane size
+    if (((size_t) commentSize > (fileLength - kEOCDLen))
+            || (eocdOffset > (fileLength - kEOCDLen) - commentSize)) {
+        ALOGW("comment size runs off end of file");
+        return false;
+    }
+
+    ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
+        numEntries, centralDirSize, centralDirOffset);
 
     /*
      * It all looks good.  Create a mapping for the CD, and set the fields
      * in pArchive.
      */
-    if (sysMapFileSegmentInShmem(fd, dirOffset, dirSize,
+    if (sysMapFileSegmentInShmem(fd, centralDirOffset, centralDirSize,
             &pArchive->mDirectoryMap) != 0)
     {
         ALOGW("Zip: cd map failed");
@@ -212,7 +272,7 @@
     }
 
     pArchive->mNumEntries = numEntries;
-    pArchive->mDirectoryOffset = dirOffset;
+    pArchive->mDirectoryOffset = centralDirOffset;
 
     return 0;
 }
@@ -231,7 +291,7 @@
     /*
      * Get and test file length.
      */
-    off_t fileLength = lseek(fd, 0, SEEK_END);
+    off64_t fileLength = lseek64(fd, 0, SEEK_END);
     if (fileLength < kEOCDLen) {
         ALOGV("Zip: length %ld is too small to be zip", (long) fileLength);
         return -1;
@@ -309,16 +369,31 @@
             goto bail;
         }
 
-        unsigned int fileNameLen, extraLen, commentLen, hash;
-        fileNameLen = get2LE(ptr + kCDENameLen);
+        unsigned int gpbf = get2LE(ptr + kCDEGPBFlags);
+        if ((gpbf & kGPFUnsupportedMask) != 0) {
+            ALOGW("Invalid General Purpose Bit Flag: %d", gpbf);
+            goto bail;
+        }
+
+        unsigned int nameLen, extraLen, commentLen, hash;
+        nameLen = get2LE(ptr + kCDENameLen);
         extraLen = get2LE(ptr + kCDEExtraLen);
         commentLen = get2LE(ptr + kCDECommentLen);
 
-        /* add the CDE filename to the hash table */
-        hash = computeHash((const char*)ptr + kCDELen, fileNameLen);
-        addToHash(pArchive, (const char*)ptr + kCDELen, fileNameLen, hash);
+        const char *name = (const char *) ptr + kCDELen;
 
-        ptr += kCDELen + fileNameLen + extraLen + commentLen;
+        /* Check name for NULL characters */
+        if (memchr(name, 0, nameLen) != NULL) {
+            ALOGW("Filename contains NUL byte");
+            goto bail;
+        }
+
+        /* add the CDE filename to the hash table */
+        hash = computeHash(name, nameLen);
+        addToHash(pArchive, name, nameLen, hash);
+
+        /* We don't care about the comment or extra data. */
+        ptr += kCDELen + nameLen + extraLen + commentLen;
         if ((size_t)(ptr - cdPtr) > cdLength) {
             ALOGW("Zip: bad CD advance (%d vs %zd) at entry %d",
                 (int) (ptr - cdPtr), cdLength, i);
@@ -553,7 +628,13 @@
             return -1;
         }
 
-        off_t dataOffset = localHdrOffset + kLFHLen
+        u4 gpbf = get2LE(lfhBuf + kLFHGPBFlags);
+        if ((gpbf & kGPFUnsupportedMask) != 0) {
+            ALOGW("Invalid General Purpose Bit Flag: %d", gpbf);
+            return -1;
+        }
+
+        off64_t dataOffset = localHdrOffset + kLFHLen
             + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
         if (dataOffset >= cdOffset) {
             ALOGW("Zip: bad data offset %ld in zip", (long) dataOffset);
diff --git a/tests/089-many-methods/expected.txt b/tests/089-many-methods/expected.txt
index fc97b3d..b74e0ee 100644
--- a/tests/089-many-methods/expected.txt
+++ b/tests/089-many-methods/expected.txt
@@ -1,4 +1,6 @@
 
-trouble writing output: Too many fields: 131000; max is 65536. By package:
+trouble writing output: Too many field references: 131000; max is 65536.
+You may try using --multi-dex option.
+References by package:
 131000 default
 build exit status: 2
diff --git a/tests/097-suspend-check/expected.txt b/tests/097-suspend-check/expected.txt
new file mode 100644
index 0000000..07cc825
--- /dev/null
+++ b/tests/097-suspend-check/expected.txt
@@ -0,0 +1,7 @@
+Running (5 seconds) ...
+.
+.
+.
+.
+.
+Done.
diff --git a/tests/097-suspend-check/info.txt b/tests/097-suspend-check/info.txt
new file mode 100644
index 0000000..d89d66a
--- /dev/null
+++ b/tests/097-suspend-check/info.txt
@@ -0,0 +1,2 @@
+To support garbage collection, debugging and profiling the VM must be able to send all threads
+to a safepoint.  This tests the ability of the VM to do this for a tight loop.
diff --git a/tests/097-suspend-check/src/Main.java b/tests/097-suspend-check/src/Main.java
new file mode 100644
index 0000000..d92b9e5
--- /dev/null
+++ b/tests/097-suspend-check/src/Main.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+public class Main {
+    private static final int TEST_TIME = 5;
+
+    public static void main(String[] args) {
+        System.out.println("Running (" + TEST_TIME + " seconds) ...");
+        InfiniteForLoop forLoop = new InfiniteForLoop();
+        InfiniteWhileLoop whileLoop = new InfiniteWhileLoop();
+        InfiniteDoWhileLoop doWhileLoop = new InfiniteDoWhileLoop();
+        MakeGarbage garbage = new MakeGarbage();
+        forLoop.start();
+        whileLoop.start();
+        doWhileLoop.start();
+        garbage.start();
+        for (int i = 0; i < TEST_TIME; i++) {
+          System.gc();
+          System.out.println(".");
+          sleep(1000);
+        }
+        forLoop.stopNow();
+        whileLoop.stopNow();
+        doWhileLoop.stopNow();
+        garbage.stopNow();
+        System.out.println("Done.");
+    }
+
+    public static void sleep(int ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ie) {
+            System.err.println("sleep was interrupted");
+        }
+    }
+}
+
+class InfiniteWhileLoop extends Thread {
+  volatile private boolean keepGoing = true;
+  public void run() {
+    int i = 0;
+    while (keepGoing) {
+      i++;
+    }
+  }
+  public void stopNow() {
+    keepGoing = false;
+  }
+}
+
+class InfiniteDoWhileLoop extends Thread {
+  volatile private boolean keepGoing = true;
+  public void run() {
+    int i = 0;
+    do {
+      i++;
+    } while (keepGoing);
+  }
+  public void stopNow() {
+    keepGoing = false;
+  }
+}
+
+class InfiniteForLoop extends Thread {
+  int count = 100000;
+  volatile private boolean keepGoing = true;
+  public void run() {
+    int i = 0;
+    for (int j = 0; keepGoing; j++) {
+      i += j;
+    }
+  }
+  public void stopNow() {
+    keepGoing = false;
+  }
+}
+
+
+class MakeGarbage extends Thread {
+  volatile private boolean keepGoing = true;
+  public void run() {
+    while (keepGoing) {
+      byte[] garbage = new byte[100000];
+    }
+  }
+  public void stopNow() {
+    keepGoing = false;
+  }
+}
diff --git a/tests/098-native-allocations/expected.txt b/tests/098-native-allocations/expected.txt
new file mode 100644
index 0000000..f75da10
--- /dev/null
+++ b/tests/098-native-allocations/expected.txt
@@ -0,0 +1 @@
+Test complete
diff --git a/tests/098-native-allocations/info.txt b/tests/098-native-allocations/info.txt
new file mode 100644
index 0000000..2e5b88a
--- /dev/null
+++ b/tests/098-native-allocations/info.txt
@@ -0,0 +1,2 @@
+This is a test to verify that native allocation successfully runs
+finalizers and prevents OOM.
diff --git a/tests/098-native-allocations/src/Main.java b/tests/098-native-allocations/src/Main.java
new file mode 100644
index 0000000..87179b5
--- /dev/null
+++ b/tests/098-native-allocations/src/Main.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+import java.lang.reflect.*;
+
+class Main {
+    static Object nativeLock = new Object();
+    static int nativeBytes = 0;
+    static Object runtime;
+    static Method register_native_allocation;
+    static Method register_native_free;
+    static int maxMem = 64 * 1024 * 1024;
+
+    static class NativeAllocation {
+        private int bytes;
+
+        NativeAllocation(int bytes) throws Exception {
+            this.bytes = bytes;
+            register_native_allocation.invoke(runtime, bytes);
+            synchronized (nativeLock) {
+                nativeBytes += bytes;
+                if (nativeBytes > maxMem) {
+                    throw new OutOfMemoryError();
+                }
+            }
+        }
+
+        protected void finalize() throws Exception {
+            synchronized (nativeLock) {
+                nativeBytes -= bytes;
+            }
+            register_native_free.invoke(runtime, bytes);
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        Class<?> vm_runtime = Class.forName("dalvik.system.VMRuntime");
+        Method get_runtime = vm_runtime.getDeclaredMethod("getRuntime");
+        runtime = get_runtime.invoke(null);
+        register_native_allocation = vm_runtime.getDeclaredMethod("registerNativeAllocation", Integer.TYPE);
+        register_native_free = vm_runtime.getDeclaredMethod("registerNativeFree", Integer.TYPE);
+        int count = 16;
+        int size = 512 * 0x400;
+        int allocation_count = 256;
+        NativeAllocation[] allocations = new NativeAllocation[count];
+        for (int i = 0; i < allocation_count; ++i) {
+            allocations[i % count] = new NativeAllocation(size);
+        }
+        System.out.println("Test complete");
+    }
+}
+
diff --git a/tools/jdwpspy/Android.mk b/tools/jdwpspy/Android.mk
deleted file mode 100644
index 2201aab..0000000
--- a/tools/jdwpspy/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2006 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	Main.cpp \
-	Net.cpp \
-	find_JdwpConstants.cpp
-
-LOCAL_C_INCLUDES += \
-	dalvik/vm
-
-LOCAL_MODULE := jdwpspy
-
-include $(BUILD_HOST_EXECUTABLE)
-
diff --git a/tools/jdwpspy/Common.h b/tools/jdwpspy/Common.h
deleted file mode 100644
index ddaba9c..0000000
--- a/tools/jdwpspy/Common.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * jdwpspy common stuff.
- */
-#ifndef _JDWPSPY_COMMON
-#define _JDWPSPY_COMMON
-
-#include <stdio.h>
-#include <sys/types.h>
-
-typedef unsigned char u1;
-typedef unsigned short u2;
-typedef unsigned int u4;
-typedef unsigned long long u8;
-
-#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
-
-#ifndef _JDWP_MISC_INLINE
-# define INLINE extern inline
-#else
-# define INLINE
-#endif
-
-/*
- * Get 1 byte.  (Included to make the code more legible.)
- */
-INLINE u1 get1(unsigned const char* pSrc)
-{
-    return *pSrc;
-}
-
-/*
- * Get 2 big-endian bytes.
- */
-INLINE u2 get2BE(unsigned char const* pSrc)
-{
-    u2 result;
-
-    result = *pSrc++ << 8;
-    result |= *pSrc++;
-
-    return result;
-}
-
-/*
- * Get 4 big-endian bytes.
- */
-INLINE u4 get4BE(unsigned char const* pSrc)
-{
-    u4 result;
-
-    result = *pSrc++ << 24;
-    result |= *pSrc++ << 16;
-    result |= *pSrc++ << 8;
-    result |= *pSrc++;
-
-    return result;
-}
-
-/*
- * Get 8 big-endian bytes.
- */
-INLINE u8 get8BE(unsigned char const* pSrc)
-{
-    u8 result;
-
-    result = (u8) *pSrc++ << 56;
-    result |= (u8) *pSrc++ << 48;
-    result |= (u8) *pSrc++ << 40;
-    result |= (u8) *pSrc++ << 32;
-    result |= (u8) *pSrc++ << 24;
-    result |= (u8) *pSrc++ << 16;
-    result |= (u8) *pSrc++ << 8;
-    result |= (u8) *pSrc++;
-
-    return result;
-}
-
-
-/*
- * Start here.
- */
-int run(const char* connectHost, int connectPort, int listenPort);
-
-/*
- * Print a hex dump to the specified file pointer.
- *
- * "local" mode prints a hex dump starting from offset 0 (roughly equivalent
- * to "xxd -g1").
- *
- * "mem" mode shows the actual memory address, and will offset the start
- * so that the low nibble of the address is always zero.
- */
-typedef enum { kHexDumpLocal, kHexDumpMem } HexDumpMode;
-void printHexDump(const void* vaddr, size_t length);
-void printHexDump2(const void* vaddr, size_t length, const char* prefix);
-void printHexDumpEx(FILE* fp, const void* vaddr, size_t length,
-    HexDumpMode mode, const char* prefix);
-
-#endif /*_JDWPSPY_COMMON*/
diff --git a/tools/jdwpspy/Main.cpp b/tools/jdwpspy/Main.cpp
deleted file mode 100644
index b53f99b..0000000
--- a/tools/jdwpspy/Main.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * JDWP spy.
- */
-#define _JDWP_MISC_INLINE
-#include "Common.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <assert.h>
-#include <ctype.h>
-
-static const char gHexDigit[] = "0123456789abcdef";
-
-/*
- * Print a hex dump.  Just hands control off to the fancy version.
- */
-void printHexDump(const void* vaddr, size_t length)
-{
-    printHexDumpEx(stdout, vaddr, length, kHexDumpLocal, "");
-}
-void printHexDump2(const void* vaddr, size_t length, const char* prefix)
-{
-    printHexDumpEx(stdout, vaddr, length, kHexDumpLocal, prefix);
-}
-
-/*
- * Print a hex dump in this format:
- *
-01234567: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef\n
- */
-void printHexDumpEx(FILE* fp, const void* vaddr, size_t length,
-    HexDumpMode mode, const char* prefix)
-{
-    const unsigned char* addr = reinterpret_cast<const unsigned char*>(vaddr);
-    char out[77];       /* exact fit */
-    uintptr_t offset;   /* offset to show while printing */
-    char* hex;
-    char* asc;
-    int gap;
-
-    if (mode == kHexDumpLocal)
-        offset = 0;
-    else
-        offset = (uintptr_t) addr;
-
-    memset(out, ' ', sizeof(out)-1);
-    out[8] = ':';
-    out[sizeof(out)-2] = '\n';
-    out[sizeof(out)-1] = '\0';
-
-    gap = (int) offset & 0x0f;
-    while (length) {
-        unsigned int lineOffset = offset & ~0x0f;
-        char* hex = out;
-        char* asc = out + 59;
-
-        for (int i = 0; i < 8; i++) {
-            *hex++ = gHexDigit[lineOffset >> 28];
-            lineOffset <<= 4;
-        }
-        hex++;
-        hex++;
-
-        int count = ((int)length > 16-gap) ? 16-gap : (int) length; /* cap length */
-        assert(count != 0);
-        assert(count+gap <= 16);
-
-        if (gap) {
-            /* only on first line */
-            hex += gap * 3;
-            asc += gap;
-        }
-
-        int i;
-        for (i = gap ; i < count+gap; i++) {
-            *hex++ = gHexDigit[*addr >> 4];
-            *hex++ = gHexDigit[*addr & 0x0f];
-            hex++;
-            if (isprint(*addr))
-                *asc++ = *addr;
-            else
-                *asc++ = '.';
-            addr++;
-        }
-        for ( ; i < 16; i++) {
-            /* erase extra stuff; only happens on last line */
-            *hex++ = ' ';
-            *hex++ = ' ';
-            hex++;
-            *asc++ = ' ';
-        }
-
-        fprintf(fp, "%s%s", prefix, out);
-
-        gap = 0;
-        length -= count;
-        offset += count;
-    }
-}
-
-
-/*
- * Explain it.
- */
-static void usage(const char* progName)
-{
-    fprintf(stderr, "Usage: %s VM-port [debugger-listen-port]\n\n", progName);
-    fprintf(stderr,
-"When a debugger connects to the debugger-listen-port, jdwpspy will connect\n");
-    fprintf(stderr, "to the VM on the VM-port.\n");
-}
-
-/*
- * Parse args.
- */
-int main(int argc, char* argv[])
-{
-    if (argc < 2 || argc > 3) {
-        usage("jdwpspy");
-        return 2;
-    }
-
-    setvbuf(stdout, NULL, _IONBF, 0);
-
-    /* may want this to be host:port */
-    int connectPort = atoi(argv[1]);
-
-    int listenPort;
-    if (argc > 2)
-        listenPort = atoi(argv[2]);
-    else
-        listenPort = connectPort + 1;
-
-    int cc = run("localhost", connectPort, listenPort);
-
-    return (cc != 0);
-}
diff --git a/tools/jdwpspy/Net.cpp b/tools/jdwpspy/Net.cpp
deleted file mode 100644
index b923006..0000000
--- a/tools/jdwpspy/Net.cpp
+++ /dev/null
@@ -1,748 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * JDWP spy.  This is a rearranged version of the JDWP code from the VM.
- */
-#include "Common.h"
-#include "jdwp/JdwpConstants.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <time.h>
-#include <errno.h>
-#include <assert.h>
-
-#define kInputBufferSize    (256*1024)
-
-#define kMagicHandshakeLen  14      /* "JDWP-Handshake" */
-#define kJDWPHeaderLen      11
-#define kJDWPFlagReply      0x80
-
-
-/*
- * Information about the remote end.
- */
-typedef struct Peer {
-    char    label[2];           /* 'D' or 'V' */
-
-    int     sock;
-    unsigned char   inputBuffer[kInputBufferSize];
-    int     inputCount;
-
-    bool    awaitingHandshake;  /* waiting for "JDWP-Handshake" */
-} Peer;
-
-
-/*
- * Network state.
- */
-typedef struct NetState {
-    /* listen here for connection from debugger */
-    int     listenSock;
-
-    /* connect here to contact VM */
-    struct in_addr vmAddr;
-    short   vmPort;
-
-    Peer    dbg;
-    Peer    vm;
-} NetState;
-
-/*
- * Function names.
- */
-typedef struct {
-    u1  cmdSet;
-    u1  cmd;
-    const char* descr;
-} JdwpHandlerMap;
-
-/*
- * Map commands to names.
- *
- * Command sets 0-63 are incoming requests, 64-127 are outbound requests,
- * and 128-256 are vendor-defined.
- */
-static const JdwpHandlerMap gHandlerMap[] = {
-    /* VirtualMachine command set (1) */
-    { 1,    1,  "VirtualMachine.Version" },
-    { 1,    2,  "VirtualMachine.ClassesBySignature" },
-    { 1,    3,  "VirtualMachine.AllClasses" },
-    { 1,    4,  "VirtualMachine.AllThreads" },
-    { 1,    5,  "VirtualMachine.TopLevelThreadGroups" },
-    { 1,    6,  "VirtualMachine.Dispose" },
-    { 1,    7,  "VirtualMachine.IDSizes" },
-    { 1,    8,  "VirtualMachine.Suspend" },
-    { 1,    9,  "VirtualMachine.Resume" },
-    { 1,    10, "VirtualMachine.Exit" },
-    { 1,    11, "VirtualMachine.CreateString" },
-    { 1,    12, "VirtualMachine.Capabilities" },
-    { 1,    13, "VirtualMachine.ClassPaths" },
-    { 1,    14, "VirtualMachine.DisposeObjects" },
-    { 1,    15, "VirtualMachine.HoldEvents" },
-    { 1,    16, "VirtualMachine.ReleaseEvents" },
-    { 1,    17, "VirtualMachine.CapabilitiesNew" },
-    { 1,    18, "VirtualMachine.RedefineClasses" },
-    { 1,    19, "VirtualMachine.SetDefaultStratum" },
-    { 1,    20, "VirtualMachine.AllClassesWithGeneric"},
-    { 1,    21, "VirtualMachine.InstanceCounts"},
-
-    /* ReferenceType command set (2) */
-    { 2,    1,  "ReferenceType.Signature" },
-    { 2,    2,  "ReferenceType.ClassLoader" },
-    { 2,    3,  "ReferenceType.Modifiers" },
-    { 2,    4,  "ReferenceType.Fields" },
-    { 2,    5,  "ReferenceType.Methods" },
-    { 2,    6,  "ReferenceType.GetValues" },
-    { 2,    7,  "ReferenceType.SourceFile" },
-    { 2,    8,  "ReferenceType.NestedTypes" },
-    { 2,    9,  "ReferenceType.Status" },
-    { 2,    10, "ReferenceType.Interfaces" },
-    { 2,    11, "ReferenceType.ClassObject" },
-    { 2,    12, "ReferenceType.SourceDebugExtension" },
-    { 2,    13, "ReferenceType.SignatureWithGeneric" },
-    { 2,    14, "ReferenceType.FieldsWithGeneric" },
-    { 2,    15, "ReferenceType.MethodsWithGeneric" },
-    { 2,    16, "ReferenceType.Instances" },
-    { 2,    17, "ReferenceType.ClassFileVersion" },
-    { 2,    18, "ReferenceType.ConstantPool" },
-
-    /* ClassType command set (3) */
-    { 3,    1,  "ClassType.Superclass" },
-    { 3,    2,  "ClassType.SetValues" },
-    { 3,    3,  "ClassType.InvokeMethod" },
-    { 3,    4,  "ClassType.NewInstance" },
-
-    /* ArrayType command set (4) */
-    { 4,    1,  "ArrayType.NewInstance" },
-
-    /* InterfaceType command set (5) */
-
-    /* Method command set (6) */
-    { 6,    1,  "Method.LineTable" },
-    { 6,    2,  "Method.VariableTable" },
-    { 6,    3,  "Method.Bytecodes" },
-    { 6,    4,  "Method.IsObsolete" },
-    { 6,    5,  "Method.VariableTableWithGeneric" },
-
-    /* Field command set (8) */
-
-    /* ObjectReference command set (9) */
-    { 9,    1,  "ObjectReference.ReferenceType" },
-    { 9,    2,  "ObjectReference.GetValues" },
-    { 9,    3,  "ObjectReference.SetValues" },
-    { 9,    4,  "ObjectReference.UNUSED" },
-    { 9,    5,  "ObjectReference.MonitorInfo" },
-    { 9,    6,  "ObjectReference.InvokeMethod" },
-    { 9,    7,  "ObjectReference.DisableCollection" },
-    { 9,    8,  "ObjectReference.EnableCollection" },
-    { 9,    9,  "ObjectReference.IsCollected" },
-    { 9,    10, "ObjectReference.ReferringObjects" },
-
-    /* StringReference command set (10) */
-    { 10,   1,  "StringReference.Value" },
-
-    /* ThreadReference command set (11) */
-    { 11,   1,  "ThreadReference.Name" },
-    { 11,   2,  "ThreadReference.Suspend" },
-    { 11,   3,  "ThreadReference.Resume" },
-    { 11,   4,  "ThreadReference.Status" },
-    { 11,   5,  "ThreadReference.ThreadGroup" },
-    { 11,   6,  "ThreadReference.Frames" },
-    { 11,   7,  "ThreadReference.FrameCount" },
-    { 11,   8,  "ThreadReference.OwnedMonitors" },
-    { 11,   9,  "ThreadReference.CurrentContendedMonitor" },
-    { 11,   10, "ThreadReference.Stop" },
-    { 11,   11, "ThreadReference.Interrupt" },
-    { 11,   12, "ThreadReference.SuspendCount" },
-    { 11,   13, "ThreadReference.OwnedMonitorsStackDepthInfo" },
-    { 11,   14, "ThreadReference.ForceEarlyReturn" },
-
-    /* ThreadGroupReference command set (12) */
-    { 12,   1,  "ThreadGroupReference.Name" },
-    { 12,   2,  "ThreadGroupReference.Parent" },
-    { 12,   3,  "ThreadGroupReference.Children" },
-
-    /* ArrayReference command set (13) */
-    { 13,   1,  "ArrayReference.Length" },
-    { 13,   2,  "ArrayReference.GetValues" },
-    { 13,   3,  "ArrayReference.SetValues" },
-
-    /* ClassLoaderReference command set (14) */
-    { 14,   1,  "ArrayReference.VisibleClasses" },
-
-    /* EventRequest command set (15) */
-    { 15,   1,  "EventRequest.Set" },
-    { 15,   2,  "EventRequest.Clear" },
-    { 15,   3,  "EventRequest.ClearAllBreakpoints" },
-
-    /* StackFrame command set (16) */
-    { 16,   1,  "StackFrame.GetValues" },
-    { 16,   2,  "StackFrame.SetValues" },
-    { 16,   3,  "StackFrame.ThisObject" },
-    { 16,   4,  "StackFrame.PopFrames" },
-
-    /* ClassObjectReference command set (17) */
-    { 17,   1,  "ClassObjectReference.ReflectedType" },
-
-    /* Event command set (64) */
-    { 64,  100, "Event.Composite" },
-
-    /* DDMS */
-    { 199,  1,  "DDMS.Chunk" },
-};
-
-/*
- * Look up a command's name.
- */
-static const char* getCommandName(int cmdSet, int cmd)
-{
-    for (int i = 0; i < (int) NELEM(gHandlerMap); i++) {
-        if (gHandlerMap[i].cmdSet == cmdSet &&
-            gHandlerMap[i].cmd == cmd)
-        {
-            return gHandlerMap[i].descr;
-        }
-    }
-
-    return "?UNKNOWN?";
-}
-
-
-void jdwpNetFree(NetState* netState);       /* fwd */
-
-/*
- * Allocate state structure and bind to the listen port.
- *
- * Returns 0 on success.
- */
-NetState* jdwpNetStartup(unsigned short listenPort, const char* connectHost,
-    unsigned short connectPort)
-{
-    NetState* netState = (NetState*) malloc(sizeof(*netState));
-    memset(netState, 0, sizeof(*netState));
-    netState->listenSock = -1;
-    netState->dbg.sock = netState->vm.sock = -1;
-
-    strcpy(netState->dbg.label, "D");
-    strcpy(netState->vm.label, "V");
-
-    /*
-     * Set up a socket to listen for connections from the debugger.
-     */
-
-    netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if (netState->listenSock < 0) {
-        fprintf(stderr, "Socket create failed: %s\n", strerror(errno));
-        goto fail;
-    }
-
-    /* allow immediate re-use if we die */
-    {
-        int one = 1;
-        if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one,
-                sizeof(one)) < 0)
-        {
-            fprintf(stderr, "setsockopt(SO_REUSEADDR) failed: %s\n",
-                strerror(errno));
-            goto fail;
-        }
-    }
-
-    struct sockaddr_in addr;
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(listenPort);
-    addr.sin_addr.s_addr = INADDR_ANY;
-
-    if (bind(netState->listenSock, (struct sockaddr*) &addr, sizeof(addr)) != 0)
-    {
-        fprintf(stderr, "attempt to bind to port %u failed: %s\n",
-            listenPort, strerror(errno));
-        goto fail;
-    }
-
-    fprintf(stderr, "+++ bound to port %u\n", listenPort);
-
-    if (listen(netState->listenSock, 5) != 0) {
-        fprintf(stderr, "Listen failed: %s\n", strerror(errno));
-        goto fail;
-    }
-
-    /*
-     * Do the hostname lookup for the VM.
-     */
-    struct hostent* pHost;
-
-    pHost = gethostbyname(connectHost);
-    if (pHost == NULL) {
-        fprintf(stderr, "Name lookup of '%s' failed: %s\n",
-            connectHost, strerror(h_errno));
-        goto fail;
-    }
-
-    netState->vmAddr = *((struct in_addr*) pHost->h_addr_list[0]);
-    netState->vmPort = connectPort;
-
-    fprintf(stderr, "+++ connect host resolved to %s\n",
-        inet_ntoa(netState->vmAddr));
-
-    return netState;
-
-fail:
-    jdwpNetFree(netState);
-    return NULL;
-}
-
-/*
- * Shut down JDWP listener.  Don't free state.
- *
- * Note that "netState" may be partially initialized if "startup" failed.
- */
-void jdwpNetShutdown(NetState* netState)
-{
-    int listenSock = netState->listenSock;
-    int dbgSock = netState->dbg.sock;
-    int vmSock = netState->vm.sock;
-
-    /* clear these out so it doesn't wake up and try to reuse them */
-    /* (important when multi-threaded) */
-    netState->listenSock = netState->dbg.sock = netState->vm.sock = -1;
-
-    if (listenSock >= 0) {
-        shutdown(listenSock, SHUT_RDWR);
-        close(listenSock);
-    }
-    if (dbgSock >= 0) {
-        shutdown(dbgSock, SHUT_RDWR);
-        close(dbgSock);
-    }
-    if (vmSock >= 0) {
-        shutdown(vmSock, SHUT_RDWR);
-        close(vmSock);
-    }
-}
-
-/*
- * Shut down JDWP listener and free its state.
- */
-void jdwpNetFree(NetState* netState)
-{
-    if (netState == NULL)
-        return;
-
-    jdwpNetShutdown(netState);
-    free(netState);
-}
-
-/*
- * Disable the TCP Nagle algorithm, which delays transmission of outbound
- * packets until the previous transmissions have been acked.  JDWP does a
- * lot of back-and-forth with small packets, so this may help.
- */
-static int setNoDelay(int fd)
-{
-    int cc, on = 1;
-
-    cc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
-    assert(cc == 0);
-    return cc;
-}
-
-/*
- * Accept a connection.  This will block waiting for somebody to show up.
- */
-bool jdwpAcceptConnection(NetState* netState)
-{
-    struct sockaddr_in addr;
-    socklen_t addrlen;
-    int sock;
-
-    if (netState->listenSock < 0)
-        return false;       /* you're not listening! */
-
-    assert(netState->dbg.sock < 0);     /* must not already be talking */
-
-    addrlen = sizeof(addr);
-    do {
-        sock = accept(netState->listenSock, (struct sockaddr*) &addr, &addrlen);
-        if (sock < 0 && errno != EINTR) {
-            fprintf(stderr, "accept failed: %s\n", strerror(errno));
-            return false;
-        }
-    } while (sock < 0);
-
-    fprintf(stderr, "+++ accepted connection from %s:%u\n",
-        inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
-
-    netState->dbg.sock = sock;
-    netState->dbg.awaitingHandshake = true;
-    netState->dbg.inputCount = 0;
-
-    setNoDelay(sock);
-
-    return true;
-}
-
-/*
- * Close the connections to the debugger and VM.
- *
- * Reset the state so we're ready to receive a new connection.
- */
-void jdwpCloseConnection(NetState* netState)
-{
-    if (netState->dbg.sock >= 0) {
-        fprintf(stderr, "+++ closing connection to debugger\n");
-        close(netState->dbg.sock);
-        netState->dbg.sock = -1;
-    }
-    if (netState->vm.sock >= 0) {
-        fprintf(stderr, "+++ closing connection to vm\n");
-        close(netState->vm.sock);
-        netState->vm.sock = -1;
-    }
-}
-
-/*
- * Figure out if we have a full packet in the buffer.
- */
-static bool haveFullPacket(Peer* pPeer)
-{
-    long length;
-
-    if (pPeer->awaitingHandshake)
-        return (pPeer->inputCount >= kMagicHandshakeLen);
-
-    if (pPeer->inputCount < 4)
-        return false;
-
-    length = get4BE(pPeer->inputBuffer);
-    return (pPeer->inputCount >= length);
-}
-
-/*
- * Consume bytes from the buffer.
- *
- * This would be more efficient with a circular buffer.  However, we're
- * usually only going to find one packet, which is trivial to handle.
- */
-static void consumeBytes(Peer* pPeer, int count)
-{
-    assert(count > 0);
-    assert(count <= pPeer->inputCount);
-
-    if (count == pPeer->inputCount) {
-        pPeer->inputCount = 0;
-        return;
-    }
-
-    memmove(pPeer->inputBuffer, pPeer->inputBuffer + count,
-        pPeer->inputCount - count);
-    pPeer->inputCount -= count;
-}
-
-/*
- * Get the current time.
- */
-static void getCurrentTime(int* pMin, int* pSec)
-{
-    time_t now;
-    struct tm* ptm;
-
-    now = time(NULL);
-    ptm = localtime(&now);
-    *pMin = ptm->tm_min;
-    *pSec = ptm->tm_sec;
-}
-
-/*
- * Dump the contents of a packet to stdout.
- */
-static void dumpPacket(const unsigned char* packetBuf, const char* srcName,
-    const char* dstName)
-{
-    const unsigned char* buf = packetBuf;
-    char prefix[3];
-    u4 length, id;
-    u1 flags, cmdSet=0, cmd=0;
-    JdwpError error = ERR_NONE;
-    bool reply;
-    int dataLen;
-
-    length = get4BE(buf+0);
-    id = get4BE(buf+4);
-    flags = get1(buf+8);
-    if ((flags & kJDWPFlagReply) != 0) {
-        reply = true;
-        error = static_cast<JdwpError>(get2BE(buf+9));
-    } else {
-        reply = false;
-        cmdSet = get1(buf+9);
-        cmd = get1(buf+10);
-    }
-
-    buf += kJDWPHeaderLen;
-    dataLen = length - (buf - packetBuf);
-
-    if (!reply) {
-        prefix[0] = srcName[0];
-        prefix[1] = '>';
-    } else {
-        prefix[0] = dstName[0];
-        prefix[1] = '<';
-    }
-    prefix[2] = '\0';
-
-    int min, sec;
-    getCurrentTime(&min, &sec);
-
-    if (!reply) {
-        printf("%s REQUEST dataLen=%-5u id=0x%08x flags=0x%02x cmd=%d/%d [%02d:%02d]\n",
-            prefix, dataLen, id, flags, cmdSet, cmd, min, sec);
-        printf("%s   --> %s\n", prefix, getCommandName(cmdSet, cmd));
-    } else {
-        printf("%s REPLY   dataLen=%-5u id=0x%08x flags=0x%02x err=%d (%s) [%02d:%02d]\n",
-            prefix, dataLen, id, flags, error, dvmJdwpErrorStr(error), min,sec);
-    }
-    if (dataLen > 0)
-        printHexDump2(buf, dataLen, prefix);
-    printf("%s ----------\n", prefix);
-}
-
-/*
- * Handle a packet.  Returns "false" if we encounter a connection-fatal error.
- */
-static bool handlePacket(Peer* pDst, Peer* pSrc)
-{
-    const unsigned char* buf = pSrc->inputBuffer;
-    u4 length;
-    u1 flags;
-    int cc;
-
-    length = get4BE(buf+0);
-    flags = get1(buf+9);
-
-    assert((int) length <= pSrc->inputCount);
-
-    dumpPacket(buf, pSrc->label, pDst->label);
-
-    cc = write(pDst->sock, buf, length);
-    if (cc != (int) length) {
-        fprintf(stderr, "Failed sending packet: %s\n", strerror(errno));
-        return false;
-    }
-    /*printf("*** wrote %d bytes from %c to %c\n",
-        cc, pSrc->label[0], pDst->label[0]);*/
-
-    consumeBytes(pSrc, length);
-    return true;
-}
-
-/*
- * Handle incoming data.  If we have a full packet in the buffer, process it.
- */
-static bool handleIncoming(Peer* pWritePeer, Peer* pReadPeer)
-{
-    if (haveFullPacket(pReadPeer)) {
-        if (pReadPeer->awaitingHandshake) {
-            printf("Handshake [%c]: %.14s\n",
-                pReadPeer->label[0], pReadPeer->inputBuffer);
-            if (write(pWritePeer->sock, pReadPeer->inputBuffer,
-                    kMagicHandshakeLen) != kMagicHandshakeLen)
-            {
-                fprintf(stderr,
-                    "+++ [%c] handshake write failed\n", pReadPeer->label[0]);
-                goto fail;
-            }
-            consumeBytes(pReadPeer, kMagicHandshakeLen);
-            pReadPeer->awaitingHandshake = false;
-        } else {
-            if (!handlePacket(pWritePeer, pReadPeer))
-                goto fail;
-        }
-    } else {
-        /*printf("*** %c not full yet\n", pReadPeer->label[0]);*/
-    }
-
-    return true;
-
-fail:
-    return false;
-}
-
-/*
- * Process incoming data.  If no data is available, this will block until
- * some arrives.
- *
- * Returns "false" on error (indicating that the connection has been severed).
- */
-bool jdwpProcessIncoming(NetState* netState)
-{
-    int cc;
-
-    assert(netState->dbg.sock >= 0);
-    assert(netState->vm.sock >= 0);
-
-    while (!haveFullPacket(&netState->dbg) && !haveFullPacket(&netState->vm)) {
-        /* read some more */
-        int highFd;
-        fd_set readfds;
-
-        highFd = (netState->dbg.sock > netState->vm.sock) ?
-            netState->dbg.sock+1 : netState->vm.sock+1;
-        FD_ZERO(&readfds);
-        FD_SET(netState->dbg.sock, &readfds);
-        FD_SET(netState->vm.sock, &readfds);
-
-        errno = 0;
-        cc = select(highFd, &readfds, NULL, NULL, NULL);
-        if (cc < 0) {
-            if (errno == EINTR) {
-                fprintf(stderr, "+++ EINTR on select\n");
-                continue;
-            }
-            fprintf(stderr, "+++ select failed: %s\n", strerror(errno));
-            goto fail;
-        }
-
-        if (FD_ISSET(netState->dbg.sock, &readfds)) {
-            cc = read(netState->dbg.sock,
-                netState->dbg.inputBuffer + netState->dbg.inputCount,
-                sizeof(netState->dbg.inputBuffer) - netState->dbg.inputCount);
-            if (cc < 0) {
-                if (errno == EINTR) {
-                    fprintf(stderr, "+++ EINTR on read\n");
-                    continue;
-                }
-                fprintf(stderr, "+++ dbg read failed: %s\n", strerror(errno));
-                goto fail;
-            }
-            if (cc == 0) {
-                if (sizeof(netState->dbg.inputBuffer) ==
-                        netState->dbg.inputCount)
-                    fprintf(stderr, "+++ debugger sent huge message\n");
-                else
-                    fprintf(stderr, "+++ debugger disconnected\n");
-                goto fail;
-            }
-
-            /*printf("*** %d bytes from dbg\n", cc);*/
-            netState->dbg.inputCount += cc;
-        }
-
-        if (FD_ISSET(netState->vm.sock, &readfds)) {
-            cc = read(netState->vm.sock,
-                netState->vm.inputBuffer + netState->vm.inputCount,
-                sizeof(netState->vm.inputBuffer) - netState->vm.inputCount);
-            if (cc < 0) {
-                if (errno == EINTR) {
-                    fprintf(stderr, "+++ EINTR on read\n");
-                    continue;
-                }
-                fprintf(stderr, "+++ vm read failed: %s\n", strerror(errno));
-                goto fail;
-            }
-            if (cc == 0) {
-                if (sizeof(netState->vm.inputBuffer) ==
-                        netState->vm.inputCount)
-                    fprintf(stderr, "+++ vm sent huge message\n");
-                else
-                    fprintf(stderr, "+++ vm disconnected\n");
-                goto fail;
-            }
-
-            /*printf("*** %d bytes from vm\n", cc);*/
-            netState->vm.inputCount += cc;
-        }
-    }
-
-    if (!handleIncoming(&netState->dbg, &netState->vm))
-        goto fail;
-    if (!handleIncoming(&netState->vm, &netState->dbg))
-        goto fail;
-
-    return true;
-
-fail:
-    jdwpCloseConnection(netState);
-    return false;
-}
-
-/*
- * Connect to the VM.
- */
-bool jdwpConnectToVm(NetState* netState)
-{
-    struct sockaddr_in addr;
-    int sock = -1;
-
-    sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if (sock < 0) {
-        fprintf(stderr, "Socket create failed: %s\n", strerror(errno));
-        goto fail;
-    }
-
-    addr.sin_family = AF_INET;
-    addr.sin_addr = netState->vmAddr;
-    addr.sin_port = htons(netState->vmPort);
-    if (connect(sock, (struct sockaddr*) &addr, sizeof(addr)) != 0) {
-        fprintf(stderr, "Connection to %s:%u failed: %s\n",
-            inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), strerror(errno));
-        goto fail;
-    }
-    fprintf(stderr, "+++ connected to VM %s:%u\n",
-        inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
-
-    netState->vm.sock = sock;
-    netState->vm.awaitingHandshake = true;
-    netState->vm.inputCount = 0;
-
-    setNoDelay(netState->vm.sock);
-    return true;
-
-fail:
-    if (sock >= 0)
-        close(sock);
-    return false;
-}
-
-/*
- * Establish network connections and start things running.
- *
- * We wait for a new connection from the debugger.  When one arrives we
- * open a connection to the VM.  If one side or the other goes away, we
- * drop both ends and go back to listening.
- */
-int run(const char* connectHost, int connectPort, int listenPort)
-{
-    NetState* state;
-
-    state = jdwpNetStartup(listenPort, connectHost, connectPort);
-    if (state == NULL)
-        return -1;
-
-    while (true) {
-        if (!jdwpAcceptConnection(state))
-            break;
-
-        if (jdwpConnectToVm(state)) {
-            while (true) {
-                if (!jdwpProcessIncoming(state))
-                    break;
-            }
-        }
-
-        jdwpCloseConnection(state);
-    }
-
-    jdwpNetFree(state);
-
-    return 0;
-}
diff --git a/tools/jdwpspy/find_JdwpConstants.cpp b/tools/jdwpspy/find_JdwpConstants.cpp
deleted file mode 100644
index 57b7dbb..0000000
--- a/tools/jdwpspy/find_JdwpConstants.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "jdwp/JdwpConstants.cpp"
diff --git a/vm/Globals.h b/vm/Globals.h
index d4076e9..29f7356 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -90,6 +90,7 @@
     size_t      heapStartingSize;
     size_t      heapMaximumSize;
     size_t      heapGrowthLimit;
+    bool        lowMemoryMode;
     double      heapTargetUtilization;
     size_t      heapMinFree;
     size_t      heapMaxFree;
@@ -267,6 +268,7 @@
     ClassObject* classJavaLangReflectMethod;
     ClassObject* classJavaLangReflectMethodArray;
     ClassObject* classJavaLangReflectProxy;
+    ClassObject* classJavaLangSystem;
     ClassObject* classJavaNioDirectByteBuffer;
     ClassObject* classLibcoreReflectAnnotationFactory;
     ClassObject* classLibcoreReflectAnnotationMember;
@@ -406,6 +408,9 @@
     /* field offsets - java.lang.reflect.Proxy */
     int         offJavaLangReflectProxy_h;
 
+    /* direct method pointer - java.lang.System.runFinalization */
+    Method*     methJavaLangSystem_runFinalization;
+
     /* field offsets - java.io.FileDescriptor */
     int         offJavaIoFileDescriptor_descriptor;
 
@@ -736,6 +741,8 @@
 
 #if defined(WITH_JIT)
 
+#define DEFAULT_CODE_CACHE_SIZE 0xffffffff
+
 /* Trace profiling modes.  Ordering matters - off states before on states */
 enum TraceProfilingModes {
     kTraceProfilingDisabled = 0,      // Not profiling
@@ -800,7 +807,7 @@
     /* How many entries in the JitEntryTable are in use */
     unsigned int jitTableEntriesUsed;
 
-    /* Bytes allocated for the code cache */
+    /* Max bytes allocated for the code cache.  Rough rule of thumb: 1K per 1M of system RAM */
     unsigned int codeCacheSize;
 
     /* Trigger for trace selection */
diff --git a/vm/Init.cpp b/vm/Init.cpp
index 4e547d3..136e1cb 100644
--- a/vm/Init.cpp
+++ b/vm/Init.cpp
@@ -140,6 +140,7 @@
                        "[,hexopvalue[-endvalue]]*\n");
     dvmFprintf(stderr, "  -Xincludeselectedmethod\n");
     dvmFprintf(stderr, "  -Xjitthreshold:decimalvalue\n");
+    dvmFprintf(stderr, "  -Xjitcodecachesize:decimalvalueofkbytes\n");
     dvmFprintf(stderr, "  -Xjitblocking\n");
     dvmFprintf(stderr, "  -Xjitmethod:signature[,signature]* "
                        "(eg Ljava/lang/String\\;replace)\n");
@@ -941,6 +942,8 @@
                 dvmFprintf(stderr, "Invalid -XX:HeapMaxFree option '%s'\n", argv[i]);
                 return -1;
             }
+        } else if (strcmp(argv[i], "-XX:LowMemoryMode") == 0) {
+          gDvm.lowMemoryMode = true;
         } else if (strncmp(argv[i], "-XX:HeapTargetUtilization=", 26) == 0) {
             const char* start = argv[i] + 26;
             const char* end = start;
@@ -1120,6 +1123,11 @@
           gDvmJit.blockingMode = true;
         } else if (strncmp(argv[i], "-Xjitthreshold:", 15) == 0) {
           gDvmJit.threshold = atoi(argv[i] + 15);
+        } else if (strncmp(argv[i], "-Xjitcodecachesize:", 19) == 0) {
+          gDvmJit.codeCacheSize = atoi(argv[i] + 19) * 1024;
+          if (gDvmJit.codeCacheSize == 0) {
+            gDvm.executionMode = kExecutionModeInterpFast;
+          }
         } else if (strncmp(argv[i], "-Xincludeselectedop", 19) == 0) {
           gDvmJit.includeSelectedOp = true;
         } else if (strncmp(argv[i], "-Xincludeselectedmethod", 23) == 0) {
@@ -1235,6 +1243,7 @@
     gDvm.heapStartingSize = 2 * 1024 * 1024;  // Spec says 16MB; too big for us.
     gDvm.heapMaximumSize = 16 * 1024 * 1024;  // Spec says 75% physical mem
     gDvm.heapGrowthLimit = 0;  // 0 means no growth limit
+    gDvm.lowMemoryMode = false;
     gDvm.stackSize = kDefaultStackSize;
     gDvm.mainThreadStackSize = kDefaultStackSize;
     // When the heap is less than the maximum or growth limited size,
@@ -1275,6 +1284,7 @@
     gDvmJit.includeSelectedOffset = false;
     gDvmJit.methodTable = NULL;
     gDvmJit.classTable = NULL;
+    gDvmJit.codeCacheSize = DEFAULT_CODE_CACHE_SIZE;
 
     gDvm.constInit = false;
     gDvm.commonInit = false;
@@ -1700,7 +1710,7 @@
     const char* target_base = getenv("EMULATED_STORAGE_TARGET");
     if (target_base != NULL) {
         if (mount("tmpfs", target_base, "tmpfs", MS_NOSUID | MS_NODEV,
-                "uid=0,gid=1028,mode=0050") == -1) {
+                "uid=0,gid=1028,mode=0751") == -1) {
             SLOGE("Failed to mount tmpfs to %s: %s", target_base, strerror(errno));
             return -1;
         }
@@ -1728,13 +1738,10 @@
 
 #ifdef HAVE_ANDROID_OS
     if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
-        if (errno == EINVAL) {
-            SLOGW("PR_SET_NO_NEW_PRIVS failed. "
-                  "Is your kernel compiled correctly?: %s", strerror(errno));
-            // Don't return -1 here, since it's expected that not all
-            // kernels will support this option.
-        } else {
-            SLOGW("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
+        // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return
+        // EINVAL. Don't die on such kernels.
+        if (errno != EINVAL) {
+            SLOGE("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
             return -1;
         }
     }
diff --git a/vm/InitRefs.cpp b/vm/InitRefs.cpp
index 9a04842..08c28f8 100644
--- a/vm/InitRefs.cpp
+++ b/vm/InitRefs.cpp
@@ -127,6 +127,7 @@
         { &gDvm.classJavaLangReflectMethod,             "Ljava/lang/reflect/Method;" },
         { &gDvm.classJavaLangReflectMethodArray,        "[Ljava/lang/reflect/Method;"},
         { &gDvm.classJavaLangReflectProxy,              "Ljava/lang/reflect/Proxy;" },
+        { &gDvm.classJavaLangSystem,                    "Ljava/lang/System;" },
         { &gDvm.classJavaNioDirectByteBuffer,           "Ljava/nio/DirectByteBuffer;" },
         { &gDvm.classOrgApacheHarmonyDalvikDdmcChunk,   "Lorg/apache/harmony/dalvik/ddmc/Chunk;" },
         { &gDvm.classOrgApacheHarmonyDalvikDdmcDdmServer,
@@ -357,6 +358,9 @@
           "getSystemClassLoader", "()Ljava/lang/ClassLoader;" },
         { &gDvm.methJavaLangReflectProxy_constructorPrototype, "Ljava/lang/reflect/Proxy;",
           "constructorPrototype", "(Ljava/lang/reflect/InvocationHandler;)V" },
+        { &gDvm.methJavaLangSystem_runFinalization, "Ljava/lang/System;",
+          "runFinalization", "()V" },
+
         { &gDvm.methodTraceGcMethod, "Ldalvik/system/VMDebug;", "startGC", "()V" },
         { &gDvm.methodTraceClassPrepMethod, "Ldalvik/system/VMDebug;", "startClassPrep", "()V" },
         { &gDvm.methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation,
diff --git a/vm/alloc/Alloc.cpp b/vm/alloc/Alloc.cpp
index cfcb968..3d40d11 100644
--- a/vm/alloc/Alloc.cpp
+++ b/vm/alloc/Alloc.cpp
@@ -17,9 +17,12 @@
  * Garbage-collecting memory allocator.
  */
 #include "Dalvik.h"
+#include "Globals.h"
 #include "alloc/Heap.h"
 #include "alloc/HeapInternal.h"
 #include "alloc/HeapSource.h"
+#include "cutils/atomic.h"
+#include "cutils/atomic-inline.h"
 
 /*
  * Initialize the GC universe.
@@ -305,6 +308,17 @@
     dvmUnlockHeap();
 }
 
+/*
+ * Run finalization.
+ */
+void dvmRunFinalization() {
+  Thread *self = dvmThreadSelf();
+  assert(self != NULL);
+  JValue unusedResult;
+  assert(gDvm.methJavaLangSystem_runFinalization != NULL);
+  dvmCallMethod(self, gDvm.methJavaLangSystem_runFinalization, NULL, &unusedResult);
+}
+
 struct CountContext {
     const ClassObject *clazz;
     size_t count;
diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h
index efee1bd..b838719 100644
--- a/vm/alloc/Alloc.h
+++ b/vm/alloc/Alloc.h
@@ -125,6 +125,11 @@
 void dvmCollectGarbage(void);
 
 /*
+ * Calls System.runFinalization().
+ */
+void dvmRunFinalization();
+
+/*
  * Returns a count of the direct instances of a class.
  */
 size_t dvmCountInstancesOfClass(const ClassObject *clazz);
diff --git a/vm/alloc/CardTable.cpp b/vm/alloc/CardTable.cpp
index 28fe58e..87143dc 100644
--- a/vm/alloc/CardTable.cpp
+++ b/vm/alloc/CardTable.cpp
@@ -133,21 +133,20 @@
      */
     assert(gDvm.gcHeap->cardTableBase != NULL);
 
-#if 1
-    // zero out cards with memset(), using liveBits as an estimate
-    const HeapBitmap* liveBits = dvmHeapSourceGetLiveBits();
-    size_t maxLiveCard = (liveBits->max - liveBits->base) / GC_CARD_SIZE;
-    maxLiveCard = ALIGN_UP_TO_PAGE_SIZE(maxLiveCard);
-    if (maxLiveCard > gDvm.gcHeap->cardTableLength) {
-        maxLiveCard = gDvm.gcHeap->cardTableLength;
-    }
+    if (gDvm.lowMemoryMode) {
+      // zero out cards with madvise(), discarding all pages in the card table
+      madvise(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength, MADV_DONTNEED);
+    } else {
+      // zero out cards with memset(), using liveBits as an estimate
+      const HeapBitmap* liveBits = dvmHeapSourceGetLiveBits();
+      size_t maxLiveCard = (liveBits->max - liveBits->base) / GC_CARD_SIZE;
+      maxLiveCard = ALIGN_UP_TO_PAGE_SIZE(maxLiveCard);
+      if (maxLiveCard > gDvm.gcHeap->cardTableLength) {
+          maxLiveCard = gDvm.gcHeap->cardTableLength;
+      }
 
-    memset(gDvm.gcHeap->cardTableBase, GC_CARD_CLEAN, maxLiveCard);
-#else
-    // zero out cards with madvise(), discarding all pages in the card table
-    madvise(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength,
-        MADV_DONTNEED);
-#endif
+      memset(gDvm.gcHeap->cardTableBase, GC_CARD_CLEAN, maxLiveCard);
+    }
 }
 
 /*
diff --git a/vm/alloc/Heap.cpp b/vm/alloc/Heap.cpp
index 4a8d165..2de20ef 100644
--- a/vm/alloc/Heap.cpp
+++ b/vm/alloc/Heap.cpp
@@ -29,8 +29,9 @@
 #include "alloc/MarkSweep.h"
 #include "os/os.h"
 
-#include <sys/time.h>
+#include <sys/mman.h>
 #include <sys/resource.h>
+#include <sys/time.h>
 #include <limits.h>
 #include <errno.h>
 
diff --git a/vm/alloc/HeapSource.cpp b/vm/alloc/HeapSource.cpp
index 35e11c7..22de1b5 100644
--- a/vm/alloc/HeapSource.cpp
+++ b/vm/alloc/HeapSource.cpp
@@ -17,6 +17,7 @@
 #include <stdint.h>
 #include <sys/mman.h>
 #include <errno.h>
+#include <cutils/ashmem.h>
 
 #define SIZE_MAX UINT_MAX  // TODO: get SIZE_MAX from stdint.h
 
@@ -28,6 +29,7 @@
 #include "alloc/HeapBitmap.h"
 #include "alloc/HeapBitmapInlines.h"
 
+static void dvmHeapSourceUpdateMaxNativeFootprint();
 static void snapIdealFootprint();
 static void setIdealFootprint(size_t max);
 static size_t getMaximumSize(const HeapSource *hs);
@@ -177,6 +179,14 @@
     HeapBitmap markBits;
 
     /*
+     * Native allocations.
+     */
+    int32_t nativeBytesAllocated;
+    size_t nativeFootprintGCWatermark;
+    size_t nativeFootprintLimit;
+    bool nativeNeedToRunFinalization;
+
+    /*
      * State for the GC daemon.
      */
     bool hasGcThread;
@@ -377,6 +387,36 @@
 }
 
 /*
+ * A helper for addNewHeap(). Remap the new heap so that it will have
+ * a separate ashmem region with possibly a different name, etc. In
+ * practice, this is used to give the app heap a separate ashmem
+ * region from the zygote heap's.
+ */
+static bool remapNewHeap(HeapSource* hs, Heap* newHeap)
+{
+  char* newHeapBase = newHeap->base;
+  size_t rem_size = hs->heapBase + hs->heapLength - newHeapBase;
+  munmap(newHeapBase, rem_size);
+  int fd = ashmem_create_region("dalvik-heap", rem_size);
+  if (fd == -1) {
+    ALOGE("Unable to create an ashmem region for the new heap");
+    return false;
+  }
+  void* addr = mmap(newHeapBase, rem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+  int ret = close(fd);
+  if (addr == MAP_FAILED) {
+    ALOGE("Unable to map an ashmem region for the new heap");
+    return false;
+  }
+  if (ret == -1) {
+    ALOGE("Unable to close fd for the ashmem region for the new heap");
+    munmap(newHeapBase, rem_size);
+    return false;
+  }
+  return true;
+}
+
+/*
  * Adds an additional heap to the heap source.  Returns false if there
  * are too many heaps or insufficient free space to add another heap.
  */
@@ -414,6 +454,9 @@
     heap.base = base;
     heap.limit = heap.base + heap.maximumSize;
     heap.brk = heap.base + morecoreStart;
+    if (!remapNewHeap(hs, &heap)) {
+      return false;
+    }
     heap.msp = createMspace(base, morecoreStart, hs->minFree);
     if (heap.msp == NULL) {
         return false;
@@ -568,7 +611,7 @@
      * among the heaps managed by the garbage collector.
      */
     length = ALIGN_UP_TO_PAGE_SIZE(maximumSize);
-    base = dvmAllocRegion(length, PROT_NONE, "dalvik-heap");
+    base = dvmAllocRegion(length, PROT_NONE, gDvm.zygote ? "dalvik-zygote" : "dalvik-heap");
     if (base == NULL) {
         dvmAbort();
     }
@@ -603,6 +646,10 @@
     hs->softLimit = SIZE_MAX;    // no soft limit at first
     hs->numHeaps = 0;
     hs->sawZygote = gDvm.zygote;
+    hs->nativeBytesAllocated = 0;
+    hs->nativeFootprintGCWatermark = startSize;
+    hs->nativeFootprintLimit = startSize * 2;
+    hs->nativeNeedToRunFinalization = false;
     hs->hasGcThread = false;
     hs->heapBase = (char *)base;
     hs->heapLength = length;
@@ -878,10 +925,45 @@
                   FRACTIONAL_MB(hs->softLimit), n);
         return NULL;
     }
-    void* ptr = mspace_calloc(heap->msp, 1, n);
-    if (ptr == NULL) {
-        return NULL;
+    void* ptr;
+    if (gDvm.lowMemoryMode) {
+        /* This is only necessary because mspace_calloc always memsets the
+         * allocated memory to 0. This is bad for memory usage since it leads
+         * to dirty zero pages. If low memory mode is enabled, we use
+         * mspace_malloc which doesn't memset the allocated memory and madvise
+         * the page aligned region back to the kernel.
+         */
+        ptr = mspace_malloc(heap->msp, n);
+        if (ptr == NULL) {
+            return NULL;
+        }
+        uintptr_t zero_begin = (uintptr_t)ptr;
+        uintptr_t zero_end = (uintptr_t)ptr + n;
+        /* Calculate the page aligned region.
+         */
+        uintptr_t begin = ALIGN_UP_TO_PAGE_SIZE(zero_begin);
+        uintptr_t end = zero_end & ~(uintptr_t)(SYSTEM_PAGE_SIZE - 1);
+        /* If our allocation spans more than one page, we attempt to madvise.
+         */
+        if (begin < end) {
+            /* madvise the page aligned region to kernel.
+             */
+            madvise((void*)begin, end - begin, MADV_DONTNEED);
+            /* Zero the region after the page aligned region.
+             */
+            memset((void*)end, 0, zero_end - end);
+            /* Zero out the region before the page aligned region.
+             */
+            zero_end = begin;
+        }
+        memset((void*)zero_begin, 0, zero_end - zero_begin);
+    } else {
+        ptr = mspace_calloc(heap->msp, 1, n);
+        if (ptr == NULL) {
+            return NULL;
+        }
     }
+
     countAllocation(heap, ptr);
     /*
      * Check to see if a concurrent GC should be initiated.
@@ -1324,6 +1406,11 @@
     } else {
         heap->concurrentStartBytes = freeBytes - CONCURRENT_START;
     }
+
+    /* Mark that we need to run finalizers and update the native watermarks
+     * next time we attempt to register a native allocation.
+     */
+    gHs->nativeNeedToRunFinalization = true;
 }
 
 /*
@@ -1418,3 +1505,95 @@
         return NULL;
     }
 }
+
+static void dvmHeapSourceUpdateMaxNativeFootprint()
+{
+    /* Use the current target utilization ratio to determine the new native GC
+     * watermarks.
+     */
+    size_t nativeSize = gHs->nativeBytesAllocated;
+    size_t targetSize =
+        (nativeSize / gHs->targetUtilization) * HEAP_UTILIZATION_MAX;
+
+    if (targetSize > nativeSize + gHs->maxFree) {
+        targetSize = nativeSize + gHs->maxFree;
+    } else if (targetSize < nativeSize + gHs->minFree) {
+        targetSize = nativeSize + gHs->minFree;
+    }
+    gHs->nativeFootprintGCWatermark = targetSize;
+    gHs->nativeFootprintLimit = 2 * targetSize - nativeSize;
+}
+
+void dvmHeapSourceRegisterNativeAllocation(int bytes)
+{
+    /* If we have just done a GC, ensure that the finalizers are done and update
+     * the native watermarks.
+     */
+    if (gHs->nativeNeedToRunFinalization) {
+        dvmRunFinalization();
+        dvmHeapSourceUpdateMaxNativeFootprint();
+        gHs->nativeNeedToRunFinalization = false;
+    }
+
+    android_atomic_add(bytes, &gHs->nativeBytesAllocated);
+
+    if ((size_t)gHs->nativeBytesAllocated > gHs->nativeFootprintGCWatermark) {
+        /* The second watermark is higher than the gc watermark. If you hit
+         * this it means you are allocating native objects faster than the GC
+         * can keep up with. If this occurs, we do a GC for alloc.
+         */
+        if ((size_t)gHs->nativeBytesAllocated > gHs->nativeFootprintLimit) {
+            Thread* self = dvmThreadSelf();
+            dvmRunFinalization();
+            if (dvmCheckException(self)) {
+                return;
+            }
+            dvmLockHeap();
+            bool waited = dvmWaitForConcurrentGcToComplete();
+            dvmUnlockHeap();
+            if (waited) {
+                // Just finished a GC, attempt to run finalizers.
+                dvmRunFinalization();
+                if (dvmCheckException(self)) {
+                    return;
+                }
+            }
+
+            // If we still are over the watermark, attempt a GC for alloc and run finalizers.
+            if ((size_t)gHs->nativeBytesAllocated > gHs->nativeFootprintLimit) {
+                dvmLockHeap();
+                dvmWaitForConcurrentGcToComplete();
+                dvmCollectGarbageInternal(GC_FOR_MALLOC);
+                dvmUnlockHeap();
+                dvmRunFinalization();
+                gHs->nativeNeedToRunFinalization = false;
+                if (dvmCheckException(self)) {
+                    return;
+                }
+            }
+            /* We have just run finalizers, update the native watermark since
+             * it is very likely that finalizers released native managed
+             * allocations.
+             */
+            dvmHeapSourceUpdateMaxNativeFootprint();
+        } else {
+            dvmSignalCond(&gHs->gcThreadCond);
+        }
+    }
+}
+
+/*
+ * Called from VMRuntime.registerNativeFree.
+ */
+void dvmHeapSourceRegisterNativeFree(int bytes)
+{
+    int expected_size, new_size;
+    do {
+        expected_size = gHs->nativeBytesAllocated;
+        new_size = expected_size - bytes;
+        if (new_size < 0) {
+            break;
+        }
+    } while (android_atomic_cas(expected_size, new_size,
+                                &gHs->nativeBytesAllocated));
+}
diff --git a/vm/alloc/HeapSource.h b/vm/alloc/HeapSource.h
index e1f6820..42fccb2 100644
--- a/vm/alloc/HeapSource.h
+++ b/vm/alloc/HeapSource.h
@@ -200,4 +200,14 @@
  */
 size_t dvmHeapSourceGetMaximumSize(void);
 
+/*
+ * Called from VMRuntime.registerNativeAllocation.
+ */
+void dvmHeapSourceRegisterNativeAllocation(int bytes);
+
+/*
+ * Called from VMRuntime.registerNativeFree.
+ */
+void dvmHeapSourceRegisterNativeFree(int bytes);
+
 #endif  // DALVIK_HEAP_SOURCE_H_
diff --git a/vm/compiler/Compiler.cpp b/vm/compiler/Compiler.cpp
index b1ba96f..f5b96b1 100644
--- a/vm/compiler/Compiler.cpp
+++ b/vm/compiler/Compiler.cpp
@@ -182,7 +182,7 @@
                              MAP_PRIVATE , fd, 0);
     close(fd);
     if (gDvmJit.codeCache == MAP_FAILED) {
-        ALOGE("Failed to mmap the JIT code cache: %s", strerror(errno));
+        ALOGE("Failed to mmap the JIT code cache of size %d: %s", gDvmJit.codeCacheSize, strerror(errno));
         return false;
     }
 
diff --git a/vm/compiler/codegen/arm/Assemble.cpp b/vm/compiler/codegen/arm/Assemble.cpp
index a729dc5..10572eb 100644
--- a/vm/compiler/codegen/arm/Assemble.cpp
+++ b/vm/compiler/codegen/arm/Assemble.cpp
@@ -2153,6 +2153,8 @@
     }
 
     ALOGD("JIT: Average execution count -> %d",(int)(sum / numTraces));
+    // How efficiently are we using code cache memory?  Bigger is better.
+    ALOGD("JIT: CodeCache efficiency -> %.2f",(float)sum / (float)gDvmJit.codeCacheByteUsed);
 
     /* Dump the sorted entries. The count of each trace will be reset to 0. */
     for (i=0; i < gDvmJit.jitTableSize; i++) {
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.cpp b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.cpp
index 713ecfa..6c89b11 100644
--- a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.cpp
+++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.cpp
@@ -55,7 +55,13 @@
     if (gDvmJit.threshold == 0) {
         gDvmJit.threshold = 200;
     }
-    gDvmJit.codeCacheSize = 512*1024;
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 512 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
+    /* Hard limit for Arm of 2M */
+    assert(gDvmJit.codeCacheSize <= 2 * 1024 * 1024);
 
 #if defined(WITH_SELF_VERIFICATION)
     /* Force into blocking mode */
diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.cpp b/vm/compiler/codegen/arm/armv5te/ArchVariant.cpp
index 25d650e..5c7fbbe 100644
--- a/vm/compiler/codegen/arm/armv5te/ArchVariant.cpp
+++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.cpp
@@ -55,7 +55,13 @@
     if (gDvmJit.threshold == 0) {
         gDvmJit.threshold = 200;
     }
-    gDvmJit.codeCacheSize = 512*1024;
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 512 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
+    /* Hard limit for Arm of 2M */
+    assert(gDvmJit.codeCacheSize <= 2 * 1024 * 1024);
 
 #if defined(WITH_SELF_VERIFICATION)
     /* Force into blocking mode */
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.cpp b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.cpp
index 0b1e0cd..a81a2e7 100644
--- a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.cpp
+++ b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.cpp
@@ -50,7 +50,13 @@
     if (gDvmJit.threshold == 0) {
         gDvmJit.threshold = 40;
     }
-    gDvmJit.codeCacheSize = 1024*1024;
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 1500 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
+    /* Hard limit for Arm of 2M */
+    assert(gDvmJit.codeCacheSize <= 2 * 1024 * 1024);
 
 #if defined(WITH_SELF_VERIFICATION)
     /* Force into blocking */
diff --git a/vm/compiler/codegen/arm/armv7-a/ArchVariant.cpp b/vm/compiler/codegen/arm/armv7-a/ArchVariant.cpp
index c698f62..72ae3ce 100644
--- a/vm/compiler/codegen/arm/armv7-a/ArchVariant.cpp
+++ b/vm/compiler/codegen/arm/armv7-a/ArchVariant.cpp
@@ -50,7 +50,13 @@
     if (gDvmJit.threshold == 0) {
         gDvmJit.threshold = 40;
     }
-    gDvmJit.codeCacheSize = 1024*1024;
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 1500 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
+    /* Hard limit for Arm of 2M */
+    assert(gDvmJit.codeCacheSize <= 2 * 1024 * 1024);
 
 #if defined(WITH_SELF_VERIFICATION)
     /* Force into blocking */
diff --git a/vm/compiler/codegen/mips/mips/ArchVariant.cpp b/vm/compiler/codegen/mips/mips/ArchVariant.cpp
index 4362961..473b88e 100644
--- a/vm/compiler/codegen/mips/mips/ArchVariant.cpp
+++ b/vm/compiler/codegen/mips/mips/ArchVariant.cpp
@@ -55,7 +55,11 @@
     if (gDvmJit.threshold == 0) {
         gDvmJit.threshold = 200;
     }
-    gDvmJit.codeCacheSize = 512*1024;
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 512 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
 
 #if defined(WITH_SELF_VERIFICATION)
     /* Force into blocking mode */
diff --git a/vm/compiler/codegen/x86/CodegenInterface.cpp b/vm/compiler/codegen/x86/CodegenInterface.cpp
index 3027929..337bd61 100644
--- a/vm/compiler/codegen/x86/CodegenInterface.cpp
+++ b/vm/compiler/codegen/x86/CodegenInterface.cpp
@@ -67,7 +67,11 @@
     if (gDvmJit.threshold == 0) {
         gDvmJit.threshold = 255;
     }
-    gDvmJit.codeCacheSize = 512*1024;
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 512 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
     gDvmJit.optLevel = kJitOptLevelO1;
 
     //Disable Method-JIT
diff --git a/vm/interp/InterpState.h b/vm/interp/InterpState.h
index 6ef4472..8d2c224 100644
--- a/vm/interp/InterpState.h
+++ b/vm/interp/InterpState.h
@@ -192,7 +192,7 @@
 /* Number of entries in the 2nd level JIT profiler filter cache */
 #define JIT_TRACE_THRESH_FILTER_SIZE 32
 /* Number of low dalvik pc address bits to include in 2nd level filter key */
-#define JIT_TRACE_THRESH_FILTER_PC_BITS 4
+#define JIT_TRACE_THRESH_FILTER_PC_BITS 16
 #define MAX_JIT_RUN_LEN 64
 
 enum JitHint {
diff --git a/vm/native/dalvik_system_VMDebug.cpp b/vm/native/dalvik_system_VMDebug.cpp
index dae8997..5377357 100644
--- a/vm/native/dalvik_system_VMDebug.cpp
+++ b/vm/native/dalvik_system_VMDebug.cpp
@@ -18,6 +18,7 @@
  * dalvik.system.VMDebug
  */
 #include "Dalvik.h"
+#include "alloc/HeapSource.h"
 #include "native/InternalNativePriv.h"
 #include "hprof/Hprof.h"
 
@@ -770,11 +771,50 @@
     }
 }
 
+/*
+ * public static native void getHeapSpaceStats(long[] data)
+ */
+static void Dalvik_dalvik_system_VMDebug_getHeapSpaceStats(const u4* args,
+    JValue* pResult)
+{
+    ArrayObject* dataArray = (ArrayObject*) args[0];
+
+    if (dataArray == NULL || dataArray->length < 6) {
+      RETURN_VOID();
+    }
+
+    jlong* arr = (jlong*)(void*)dataArray->contents;
+
+    int j = 0;
+    size_t per_heap_allocated[2];
+    size_t per_heap_size[2];
+    memset(per_heap_allocated, 0, sizeof(per_heap_allocated));
+    memset(per_heap_size, 0, sizeof(per_heap_size));
+    dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, (size_t*) &per_heap_allocated, 2);
+    dvmHeapSourceGetValue(HS_FOOTPRINT, (size_t*) &per_heap_size, 2);
+    jlong heapSize = per_heap_size[0];
+    jlong heapUsed = per_heap_allocated[0];
+    jlong heapFree = heapSize - heapUsed;
+    jlong zygoteSize = per_heap_size[1];
+    jlong zygoteUsed = per_heap_allocated[1];
+    jlong zygoteFree = zygoteSize - zygoteUsed;
+    arr[j++] = heapSize;
+    arr[j++] = heapUsed;
+    arr[j++] = heapFree;
+    arr[j++] = zygoteSize;
+    arr[j++] = zygoteUsed;
+    arr[j++] = zygoteFree;
+
+    RETURN_VOID();
+}
+
 const DalvikNativeMethod dvm_dalvik_system_VMDebug[] = {
     { "getVmFeatureList",           "()[Ljava/lang/String;",
         Dalvik_dalvik_system_VMDebug_getVmFeatureList },
     { "getAllocCount",              "(I)I",
         Dalvik_dalvik_system_VMDebug_getAllocCount },
+    { "getHeapSpaceStats",          "([J)V",
+        Dalvik_dalvik_system_VMDebug_getHeapSpaceStats },
     { "resetAllocCount",            "(I)V",
         Dalvik_dalvik_system_VMDebug_resetAllocCount },
     { "startAllocCounting",         "()V",
diff --git a/vm/native/dalvik_system_VMRuntime.cpp b/vm/native/dalvik_system_VMRuntime.cpp
index 8211342..1f7a883 100644
--- a/vm/native/dalvik_system_VMRuntime.cpp
+++ b/vm/native/dalvik_system_VMRuntime.cpp
@@ -19,10 +19,15 @@
  */
 #include "Dalvik.h"
 #include "ScopedPthreadMutexLock.h"
+#include "UniquePtr.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Visit.h"
+#include "libdex/DexClass.h"
 #include "native/InternalNativePriv.h"
 
 #include <limits.h>
 
+#include <map>
 
 /*
  * public native float getTargetHeapUtilization()
@@ -184,6 +189,12 @@
     returnCString(pResult, buf);
 }
 
+static void Dalvik_dalvik_system_VMRuntime_vmLibrary(const u4* args,
+    JValue* pResult)
+{
+    returnCString(pResult, "libdvm.so");
+}
+
 static void Dalvik_dalvik_system_VMRuntime_setTargetSdkVersionNative(
     const u4* args,
     JValue* pResult)
@@ -204,6 +215,339 @@
     RETURN_VOID();
 }
 
+static void Dalvik_dalvik_system_VMRuntime_registerNativeAllocation(const u4* args,
+                                                                    JValue* pResult)
+{
+  int bytes = args[1];
+  if (bytes < 0) {
+    dvmThrowRuntimeException("allocation size negative");
+  } else {
+    dvmHeapSourceRegisterNativeAllocation(bytes);
+  }
+  RETURN_VOID();
+}
+
+static void Dalvik_dalvik_system_VMRuntime_registerNativeFree(const u4* args,
+                                                              JValue* pResult)
+{
+  int bytes = args[1];
+  if (bytes < 0) {
+    dvmThrowRuntimeException("allocation size negative");
+  } else {
+    dvmHeapSourceRegisterNativeFree(bytes);
+  }
+  RETURN_VOID();
+}
+
+static DvmDex* getDvmDexFromClassPathEntry(ClassPathEntry* cpe) {
+    if (cpe->kind == kCpeDex) {
+        return ((RawDexFile*) cpe->ptr)->pDvmDex;
+    }
+    if (cpe->kind == kCpeJar) {
+        return ((JarFile*) cpe->ptr)->pDvmDex;
+    }
+    LOG_ALWAYS_FATAL("Unknown cpe->kind=%d", cpe->kind);
+}
+
+typedef std::map<std::string, StringObject*> StringTable;
+
+static void preloadDexCachesStringsVisitor(void* addr, u4 threadId, RootType type, void* arg) {
+    StringTable& table = *(StringTable*) arg;
+    StringObject* strObj = *(StringObject**) addr;
+    LOG_FATAL_IF(strObj->clazz != gDvm.classJavaLangString, "Unknown class for supposed string");
+    char* newStr = dvmCreateCstrFromString(strObj);
+    // ALOGI("VMRuntime.preloadDexCaches interned=%s", newStr);
+    table[newStr] = strObj;
+    free(newStr);
+}
+
+// Based on dvmResolveString.
+static void preloadDexCachesResolveString(DvmDex* pDvmDex,
+                                          uint32_t stringIdx,
+                                          StringTable& strings) {
+    StringObject* string = dvmDexGetResolvedString(pDvmDex, stringIdx);
+    if (string != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    uint32_t utf16Size;
+    const char* utf8 = dexStringAndSizeById(pDexFile, stringIdx, &utf16Size);
+    string = strings[utf8];
+    if (string == NULL) {
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found string=%s", utf8);
+    dvmDexSetResolvedString(pDvmDex, stringIdx, string);
+}
+
+// Based on dvmResolveClass.
+static void preloadDexCachesResolveType(DvmDex* pDvmDex, uint32_t typeIdx) {
+    ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, typeIdx);
+    if (clazz != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    const char* className = dexStringByTypeIdx(pDexFile, typeIdx);
+    if (className[0] != '\0' && className[1] == '\0') {
+        /* primitive type */
+        clazz = dvmFindPrimitiveClass(className[0]);
+    } else {
+        clazz = dvmLookupClass(className, NULL, true);
+    }
+    if (clazz == NULL) {
+        return;
+    }
+    // Skip uninitialized classes because filled cache entry implies it is initialized.
+    if (!dvmIsClassInitialized(clazz)) {
+        // ALOGI("VMRuntime.preloadDexCaches uninitialized clazz=%s", className);
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found clazz=%s", className);
+    dvmDexSetResolvedClass(pDvmDex, typeIdx, clazz);
+}
+
+// Based on dvmResolveInstField/dvmResolveStaticField.
+static void preloadDexCachesResolveField(DvmDex* pDvmDex, uint32_t fieldIdx, bool instance) {
+    Field* field = dvmDexGetResolvedField(pDvmDex, fieldIdx);
+    if (field != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    const DexFieldId* pFieldId = dexGetFieldId(pDexFile, fieldIdx);
+    ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pFieldId->classIdx);
+    if (clazz == NULL) {
+        return;
+    }
+    // Skip static fields for uninitialized classes because a filled
+    // cache entry implies the class is initialized.
+    if (!instance && !dvmIsClassInitialized(clazz)) {
+        return;
+    }
+    const char* fieldName = dexStringById(pDexFile, pFieldId->nameIdx);
+    const char* signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+    if (instance) {
+        field = dvmFindInstanceFieldHier(clazz, fieldName, signature);
+    } else {
+        field = dvmFindStaticFieldHier(clazz, fieldName, signature);
+    }
+    if (field == NULL) {
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found field %s %s.%s",
+    //       signature, clazz->descriptor, fieldName);
+    dvmDexSetResolvedField(pDvmDex, fieldIdx, field);
+}
+
+// Based on dvmResolveMethod.
+static void preloadDexCachesResolveMethod(DvmDex* pDvmDex,
+                                          uint32_t methodIdx,
+                                          MethodType methodType) {
+    Method* method = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+    if (method != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    const DexMethodId* pMethodId = dexGetMethodId(pDexFile, methodIdx);
+    ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pMethodId->classIdx);
+    if (clazz == NULL) {
+        return;
+    }
+    // Skip static methods for uninitialized classes because a filled
+    // cache entry implies the class is initialized.
+    if ((methodType == METHOD_STATIC) && !dvmIsClassInitialized(clazz)) {
+        return;
+    }
+    const char* methodName = dexStringById(pDexFile, pMethodId->nameIdx);
+    DexProto proto;
+    dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+
+    if (methodType == METHOD_DIRECT) {
+        method = dvmFindDirectMethod(clazz, methodName, &proto);
+    } else if (methodType == METHOD_STATIC) {
+        method = dvmFindDirectMethodHier(clazz, methodName, &proto);
+    } else {
+        method = dvmFindVirtualMethodHier(clazz, methodName, &proto);
+    }
+    if (method == NULL) {
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found method %s.%s",
+    //        clazz->descriptor, methodName);
+    dvmDexSetResolvedMethod(pDvmDex, methodIdx, method);
+}
+
+struct DexCacheStats {
+    uint32_t numStrings;
+    uint32_t numTypes;
+    uint32_t numFields;
+    uint32_t numMethods;
+    DexCacheStats() : numStrings(0), numTypes(0), numFields(0), numMethods(0) {};
+};
+
+static const bool kPreloadDexCachesEnabled = true;
+
+// Disabled because it takes a long time (extra half second) but
+// gives almost no benefit in terms of saving private dirty pages.
+static const bool kPreloadDexCachesStrings = false;
+
+static const bool kPreloadDexCachesTypes = true;
+static const bool kPreloadDexCachesFieldsAndMethods = true;
+
+static const bool kPreloadDexCachesCollectStats = false;
+
+static void preloadDexCachesStatsTotal(DexCacheStats* total) {
+    if (!kPreloadDexCachesCollectStats) {
+        return;
+    }
+
+    for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) {
+        DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe);
+        const DexHeader* pHeader = pDvmDex->pHeader;
+        total->numStrings += pHeader->stringIdsSize;
+        total->numFields += pHeader->fieldIdsSize;
+        total->numMethods += pHeader->methodIdsSize;
+        total->numTypes += pHeader->typeIdsSize;
+    }
+}
+
+static void preloadDexCachesStatsFilled(DexCacheStats* filled) {
+    if (!kPreloadDexCachesCollectStats) {
+        return;
+    }
+    for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) {
+        DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe);
+        const DexHeader* pHeader = pDvmDex->pHeader;
+        for (size_t i = 0; i < pHeader->stringIdsSize; i++) {
+            StringObject* string = dvmDexGetResolvedString(pDvmDex, i);
+            if (string != NULL) {
+                filled->numStrings++;
+            }
+        }
+        for (size_t i = 0; i < pHeader->typeIdsSize; i++) {
+            ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, i);
+            if (clazz != NULL) {
+                filled->numTypes++;
+            }
+        }
+        for (size_t i = 0; i < pHeader->fieldIdsSize; i++) {
+            Field* field = dvmDexGetResolvedField(pDvmDex, i);
+            if (field != NULL) {
+                filled->numFields++;
+            }
+        }
+        for (size_t i = 0; i < pHeader->methodIdsSize; i++) {
+            Method* method = dvmDexGetResolvedMethod(pDvmDex, i);
+            if (method != NULL) {
+                filled->numMethods++;
+            }
+        }
+    }
+}
+
+static void Dalvik_dalvik_system_VMRuntime_preloadDexCaches(const u4* args, JValue* pResult)
+{
+    if (!kPreloadDexCachesEnabled) {
+        return;
+    }
+
+    DexCacheStats total;
+    DexCacheStats before;
+    if (kPreloadDexCachesCollectStats) {
+        ALOGI("VMRuntime.preloadDexCaches starting");
+        preloadDexCachesStatsTotal(&total);
+        preloadDexCachesStatsFilled(&before);
+    }
+
+    // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings
+    StringTable strings;
+    if (kPreloadDexCachesStrings) {
+        dvmLockMutex(&gDvm.internLock);
+        dvmHashTableLock(gDvm.literalStrings);
+        for (int i = 0; i < gDvm.literalStrings->tableSize; ++i) {
+            HashEntry *entry = &gDvm.literalStrings->pEntries[i];
+            if (entry->data != NULL && entry->data != HASH_TOMBSTONE) {
+                preloadDexCachesStringsVisitor(&entry->data, 0, ROOT_INTERNED_STRING, &strings);
+            }
+        }
+        dvmHashTableUnlock(gDvm.literalStrings);
+        dvmUnlockMutex(&gDvm.internLock);
+    }
+
+    for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) {
+        DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe);
+        const DexHeader* pHeader = pDvmDex->pHeader;
+        const DexFile* pDexFile = pDvmDex->pDexFile;
+
+        if (kPreloadDexCachesStrings) {
+            for (size_t i = 0; i < pHeader->stringIdsSize; i++) {
+                preloadDexCachesResolveString(pDvmDex, i, strings);
+            }
+        }
+
+        if (kPreloadDexCachesTypes) {
+            for (size_t i = 0; i < pHeader->typeIdsSize; i++) {
+                preloadDexCachesResolveType(pDvmDex, i);
+            }
+        }
+
+        if (kPreloadDexCachesFieldsAndMethods) {
+            for (size_t classDefIndex = 0;
+                 classDefIndex < pHeader->classDefsSize;
+                 classDefIndex++) {
+                const DexClassDef* pClassDef = dexGetClassDef(pDexFile, classDefIndex);
+                const u1* pEncodedData = dexGetClassData(pDexFile, pClassDef);
+                UniquePtr<DexClassData> pClassData(dexReadAndVerifyClassData(&pEncodedData, NULL));
+                if (pClassData.get() == NULL) {
+                    continue;
+                }
+                for (uint32_t fieldIndex = 0;
+                     fieldIndex < pClassData->header.staticFieldsSize;
+                     fieldIndex++) {
+                    const DexField* pField = &pClassData->staticFields[fieldIndex];
+                    preloadDexCachesResolveField(pDvmDex, pField->fieldIdx, false);
+                }
+                for (uint32_t fieldIndex = 0;
+                     fieldIndex < pClassData->header.instanceFieldsSize;
+                     fieldIndex++) {
+                    const DexField* pField = &pClassData->instanceFields[fieldIndex];
+                    preloadDexCachesResolveField(pDvmDex, pField->fieldIdx, true);
+                }
+                for (uint32_t methodIndex = 0;
+                     methodIndex < pClassData->header.directMethodsSize;
+                     methodIndex++) {
+                    const DexMethod* pDexMethod = &pClassData->directMethods[methodIndex];
+                    MethodType methodType = (((pDexMethod->accessFlags & ACC_STATIC) != 0) ?
+                                             METHOD_STATIC :
+                                             METHOD_DIRECT);
+                    preloadDexCachesResolveMethod(pDvmDex, pDexMethod->methodIdx, methodType);
+                }
+                for (uint32_t methodIndex = 0;
+                     methodIndex < pClassData->header.virtualMethodsSize;
+                     methodIndex++) {
+                    const DexMethod* pDexMethod = &pClassData->virtualMethods[methodIndex];
+                    preloadDexCachesResolveMethod(pDvmDex, pDexMethod->methodIdx, METHOD_VIRTUAL);
+                }
+            }
+        }
+    }
+
+    if (kPreloadDexCachesCollectStats) {
+        DexCacheStats after;
+        preloadDexCachesStatsFilled(&after);
+        ALOGI("VMRuntime.preloadDexCaches strings total=%d before=%d after=%d",
+              total.numStrings, before.numStrings, after.numStrings);
+        ALOGI("VMRuntime.preloadDexCaches types total=%d before=%d after=%d",
+              total.numTypes, before.numTypes, after.numTypes);
+        ALOGI("VMRuntime.preloadDexCaches fields total=%d before=%d after=%d",
+              total.numFields, before.numFields, after.numFields);
+        ALOGI("VMRuntime.preloadDexCaches methods total=%d before=%d after=%d",
+              total.numMethods, before.numMethods, after.numMethods);
+        ALOGI("VMRuntime.preloadDexCaches finished");
+    }
+
+    RETURN_VOID();
+}
+
 const DalvikNativeMethod dvm_dalvik_system_VMRuntime[] = {
     { "addressOf", "(Ljava/lang/Object;)J",
         Dalvik_dalvik_system_VMRuntime_addressOf },
@@ -231,5 +575,13 @@
         Dalvik_dalvik_system_VMRuntime_startJitCompilation },
     { "vmVersion", "()Ljava/lang/String;",
         Dalvik_dalvik_system_VMRuntime_vmVersion },
+    { "vmLibrary", "()Ljava/lang/String;",
+        Dalvik_dalvik_system_VMRuntime_vmLibrary },
+    { "registerNativeAllocation", "(I)V",
+        Dalvik_dalvik_system_VMRuntime_registerNativeAllocation },
+    { "registerNativeFree", "(I)V",
+        Dalvik_dalvik_system_VMRuntime_registerNativeFree },
+    { "preloadDexCaches", "()V",
+        Dalvik_dalvik_system_VMRuntime_preloadDexCaches },
     { NULL, NULL, NULL },
 };
diff --git a/vm/native/dalvik_system_Zygote.cpp b/vm/native/dalvik_system_Zygote.cpp
index 9e3005a..86a8fb9 100644
--- a/vm/native/dalvik_system_Zygote.cpp
+++ b/vm/native/dalvik_system_Zygote.cpp
@@ -273,18 +273,14 @@
 
         // Prepare source paths
         char source_user[PATH_MAX];
-        char source_obb[PATH_MAX];
         char target_user[PATH_MAX];
 
         // /mnt/shell/emulated/0
         snprintf(source_user, PATH_MAX, "%s/%d", source, userid);
-        // /mnt/shell/emulated/obb
-        snprintf(source_obb, PATH_MAX, "%s/obb", source);
         // /storage/emulated/0
         snprintf(target_user, PATH_MAX, "%s/%d", target, userid);
 
         if (fs_prepare_dir(source_user, 0000, 0, 0) == -1
-                || fs_prepare_dir(source_obb, 0000, 0, 0) == -1
                 || fs_prepare_dir(target_user, 0000, 0, 0) == -1) {
             return -1;
         }
@@ -303,23 +299,7 @@
             }
         }
 
-        // Now that user is mounted, prepare and mount OBB storage
-        // into place for current user
-        char target_android[PATH_MAX];
-        char target_obb[PATH_MAX];
-
-        // /storage/emulated/0/Android
-        snprintf(target_android, PATH_MAX, "%s/%d/Android", target, userid);
-        // /storage/emulated/0/Android/obb
-        snprintf(target_obb, PATH_MAX, "%s/%d/Android/obb", target, userid);
-
-        if (fs_prepare_dir(target_android, 0000, 0, 0) == -1
-                || fs_prepare_dir(target_obb, 0000, 0, 0) == -1
-                || fs_prepare_dir(legacy, 0000, 0, 0) == -1) {
-            return -1;
-        }
-        if (mount(source_obb, target_obb, NULL, MS_BIND, NULL) == -1) {
-            ALOGE("Failed to mount %s to %s: %s", source_obb, target_obb, strerror(errno));
+        if (fs_prepare_dir(legacy, 0000, 0, 0) == -1) {
             return -1;
         }
 
diff --git a/vm/native/java_lang_Class.cpp b/vm/native/java_lang_Class.cpp
index 8687138..52c8c3e 100644
--- a/vm/native/java_lang_Class.cpp
+++ b/vm/native/java_lang_Class.cpp
@@ -771,14 +771,10 @@
     if (dvm_dex == NULL) {
         return NULL;
     }
-
-    ScopedPthreadMutexLock lock(&dvm_dex->modLock);
-
     // Already cached?
     if (dvm_dex->dex_object != NULL) {
         return dvm_dex->dex_object;
     }
-
     jobject byte_buffer = env->NewDirectByteBuffer(dvm_dex->memMap.addr, dvm_dex->memMap.length);
     if (byte_buffer == NULL) {
         return NULL;
@@ -805,7 +801,12 @@
         return NULL;
     }
 
-    dvm_dex->dex_object = env->NewGlobalRef(local_ref);
+    // Check another thread didn't cache an object, if we've won install the object.
+    ScopedPthreadMutexLock lock(&dvm_dex->modLock);
+
+    if (dvm_dex->dex_object == NULL) {
+        dvm_dex->dex_object = env->NewGlobalRef(local_ref);
+    }
     return dvm_dex->dex_object;
 }
 
diff --git a/vm/oo/Class.cpp b/vm/oo/Class.cpp
index 78a2273..db5340e 100644
--- a/vm/oo/Class.cpp
+++ b/vm/oo/Class.cpp
@@ -1740,6 +1740,9 @@
      * Make sure the aren't any "bonus" flags set, since we use them for
      * runtime state.
      */
+    /* bits we can reasonably expect to see set in a DEX access flags field */
+    const uint32_t EXPECTED_FILE_FLAGS = (ACC_CLASS_MASK | CLASS_ISPREVERIFIED |
+                                          CLASS_ISOPTIMIZED);
     if ((pClassDef->accessFlags & ~EXPECTED_FILE_FLAGS) != 0) {
         ALOGW("Invalid file flags in class %s: %04x",
             descriptor, pClassDef->accessFlags);
diff --git a/vm/oo/Object.h b/vm/oo/Object.h
index ca41157..92438ba 100644
--- a/vm/oo/Object.h
+++ b/vm/oo/Object.h
@@ -82,10 +82,6 @@
     CLASS_ISPREVERIFIED        = (1<<16), // class has been pre-verified
 };
 
-/* bits we can reasonably expect to see set in a DEX access flags field */
-#define EXPECTED_FILE_FLAGS \
-    (ACC_CLASS_MASK | CLASS_ISPREVERIFIED | CLASS_ISOPTIMIZED)
-
 /*
  * Get/set class flags.
  */
diff --git a/vm/os/android.cpp b/vm/os/android.cpp
index 24ebd5a..b37bb70 100644
--- a/vm/os/android.cpp
+++ b/vm/os/android.cpp
@@ -23,8 +23,8 @@
 #include <limits.h>
 #include <errno.h>
 
+#include <system/thread_defs.h>
 #include <cutils/sched_policy.h>
-#include <utils/threads.h>
 
 /*
  * Conversion map for "nice" values.