Snapshot 776e91f2212c253732a2907b365bc1b510a91cba

Change-Id: Ic54c2364c02ef5817ad9dac0edd8c2d383c70aa7
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..18cf576
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,66 @@
+# -*- mode: makefile -*-
+# Copyright (C) 2009 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)
+
+
+#
+# Include the definitions to build the Java code.
+#
+
+include $(LOCAL_PATH)/JavaLibrary.mk
+
+
+#
+# Include the definitions to build the native code.
+#
+
+include $(LOCAL_PATH)/NativeCode.mk
+
+#
+# Include the definitions for the Certificate Authority (CA) certificates
+#
+
+include $(LOCAL_PATH)/CaCerts.mk
+
+#
+# Disable test modules if LIBCORE_SKIP_TESTS environment variable is set.
+#
+
+ifneq ($(LIBCORE_SKIP_TESTS),)
+$(info ********************************************************************************)
+$(info * libcore tests are skipped because environment variable LIBCORE_SKIP_TESTS=$(LIBCORE_SKIP_TESTS))
+$(info ********************************************************************************)
+endif
+
+
+#
+# "m dalvik-host" for quick minimal host build
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+    .PHONY: dalvik-host
+    dalvik-host: \
+        dalvik \
+        $(HOST_OUT)/bin/dalvikvm \
+        $(HOST_OUT)/bin/dexopt \
+        $(HOST_OUT)/lib/libjavacore.so \
+        cacerts-host \
+        core-hostdex \
+        bouncycastle-hostdex \
+        apache-xml-hostdex \
+        apache-harmony-tests-hostdex \
+        $(call intermediates-dir-for,JAVA_LIBRARIES,core-tests,,COMMON)/classes.jar
+endif
diff --git a/CaCerts.mk b/CaCerts.mk
new file mode 100644
index 0000000..980d0fb
--- /dev/null
+++ b/CaCerts.mk
@@ -0,0 +1,59 @@
+# -*- mode: makefile -*-
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Definitions for installing Certificate Authority (CA) certificates
+#
+
+define all-files-under
+$(patsubst ./%,%, \
+  $(shell cd $(LOCAL_PATH) ; \
+          find $(1) -type f) \
+ )
+endef
+
+# $(1): module name
+# $(2): source file
+# $(3): destination directory
+define include-prebuilt-with-destination-directory
+include $$(CLEAR_VARS)
+LOCAL_MODULE := $(1)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/CaCerts.mk
+LOCAL_MODULE_STEM := $(notdir $(2))
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(3)
+LOCAL_SRC_FILES := $(2)
+include $$(BUILD_PREBUILT)
+endef
+
+cacerts := $(call all-files-under,luni/src/main/files/cacerts)
+
+cacerts_target_directory := $(TARGET_OUT)/etc/security/cacerts
+$(foreach cacert, $(cacerts), $(eval $(call include-prebuilt-with-destination-directory,target-cacert-$(notdir $(cacert)),$(cacert),$(cacerts_target_directory))))
+cacerts_target := $(addprefix $(cacerts_target_directory)/,$(foreach cacert,$(cacerts),$(notdir $(cacert))))
+.PHONY: cacerts_target
+cacerts: $(cacerts_target)
+
+# This is so that build/target/product/core.mk can use cacerts in PRODUCT_PACKAGES
+ALL_MODULES.cacerts.INSTALLED := $(cacerts_target)
+
+ifeq ($(WITH_HOST_DALVIK),true)
+cacerts_host_directory := $(HOST_OUT)/etc/security/cacerts
+$(foreach cacert, $(cacerts), $(eval $(call include-prebuilt-with-destination-directory,host-cacert-$(notdir $(cacert)),$(cacert),$(cacerts_host_directory))))
+endif
+cacerts_host := $(addprefix $(cacerts_host_directory)/,$(foreach cacert,$(cacerts),$(notdir $(cacert))))
+.PHONY: cacerts-host
+cacerts-host: $(cacerts_host)
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..1618f1b
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,50 @@
+# -*- mode: makefile -*-
+# Copyright (C) 2007 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+#$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/Docs.mk b/Docs.mk
new file mode 100644
index 0000000..875a008
--- /dev/null
+++ b/Docs.mk
@@ -0,0 +1,29 @@
+# -*- mode: makefile -*-
+# List of libcore directories to include in documentation.
+# Shared between libcore and frameworks/base.
+
+define libcoredoc-all-java-files-under
+$(patsubst ./%,%, \
+  $(shell cd $(1) ; \
+          find $(2) -name "*.java" -and -not -name ".*") \
+ )
+endef
+
+# List of libcore javadoc source files
+# 
+# Note dalvik/system is non-recursive to exclude dalvik.system.profiler
+#
+# $(1): directory for search (to support use from frameworks/base)
+define libcore_to_document
+ $(call libcoredoc-all-java-files-under,$(1), \
+   dalvik/src/main/java/dalvik/system/ -maxdepth 1) \
+ $(call libcoredoc-all-java-files-under,$(1), \
+   dalvik/src/main/java/dalvik/annotation \
+   dalvik/src/main/java/dalvik/bytecode \
+   json/src/main/java \
+   luni/src/main/java/java \
+   luni/src/main/java/javax \
+   luni/src/main/java/org/xml/sax \
+   luni/src/main/java/org/w3c \
+   xml/src/main/java/org/xmlpull/v1)
+endef
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
new file mode 100644
index 0000000..98de1f2
--- /dev/null
+++ b/JavaLibrary.mk
@@ -0,0 +1,207 @@
+# -*- mode: makefile -*-
+# Copyright (C) 2007 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.
+
+#
+# Definitions for building the Java library and associated tests.
+#
+
+#
+# Common definitions for host and target.
+#
+
+# libcore is divided into modules.
+#
+# The structure of each module is:
+#
+#   src/
+#       main/               # To be shipped on every device.
+#            java/          # Java source for library code.
+#            native/        # C++ source for library code.
+#            resources/     # Support files.
+#       test/               # Built only on demand, for testing.
+#            java/          # Java source for tests.
+#            native/        # C++ source for tests (rare).
+#            resources/     # Support files.
+#
+# All subdirectories are optional (hence the "2> /dev/null"s below).
+
+define all-main-java-files-under
+$(foreach dir,$(1),$(patsubst ./%,%,$(shell cd $(LOCAL_PATH) && find $(dir)/src/main/java -name "*.java" 2> /dev/null)))
+endef
+
+define all-test-java-files-under
+$(foreach dir,$(1),$(patsubst ./%,%,$(shell cd $(LOCAL_PATH) && find $(dir)/src/test/java -name "*.java" 2> /dev/null)))
+endef
+
+define all-core-resource-dirs
+$(shell cd $(LOCAL_PATH) && ls -d */src/$(1)/{java,resources} 2> /dev/null)
+endef
+
+# The Java files and their associated resources.
+core_src_files := $(call all-main-java-files-under,dalvik dom json luni support xml)
+core_resource_dirs := $(call all-core-resource-dirs,main)
+test_resource_dirs := $(call all-core-resource-dirs,test)
+
+ifeq ($(EMMA_INSTRUMENT),true)
+ifneq ($(EMMA_INSTRUMENT_STATIC),true)
+    core_src_files += $(call all-java-files-under, ../external/emma/core ../external/emma/pregenerated)
+    core_resource_dirs += ../external/emma/core/res ../external/emma/pregenerated/res
+endif
+endif
+
+local_javac_flags=-encoding UTF-8
+#local_javac_flags+=-Xlint:all -Xlint:-serial,-deprecation,-unchecked
+local_javac_flags+=-Xmaxwarns 9999999
+
+#
+# Build for the target (device).
+#
+
+# Definitions to make the core library.
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(core_src_files)
+LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
+
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := core
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+LOCAL_REQUIRED_MODULES := tzdata
+
+include $(BUILD_JAVA_LIBRARY)
+
+core-intermediates := ${intermediates}
+
+
+# Make the core-tests library.
+ifeq ($(LIBCORE_SKIP_TESTS),)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-test-java-files-under,dalvik dom json luni support xml)
+LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs)
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVA_LIBRARIES := bouncycastle core core-junit
+LOCAL_STATIC_JAVA_LIBRARIES := sqlite-jdbc mockwebserver nist-pkix-tests
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_MODULE := core-tests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+include $(BUILD_STATIC_JAVA_LIBRARY)
+endif
+
+# This one's tricky. One of our tests needs to have a
+# resource with a "#" in its name, but Perforce doesn't
+# allow us to submit such a file. So we create it here
+# on-the-fly.
+TMP_RESOURCE_DIR := $(intermediates.COMMON)/tmp/
+TMP_RESOURCE_FILE := org/apache/harmony/luni/tests/java/lang/test\#.properties
+
+$(TMP_RESOURCE_DIR)$(TMP_RESOURCE_FILE):
+	@mkdir -p $(dir $@)
+	@echo "Hello, world!" > $@
+
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EXTRA_JAR_ARGS := $(extra_jar_args) -C "$(TMP_RESOURCE_DIR)" "$(TMP_RESOURCE_FILE)"
+$(LOCAL_INTERMEDIATE_TARGETS): $(TMP_RESOURCE_DIR)$(TMP_RESOURCE_FILE)
+
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+
+    # Definitions to make the core library.
+
+    include $(CLEAR_VARS)
+
+    LOCAL_SRC_FILES := $(core_src_files)
+    LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
+
+    LOCAL_NO_STANDARD_LIBRARIES := true
+    LOCAL_JAVACFLAGS := $(local_javac_flags)
+    LOCAL_DX_FLAGS := --core-library
+
+    LOCAL_BUILD_HOST_DEX := true
+
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := core-hostdex
+    LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+    LOCAL_REQUIRED_MODULES := tzdata-host
+
+    include $(BUILD_HOST_JAVA_LIBRARY)
+
+    # Make the core-tests library.
+    ifeq ($(LIBCORE_SKIP_TESTS),)
+    include $(CLEAR_VARS)
+    LOCAL_SRC_FILES := $(call all-test-java-files-under,dalvik dom json luni support xml)
+    LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs)
+    LOCAL_NO_STANDARD_LIBRARIES := true
+    LOCAL_JAVA_LIBRARIES := bouncycastle-hostdex core-hostdex core-junit-hostdex
+    LOCAL_STATIC_JAVA_LIBRARIES := sqlite-jdbc-host mockwebserver-host nist-pkix-tests-host
+    LOCAL_JAVACFLAGS := $(local_javac_flags)
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := core-tests-hostdex
+    LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+    LOCAL_BUILD_HOST_DEX := true
+    include $(BUILD_HOST_JAVA_LIBRARY)
+    endif
+endif
+
+#
+# Local droiddoc for faster libcore testing
+#
+#
+# Run with:
+#     mm -j32 libcore-docs
+#
+# Main output:
+#     ../out/target/common/docs/libcore/reference/packages.html
+#
+# All text for proofreading (or running tools over):
+#     ../out/target/common/docs/libcore-proofread.txt
+#
+# TODO list of missing javadoc, etc:
+#     ../out/target/common/docs/libcore-docs-todo.html
+#
+# Rerun:
+#     rm -rf ../out/target/common/docs/libcore-timestamp && mm -j32 libcore-docs
+#
+include $(CLEAR_VARS)
+
+# for shared defintion of libcore_to_document
+include $(LOCAL_PATH)/Docs.mk
+
+LOCAL_SRC_FILES:=$(call libcore_to_document,$(LOCAL_PATH))
+# rerun doc generation without recompiling the java
+LOCAL_JAVA_LIBRARIES:=
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_MODULE_CLASS:=JAVA_LIBRARIES
+
+LOCAL_MODULE := libcore
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+
+LOCAL_DROIDDOC_OPTIONS := \
+ -offlinemode \
+ -title "libcore" \
+ -proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
+ -todo ../$(LOCAL_MODULE)-docs-todo.html \
+ -hdf android.whichdoc offline
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
+
+include $(BUILD_DROIDDOC)
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..818f6c5
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,382 @@
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Android-specific code.                        ==
+   =========================================================================
+
+Android Code
+Copyright 2005-2008 The Android Open Source Project
+
+This product includes software developed as part of
+The Android Open Source Project (http://source.android.com).
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Apache Harmony distribution.                  ==
+   =========================================================================
+
+Apache Harmony
+Copyright 2006 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+Portions of Harmony were originally developed by
+Intel Corporation and are licensed to the Apache Software
+Foundation under the "Software Grant and Corporate Contribution
+License Agreement", informally known as the "Intel Harmony CLA".
+
+
+   =========================================================================
+   ==  NOTICE file for the ICU License.                                   ==
+   =========================================================================
+
+Copyright (c) 1995-2009 International Business Machines Corporation and others
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, and/or sell copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies of
+the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
+SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+All trademarks and registered trademarks mentioned herein are the
+property of their respective owners.
+
+
+   =========================================================================
+   ==  NOTICE file for the JUnit License.                                 ==
+   =========================================================================
+
+Common Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
+THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+      a) in the case of the initial Contributor, the initial code and
+         documentation distributed under this Agreement, and
+      b) in the case of each subsequent Contributor:
+
+      i) changes to the Program, and
+
+      ii) additions to the Program;
+
+      where such changes and/or additions to the Program originate
+      from and are distributed by that particular Contributor. A
+      Contribution 'originates' from a Contributor if it was added to
+      the Program by such Contributor itself or anyone acting on such
+      Contributor's behalf. Contributions do not include additions to
+      the Program which: (i) are separate modules of software
+      distributed in conjunction with the Program under their own
+      license agreement, and (ii) are not derivative works of the
+      Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its Contribution
+alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+      a) Subject to the terms of this Agreement, each Contributor
+      hereby grants Recipient a non-exclusive, worldwide, royalty-free
+      copyright license to reproduce, prepare derivative works of,
+      publicly display, publicly perform, distribute and sublicense
+      the Contribution of such Contributor, if any, and such
+      derivative works, in source code and object code form.
+
+      b) Subject to the terms of this Agreement, each Contributor
+      hereby grants Recipient a non-exclusive, worldwide, royalty-free
+      patent license under Licensed Patents to make, use, sell, offer
+      to sell, import and otherwise transfer the Contribution of such
+      Contributor, if any, in source code and object code form. This
+      patent license shall apply to the combination of the
+      Contribution and the Program if, at the time the Contribution is
+      added by the Contributor, such addition of the Contribution
+      causes such combination to be covered by the Licensed Patents.
+      The patent license shall not apply to any other combinations
+      which include the Contribution. No hardware per se is licensed
+      hereunder.
+
+      c) Recipient understands that although each Contributor grants
+      the licenses to its Contributions set forth herein, no
+      assurances are provided by any Contributor that the Program does
+      not infringe the patent or other intellectual property rights of
+      any other entity. Each Contributor disclaims any liability to
+      Recipient for claims brought by any other entity based on
+      infringement of intellectual property rights or otherwise. As a
+      condition to exercising the rights and licenses granted
+      hereunder, each Recipient hereby assumes sole responsibility to
+      secure any other intellectual property rights needed, if any.
+      For example, if a third party patent license is required to
+      allow Recipient to distribute the Program, it is Recipient's
+      responsibility to acquire that license before distributing the
+      Program.
+
+      d) Each Contributor represents that to its knowledge it has
+      sufficient copyright rights in its Contribution, if any, to
+      grant the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form
+under its own license agreement, provided that:
+
+      a) it complies with the terms and conditions of this Agreement; and
+
+      b) its license agreement:
+
+      i) effectively disclaims on behalf of all Contributors all
+      warranties and conditions, express and implied, including
+      warranties or conditions of title and non-infringement, and
+      implied warranties or conditions of merchantability and fitness
+      for a particular purpose;
+
+      ii) effectively excludes on behalf of all Contributors all
+      liability for damages, including direct, indirect, special,
+      incidental and consequential damages, such as lost profits;
+
+      iii) states that any provisions which differ from this Agreement
+      are offered by that Contributor alone and not by any other
+      party; and
+
+      iv) states that source code for the Program is available from
+      such Contributor, and informs licensees how to obtain it in a
+      reasonable manner on or through a medium customarily used for
+      software exchange.
+
+When the Program is made available in source code form:
+
+      a) it must be made available under this Agreement; and 
+
+      b) a copy of this Agreement must be included with each copy of
+      the Program.
+
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use
+of the Program, the Contributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for other Contributors. Therefore, if a
+Contributor includes the Program in a commercial product offering,
+such Contributor ("Commercial Contributor") hereby agrees to defend
+and indemnify every other Contributor ("Indemnified Contributor")
+against any losses, damages and costs (collectively "Losses") arising
+from claims, lawsuits and other legal actions brought by a third party
+against the Indemnified Contributor to the extent caused by the acts
+or omissions of such Commercial Contributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement.
+In order to qualify, an Indemnified Contributor must: a) promptly
+notify the Commercial Contributor in writing of such claim, and b)
+allow the Commercial Contributor to control, and cooperate with the
+Commercial Contributor in, the defense and any related settlement
+negotiations. The Indemnified Contributor may participate in any such
+claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any other
+Contributor to pay any damages as a result, the Commercial Contributor
+must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement, including but not limited to
+the risks and costs of program errors, compliance with applicable
+laws, damage to or loss of data, programs or equipment, and
+unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against a Contributor with
+respect to a patent applicable to software (including a cross-claim or
+counterclaim in a lawsuit), then any patent licenses granted by that
+Contributor to such Recipient under this Agreement shall terminate as
+of the date such litigation is filed. In addition, if Recipient
+institutes patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and
+survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted and
+may only be modified in the following manner. The Agreement Steward
+reserves the right to publish new versions (including revisions) of
+this Agreement from time to time. No one other than the Agreement
+Steward has the right to modify this Agreement. IBM is the initial
+Agreement Steward. IBM may assign the responsibility to serve as the
+Agreement Steward to a suitable separate entity. Each new version of
+the Agreement will be given a distinguishing version number. The
+Program (including Contributions) may always be distributed subject to
+the version of the Agreement under which it was received. In addition,
+after a new version of the Agreement is published, Contributor may
+elect to distribute the Program (including its Contributions) under
+the new version. Except as expressly stated in Sections 2(a) and 2(b)
+above, Recipient receives no rights or licenses to the intellectual
+property of any Contributor under this Agreement, whether expressly,
+by implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this Agreement
+more than one year after the cause of action arose. Each party waives
+its rights to a jury trial in any resulting litigation.
+
+
+   =========================================================================
+   ==  NOTICE file for the KXML License.                                  ==
+   =========================================================================
+
+Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+   =========================================================================
+   ==  NOTICE file for the SQLite Java Wrapper License.                   ==
+   =========================================================================
+
+This software is copyrighted by Christian Werner <chw@ch-werner.de>
+and others. The following terms apply to all files associated with the
+software unless explicitly disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+
+   =========================================================================
+   ==  NOTICE file for the W3C License.                                   ==
+   =========================================================================
+
+Copyright (c) 2000 World Wide Web Consortium, (Massachusetts Institute
+of Technology, Institut National de Recherche en Informatique et en
+Automatique, Keio University). All Rights Reserved. This program is
+distributed under the W3C's Software Intellectual Property License.
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See W3C License http://www.w3.org/Consortium/Legal/ for more details.
diff --git a/NativeCode.mk b/NativeCode.mk
new file mode 100644
index 0000000..b7c8b0e
--- /dev/null
+++ b/NativeCode.mk
@@ -0,0 +1,183 @@
+# -*- mode: makefile -*-
+# Copyright (C) 2007 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.
+
+#
+# Definitions for building the native code needed for the core library.
+#
+
+#
+# Common definitions for host and target.
+#
+
+# These two definitions are used to help sanity check what's put in
+# sub.mk. See, the "error" directives immediately below.
+core_magic_local_target := ...//::default:://...
+core_local_path := $(LOCAL_PATH)
+
+# Include a submakefile, resolve its source file locations,
+# and stick them on core_src_files.  The submakefiles are
+# free to append to LOCAL_SRC_FILES, LOCAL_C_INCLUDES,
+# LOCAL_SHARED_LIBRARIES, or LOCAL_STATIC_LIBRARIES, but nothing
+# else. All other LOCAL_* variables will be ignored.
+#
+# $(1): directory containing the makefile to include
+define include-core-native-dir
+    LOCAL_SRC_FILES :=
+    include $(LOCAL_PATH)/$(1)/sub.mk
+    ifneq ($$(LOCAL_MODULE),$(core_magic_local_target))
+        $$(error $(LOCAL_PATH)/$(1)/sub.mk should not include CLEAR_VARS \
+            or define LOCAL_MODULE)
+    endif
+    ifneq ($$(LOCAL_PATH),$(core_local_path))
+        $$(error $(LOCAL_PATH)/$(1)/sub.mk should not define LOCAL_PATH)
+    endif
+    core_src_files += $$(addprefix $(1)/,$$(LOCAL_SRC_FILES))
+    LOCAL_SRC_FILES :=
+endef
+
+# Set up the default state. Note: We use CLEAR_VARS here, even though
+# we aren't quite defining a new rule yet, to make sure that the
+# sub.mk files don't see anything stray from the last rule that was
+# set up.
+
+# Set up the test library first
+ifeq ($(LIBCORE_SKIP_TESTS),)
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(core_magic_local_target)
+core_src_files :=
+
+# Include the sub.mk files.
+$(foreach dir, \
+    luni/src/test/native, \
+    $(eval $(call include-core-native-dir,$(dir))))
+
+# This is for the test library, so rename the variable.
+test_src_files := $(core_src_files)
+core_src_files :=
+
+# Extract out the allowed LOCAL_* variables. Note: $(sort) also
+# removes duplicates.
+test_c_includes := $(sort libcore/include $(LOCAL_C_INCLUDES) $(JNI_H_INCLUDE))
+test_shared_libraries := $(sort $(LOCAL_SHARED_LIBRARIES))
+test_static_libraries := $(sort $(LOCAL_STATIC_LIBRARIES))
+endif # LIBCORE_SKIP_TESTS
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(core_magic_local_target)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+core_src_files :=
+
+# Include the sub.mk files.
+$(foreach dir, \
+    dalvik/src/main/native luni/src/main/native, \
+    $(eval $(call include-core-native-dir,$(dir))))
+
+# Extract out the allowed LOCAL_* variables. Note: $(sort) also
+# removes duplicates.
+core_c_includes := $(sort libcore/include $(LOCAL_C_INCLUDES) $(JNI_H_INCLUDE))
+core_shared_libraries := $(sort $(LOCAL_SHARED_LIBRARIES))
+core_static_libraries := $(sort $(LOCAL_STATIC_LIBRARIES))
+
+
+#
+# Build for the target (device).
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS += -Wall -Wextra -Werror
+LOCAL_CFLAGS += $(core_cflags)
+LOCAL_CPPFLAGS += $(core_cppflags)
+ifeq ($(TARGET_ARCH),arm)
+# Ignore "note: the mangling of 'va_list' has changed in GCC 4.4"
+LOCAL_CFLAGS += -Wno-psabi
+endif
+
+# Define the rules.
+LOCAL_SRC_FILES := $(core_src_files)
+LOCAL_C_INCLUDES := $(core_c_includes)
+LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libexpat libicuuc libicui18n libssl libcrypto libz libnativehelper
+LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libjavacore
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+
+LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
+LOCAL_SHARED_LIBRARIES += libstlport
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+# Test library
+ifeq ($(LIBCORE_SKIP_TESTS),)
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS += -Wall -Wextra -Werror
+LOCAL_CFLAGS += $(core_cflags)
+LOCAL_CPPFLAGS += $(core_cppflags)
+ifeq ($(TARGET_ARCH),arm)
+# Ignore "note: the mangling of 'va_list' has changed in GCC 4.4"
+LOCAL_CFLAGS += -Wno-psabi
+endif
+
+# Define the rules.
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_C_INCLUDES := $(test_c_includes)
+LOCAL_SHARED_LIBRARIES := $(test_shared_libraries)
+LOCAL_STATIC_LIBRARIES := $(test_static_libraries)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libjavacoretests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+
+include $(BUILD_SHARED_LIBRARY)
+endif # LIBCORE_SKIP_TESTS
+
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+    include $(CLEAR_VARS)
+    # Define the rules.
+    LOCAL_SRC_FILES := $(core_src_files)
+    LOCAL_CFLAGS += $(core_cflags)
+    LOCAL_C_INCLUDES := $(core_c_includes)
+    LOCAL_CPPFLAGS += $(core_cppflags)
+    LOCAL_LDLIBS += -ldl -lpthread
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := libjavacore
+    LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+    LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libexpat libicuuc libicui18n libssl libcrypto libz-host
+    LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
+    include $(BUILD_HOST_SHARED_LIBRARY)
+
+    ifeq ($(LIBCORE_SKIP_TESTS),)
+    include $(CLEAR_VARS)
+    # Define the rules.
+    LOCAL_SRC_FILES := $(test_src_files)
+    LOCAL_CFLAGS += $(core_cflags)
+    LOCAL_C_INCLUDES := $(test_c_includes)
+    LOCAL_CPPFLAGS += $(core_cppflags)
+    LOCAL_LDLIBS += -ldl -lpthread
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := libjavacoretests
+    LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+    LOCAL_SHARED_LIBRARIES := $(test_shared_libraries)
+    LOCAL_STATIC_LIBRARIES := $(test_static_libraries)
+    include $(BUILD_HOST_SHARED_LIBRARY)
+    endif # LIBCORE_SKIP_TESTS
+endif
diff --git a/benchmarks/AdditionBenchmark.java b/benchmarks/AdditionBenchmark.java
new file mode 100644
index 0000000..a18856e
--- /dev/null
+++ b/benchmarks/AdditionBenchmark.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * What do various kinds of addition cost?
+ */
+public class AdditionBenchmark extends SimpleBenchmark {
+    public int timeAddConstantToLocalInt(int reps) {
+        int result = 0;
+        for (int i = 0; i < reps; ++i) {
+            result += 123;
+        }
+        return result;
+    }
+    public int timeAddTwoLocalInts(int reps) {
+        int result = 0;
+        int constant = 123;
+        for (int i = 0; i < reps; ++i) {
+            result += constant;
+        }
+        return result;
+    }
+    public long timeAddConstantToLocalLong(int reps) {
+        long result = 0;
+        for (int i = 0; i < reps; ++i) {
+            result += 123L;
+        }
+        return result;
+    }
+    public long timeAddTwoLocalLongs(int reps) {
+        long result = 0;
+        long constant = 123L;
+        for (int i = 0; i < reps; ++i) {
+            result += constant;
+        }
+        return result;
+    }
+    public float timeAddConstantToLocalFloat(int reps) {
+        float result = 0.0f;
+        for (int i = 0; i < reps; ++i) {
+            result += 123.0f;
+        }
+        return result;
+    }
+    public float timeAddTwoLocalFloats(int reps) {
+        float result = 0.0f;
+        float constant = 123.0f;
+        for (int i = 0; i < reps; ++i) {
+            result += constant;
+        }
+        return result;
+    }
+    public double timeAddConstantToLocalDouble(int reps) {
+        double result = 0.0;
+        for (int i = 0; i < reps; ++i) {
+            result += 123.0;
+        }
+        return result;
+    }
+    public double timeAddTwoLocalDoubles(int reps) {
+        double result = 0.0;
+        double constant = 123.0;
+        for (int i = 0; i < reps; ++i) {
+            result += constant;
+        }
+        return result;
+    }
+}
diff --git a/benchmarks/ArrayCopyBenchmark.java b/benchmarks/ArrayCopyBenchmark.java
new file mode 100644
index 0000000..75ad243
--- /dev/null
+++ b/benchmarks/ArrayCopyBenchmark.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.Arrays;
+
+public class ArrayCopyBenchmark extends SimpleBenchmark {
+    public void timeManualArrayCopy(int reps) {
+        char[] src = new char[8192];
+        for (int rep = 0; rep < reps; ++rep) {
+            char[] dst = new char[8192];
+            for (int i = 0; i < 8192; ++i) {
+                dst[i] = src[i];
+            }
+        }
+    }
+
+    public void time_System_arrayCopy(int reps) {
+        char[] src = new char[8192];
+        for (int rep = 0; rep < reps; ++rep) {
+            char[] dst = new char[8192];
+            System.arraycopy(src, 0, dst, 0, 8192);
+        }
+    }
+
+    public void time_Arrays_copyOf(int reps) {
+        char[] src = new char[8192];
+        for (int rep = 0; rep < reps; ++rep) {
+            char[] dst = Arrays.copyOf(src, 8192);
+        }
+    }
+
+    public void time_Arrays_copyOfRange(int reps) {
+        char[] src = new char[8192];
+        for (int rep = 0; rep < reps; ++rep) {
+            char[] dst = Arrays.copyOfRange(src, 0, 8192);
+        }
+    }
+}
diff --git a/benchmarks/ArrayIterationBenchmark.java b/benchmarks/ArrayIterationBenchmark.java
new file mode 100644
index 0000000..bdc255b
--- /dev/null
+++ b/benchmarks/ArrayIterationBenchmark.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * How do various ways of iterating through an array compare?
+ */
+public class ArrayIterationBenchmark extends SimpleBenchmark {
+    Foo[] mArray = new Foo[27];
+    {
+        for (int i = 0; i < mArray.length; ++i) mArray[i] = new Foo();
+    }
+    public void timeArrayIteration(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            int sum = 0;
+            for (int i = 0; i < mArray.length; i++) {
+                sum += mArray[i].mSplat;
+            }
+        }
+    }
+    public void timeArrayIterationCached(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            int sum = 0;
+            Foo[] localArray = mArray;
+            int len = localArray.length;
+            
+            for (int i = 0; i < len; i++) {
+                sum += localArray[i].mSplat;
+            }
+        }
+    }
+    public void timeArrayIterationForEach(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            int sum = 0;
+            for (Foo a: mArray) {
+                sum += a.mSplat;
+            }
+        }
+    }
+}
diff --git a/benchmarks/ArrayListIterationBenchmark.java b/benchmarks/ArrayListIterationBenchmark.java
new file mode 100644
index 0000000..4e5f145
--- /dev/null
+++ b/benchmarks/ArrayListIterationBenchmark.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.ArrayList;
+
+/**
+ * Is a hand-coded counted loop through an ArrayList cheaper than enhanced for?
+ */
+public class ArrayListIterationBenchmark extends SimpleBenchmark {
+    ArrayList<Foo> mList = new ArrayList<Foo>();
+    {
+        for (int i = 0; i < 27; ++i) mList.add(new Foo());
+    }
+    public void timeArrayListIterationIndexed(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            int sum = 0;
+            ArrayList<Foo> list = mList;
+            int len = list.size();
+            for (int i = 0; i < len; ++i) {
+                sum += list.get(i).mSplat;
+            }
+        }
+    }
+    public void timeArrayListIterationForEach(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            int sum = 0;
+            for (Foo a : mList) {
+                sum += a.mSplat;
+            }
+        }
+    }
+}
diff --git a/benchmarks/BufferedZipFileBenchmark.java b/benchmarks/BufferedZipFileBenchmark.java
new file mode 100644
index 0000000..f4d3822
--- /dev/null
+++ b/benchmarks/BufferedZipFileBenchmark.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public final class BufferedZipFileBenchmark extends SimpleBenchmark {
+    @Param({"128", "1024", "8192", "65536"}) int compressedSize;
+    @Param({"4", "32", "128"}) int readSize;
+
+    private File file;
+
+    @Override protected void setUp() throws Exception {
+        file = File.createTempFile(getClass().getName(), ".zip");
+        file.deleteOnExit();
+
+        Random random = new Random(0);
+        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
+        byte[] data = new byte[8192];
+        out.putNextEntry(new ZipEntry("entry.data"));
+        int written = 0;
+        while (written < compressedSize) {
+            random.nextBytes(data);
+            int toWrite = Math.min(compressedSize - written, data.length);
+            out.write(data, 0, toWrite);
+            written += toWrite;
+        }
+        out.close();
+    }
+
+    public void timeUnbufferedRead(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            ZipFile zipFile = new ZipFile(file);
+            ZipEntry entry = zipFile.getEntry("entry.data");
+            InputStream in = zipFile.getInputStream(entry);
+            byte[] buffer = new byte[readSize];
+            while (in.read(buffer) != -1) {
+            }
+            in.close();
+            zipFile.close();
+        }
+    }
+
+    public void timeBufferedRead(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            ZipFile zipFile = new ZipFile(file);
+            ZipEntry entry = zipFile.getEntry("entry.data");
+            InputStream in = new BufferedInputStream(zipFile.getInputStream(entry));
+            byte[] buffer = new byte[readSize];
+            while (in.read(buffer) != -1) {
+            }
+            in.close();
+            zipFile.close();
+        }
+    }
+}
diff --git a/benchmarks/FieldAccessBenchmark.java b/benchmarks/FieldAccessBenchmark.java
new file mode 100644
index 0000000..19cb060
--- /dev/null
+++ b/benchmarks/FieldAccessBenchmark.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * What does field access cost?
+ */
+public class FieldAccessBenchmark extends SimpleBenchmark {
+    private static class Inner {
+        public int publicInnerIntVal;
+        protected int protectedInnerIntVal;
+        private int privateInnerIntVal;
+        int packageInnerIntVal;
+    }
+    int intVal = 42;
+    final int finalIntVal = 42;
+    static int staticIntVal = 42;
+    static final int staticFinalIntVal = 42;
+    public int timeField(int reps) {
+        int result = 0;
+        for (int rep = 0; rep < reps; ++rep) {
+            result = intVal;
+        }
+        return result;
+    }
+    public int timeFieldFinal(int reps) {
+        int result = 0;
+        for (int rep = 0; rep < reps; ++rep) {
+            result = finalIntVal;
+        }
+        return result;
+    }
+    public int timeFieldStatic(int reps) {
+        int result = 0;
+        for (int rep = 0; rep < reps; ++rep) {
+            result = staticIntVal;
+        }
+        return result;
+    }
+    public int timeFieldStaticFinal(int reps) {
+        int result = 0;
+        for (int rep = 0; rep < reps; ++rep) {
+            result = staticFinalIntVal;
+        }
+        return result;
+    }
+    public int timeFieldCached(int reps) {
+        int result = 0;
+        int cachedIntVal = this.intVal;
+        for (int rep = 0; rep < reps; ++rep) {
+            result = cachedIntVal;
+        }
+        return result;
+    }
+    public int timeFieldPrivateInnerClassPublicField(int reps) {
+        int result = 0;
+        Inner inner = new Inner();
+        for (int rep = 0; rep < reps; ++rep) {
+            result = inner.publicInnerIntVal;
+        }
+        return result;
+    }
+    public int timeFieldPrivateInnerClassProtectedField(int reps) {
+        int result = 0;
+        Inner inner = new Inner();
+        for (int rep = 0; rep < reps; ++rep) {
+            result = inner.protectedInnerIntVal;
+        }
+        return result;
+    }
+    public int timeFieldPrivateInnerClassPrivateField(int reps) {
+        int result = 0;
+        Inner inner = new Inner();
+        for (int rep = 0; rep < reps; ++rep) {
+            result = inner.privateInnerIntVal;
+        }
+        return result;
+    }
+    public int timeFieldPrivateInnerClassPackageField(int reps) {
+        int result = 0;
+        Inner inner = new Inner();
+        for (int rep = 0; rep < reps; ++rep) {
+            result = inner.packageInnerIntVal;
+        }
+        return result;
+    }
+}
diff --git a/benchmarks/Foo.java b/benchmarks/Foo.java
new file mode 100644
index 0000000..d288dd6
--- /dev/null
+++ b/benchmarks/Foo.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+/**
+ * A trivial class used by several benchmarks.
+ */
+public class Foo {
+    int mSplat;
+}
diff --git a/benchmarks/HashedCollectionsBenchmark.java b/benchmarks/HashedCollectionsBenchmark.java
new file mode 100644
index 0000000..a826271
--- /dev/null
+++ b/benchmarks/HashedCollectionsBenchmark.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+
+/**
+ * How do the various hash maps compare?
+ */
+public class HashedCollectionsBenchmark extends SimpleBenchmark {
+    public void timeHashMapGet(int reps) {
+        HashMap<String, String> map = new HashMap<String, String>();
+        map.put("hello", "world");
+        for (int i = 0; i < reps; ++i) {
+            map.get("hello");
+        }
+    }
+    public void timeHashMapGet_Synchronized(int reps) {
+        HashMap<String, String> map = new HashMap<String, String>();
+        synchronized (map) {
+            map.put("hello", "world");
+        }
+        for (int i = 0; i < reps; ++i) {
+            synchronized (map) {
+                map.get("hello");
+            }
+        }
+    }
+    public void timeHashtableGet(int reps) {
+        Hashtable<String, String> map = new Hashtable<String, String>();
+        map.put("hello", "world");
+        for (int i = 0; i < reps; ++i) {
+            map.get("hello");
+        }
+    }
+    public void timeLinkedHashMapGet(int reps) {
+        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
+        map.put("hello", "world");
+        for (int i = 0; i < reps; ++i) {
+            map.get("hello");
+        }
+    }
+    public void timeConcurrentHashMapGet(int reps) {
+        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
+        map.put("hello", "world");
+        for (int i = 0; i < reps; ++i) {
+            map.get("hello");
+        }
+    }
+}
diff --git a/benchmarks/MethodInvocationBenchmark.java b/benchmarks/MethodInvocationBenchmark.java
new file mode 100644
index 0000000..7a5e1b6
--- /dev/null
+++ b/benchmarks/MethodInvocationBenchmark.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Compares various kinds of method invocation.
+ */
+public class MethodInvocationBenchmark extends SimpleBenchmark {
+    interface I {
+        void emptyInterface();
+    }
+
+    static class C implements I {
+        private int field;
+
+        private int getField() {
+            return field;
+        }
+
+        public int timeInternalGetter(int reps) {
+            int result = 0;
+            for (int i = 0; i < reps; ++i) {
+                result = getField();
+            }
+            return result;
+        }
+
+        public int timeInternalFieldAccess(int reps) {
+            int result = 0;
+            for (int i = 0; i < reps; ++i) {
+                result = field;
+            }
+            return result;
+        }
+
+        public static void emptyStatic() {
+        }
+
+        public void emptyVirtual() {
+        }
+
+        public void emptyInterface() {
+        }
+    }
+
+    public void timeInternalGetter(int reps) {
+        new C().timeInternalGetter(reps);
+    }
+
+    public void timeInternalFieldAccess(int reps) {
+        new C().timeInternalFieldAccess(reps);
+    }
+
+    // Test an intrinsic.
+    public int timeStringLength(int reps) {
+        int result = 0;
+        for (int i = 0; i < reps; ++i) {
+            result = "hello, world!".length();
+        }
+        return result;
+    }
+
+    public void timeEmptyStatic(int reps) {
+        C c = new C();
+        for (int i = 0; i < reps; ++i) {
+            c.emptyStatic();
+        }
+    }
+
+    public void timeEmptyVirtual(int reps) {
+        C c = new C();
+        for (int i = 0; i < reps; ++i) {
+            c.emptyVirtual();
+        }
+    }
+
+    public void timeEmptyInterface(int reps) {
+        I c = new C();
+        for (int i = 0; i < reps; ++i) {
+            c.emptyInterface();
+        }
+    }
+
+    public static class Inner {
+        private int i;
+        private void privateMethod() { ++i; }
+        protected void protectedMethod() { ++i; }
+        public void publicMethod() { ++i; }
+        void packageMethod() { ++i; }
+        final void finalPackageMethod() { ++i; }
+    }
+
+    public void timePrivateInnerPublicMethod(int reps) {
+        Inner inner = new Inner();
+        for (int i = 0; i < reps; ++i) {
+            inner.publicMethod();
+        }
+    }
+
+    public void timePrivateInnerProtectedMethod(int reps) {
+        Inner inner = new Inner();
+        for (int i = 0; i < reps; ++i) {
+            inner.protectedMethod();
+        }
+    }
+
+    public void timePrivateInnerPrivateMethod(int reps) {
+        Inner inner = new Inner();
+        for (int i = 0; i < reps; ++i) {
+            inner.privateMethod();
+        }
+    }
+
+    public void timePrivateInnerPackageMethod(int reps) {
+        Inner inner = new Inner();
+        for (int i = 0; i < reps; ++i) {
+            inner.packageMethod();
+        }
+    }
+
+    public void timePrivateInnerFinalPackageMethod(int reps) {
+        Inner inner = new Inner();
+        for (int i = 0; i < reps; ++i) {
+          inner.finalPackageMethod();
+        }
+    }
+}
diff --git a/benchmarks/MultiplicationBenchmark.java b/benchmarks/MultiplicationBenchmark.java
new file mode 100644
index 0000000..b2f945b
--- /dev/null
+++ b/benchmarks/MultiplicationBenchmark.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * How much do various kinds of multiplication cost?
+ */
+public class MultiplicationBenchmark extends SimpleBenchmark {
+    public int timeMultiplyIntByConstant10(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result *= 10;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByConstant8(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result *= 8;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByVariable10(int reps) {
+        int result = 1;
+        int factor = 10;
+        for (int i = 0; i < reps; ++i) {
+            result *= factor;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByVariable8(int reps) {
+        int result = 1;
+        int factor = 8;
+        for (int i = 0; i < reps; ++i) {
+            result *= factor;
+        }
+        return result;
+    }
+}
diff --git a/benchmarks/StringIterationBenchmark.java b/benchmarks/StringIterationBenchmark.java
new file mode 100644
index 0000000..22c6ae2
--- /dev/null
+++ b/benchmarks/StringIterationBenchmark.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * How do the various schemes for iterating through a string compare?
+ */
+public class StringIterationBenchmark extends SimpleBenchmark {
+    public void timeStringIteration0(int reps) {
+        String s = "hello, world!";
+        for (int rep = 0; rep < reps; ++rep) {
+            char ch;
+            for (int i = 0; i < s.length(); ++i) {
+                ch = s.charAt(i);
+            }
+        }
+    }
+    public void timeStringIteration1(int reps) {
+        String s = "hello, world!";
+        for (int rep = 0; rep < reps; ++rep) {
+            char ch;
+            for (int i = 0, length = s.length(); i < length; ++i) {
+                ch = s.charAt(i);
+            }
+        }
+    }
+    public void timeStringIteration2(int reps) {
+        String s = "hello, world!";
+        for (int rep = 0; rep < reps; ++rep) {
+            char ch;
+            char[] chars = s.toCharArray();
+            for (int i = 0, length = chars.length; i < length; ++i) {
+                ch = chars[i];
+            }
+        }
+    }
+    public void timeStringToCharArray(int reps) {
+        String s = "hello, world!";
+        for (int rep = 0; rep < reps; ++rep) {
+            char[] chars = s.toCharArray();
+        }
+    }
+}
diff --git a/benchmarks/VirtualVersusInterfaceBenchmark.java b/benchmarks/VirtualVersusInterfaceBenchmark.java
new file mode 100644
index 0000000..f029c81
--- /dev/null
+++ b/benchmarks/VirtualVersusInterfaceBenchmark.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Is there a performance reason to "Prefer virtual over interface", as the
+ * Android documentation once claimed?
+ */
+public class VirtualVersusInterfaceBenchmark extends SimpleBenchmark {
+    public void timeMapPut(int reps) {
+        Map<String, String> map = new HashMap<String, String>();
+        for (int i = 0; i < reps; ++i) {
+            map.put("hello", "world");
+        }
+    }
+    public void timeHashMapPut(int reps) {
+        HashMap<String, String> map = new HashMap<String, String>();
+        for (int i = 0; i < reps; ++i) {
+            map.put("hello", "world");
+        }
+    }
+}
diff --git a/benchmarks/XmlParseBenchmark.java b/benchmarks/XmlParseBenchmark.java
new file mode 100644
index 0000000..b5e7b93
--- /dev/null
+++ b/benchmarks/XmlParseBenchmark.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.List;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xmlpull.v1.XmlPullParser;
+
+public class XmlParseBenchmark extends SimpleBenchmark {
+
+    @Param String xmlFile;
+    ByteArrayInputStream inputStream;
+
+    static List<String> xmlFileValues = Arrays.asList(
+            "/etc/apns-conf.xml",
+            "/etc/media_profiles.xml",
+            "/etc/permissions/features.xml"
+    );
+
+    private SAXParser saxParser;
+    private DocumentBuilder documentBuilder;
+    private Constructor<? extends XmlPullParser> kxmlConstructor;
+    private Constructor<? extends XmlPullParser> expatConstructor;
+
+    @SuppressWarnings("unchecked")
+    @Override protected void setUp() throws Exception {
+        byte[] xmlBytes = getXmlBytes();
+        inputStream = new ByteArrayInputStream(xmlBytes);
+        inputStream.mark(xmlBytes.length);
+
+        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+        saxParser = saxParserFactory.newSAXParser();
+
+        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+        documentBuilder = builderFactory.newDocumentBuilder();
+
+        kxmlConstructor = (Constructor) Class.forName("org.kxml2.io.KXmlParser").getConstructor();
+        expatConstructor = (Constructor) Class.forName("org.apache.harmony.xml.ExpatPullParser")
+                .getConstructor();
+    }
+
+    private byte[] getXmlBytes() throws IOException {
+        FileInputStream fileIn = new FileInputStream(xmlFile);
+        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+        int count;
+        byte[] buffer = new byte[1024];
+        while ((count = fileIn.read(buffer)) != -1) {
+            bytesOut.write(buffer, 0, count);
+        }
+        fileIn.close();
+        return bytesOut.toByteArray();
+    }
+
+    public int timeSax(int reps) throws IOException, SAXException {
+        int elementCount = 0;
+        for (int i = 0; i < reps; i++) {
+            inputStream.reset();
+            ElementCounterSaxHandler elementCounterSaxHandler = new ElementCounterSaxHandler();
+            saxParser.parse(inputStream, elementCounterSaxHandler);
+            elementCount += elementCounterSaxHandler.elementCount;
+        }
+        return elementCount;
+    }
+
+    private static class ElementCounterSaxHandler extends DefaultHandler {
+        int elementCount = 0;
+        @Override public void startElement(String uri, String localName,
+                String qName, Attributes attributes) {
+            elementCount++;
+        }
+    }
+
+    public int timeDom(int reps) throws IOException, SAXException {
+        int elementCount = 0;
+        for (int i = 0; i < reps; i++) {
+            inputStream.reset();
+            Document document = documentBuilder.parse(inputStream);
+            elementCount += countDomElements(document.getDocumentElement());
+        }
+        return elementCount;
+    }
+
+    private int countDomElements(Node node) {
+        int result = 0;
+        for (; node != null; node = node.getNextSibling()) {
+            if (node.getNodeType() == Node.ELEMENT_NODE) {
+                result++;
+            }
+            result += countDomElements(node.getFirstChild());
+        }
+        return result;
+    }
+
+    public int timeExpat(int reps) throws Exception {
+        return testXmlPull(expatConstructor, reps);
+    }
+
+    public int timeKxml(int reps) throws Exception {
+        return testXmlPull(kxmlConstructor, reps);
+    }
+
+    private int testXmlPull(Constructor<? extends XmlPullParser> constructor, int reps)
+            throws Exception {
+        int elementCount = 0;
+        for (int i = 0; i < reps; i++) {
+            inputStream.reset();
+            XmlPullParser xmlPullParser = constructor.newInstance();
+            xmlPullParser.setInput(inputStream, "UTF-8");
+            int type;
+            while ((type = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
+                if (type == XmlPullParser.START_TAG) {
+                    elementCount++;
+                }
+            }
+        }
+        return elementCount;
+    }
+
+    public static void main(String[] args) {
+        Runner.main(XmlParseBenchmark.class, args);
+    }
+}
diff --git a/benchmarks/regression/AnnotatedElementBenchmark.java b/benchmarks/regression/AnnotatedElementBenchmark.java
new file mode 100644
index 0000000..6c33968
--- /dev/null
+++ b/benchmarks/regression/AnnotatedElementBenchmark.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class AnnotatedElementBenchmark extends SimpleBenchmark {
+
+    private Class<?> type;
+    private Field field;
+    private Method method;
+
+    @Override protected void setUp() throws Exception {
+        type = Type.class;
+        field = Type.class.getField("field");
+        method = Type.class.getMethod("method", String.class);
+    }
+
+
+    // get annotations by member type and method
+
+    public void timeGetTypeAnnotations(int reps) {
+        for (int i = 0; i < reps; i++) {
+            type.getAnnotations();
+        }
+    }
+
+    public void timeGetFieldAnnotations(int reps) {
+        for (int i = 0; i < reps; i++) {
+            field.getAnnotations();
+        }
+    }
+
+    public void timeGetMethodAnnotations(int reps) {
+        for (int i = 0; i < reps; i++) {
+            method.getAnnotations();
+        }
+    }
+
+    public void timeGetParameterAnnotations(int reps) {
+        for (int i = 0; i < reps; i++) {
+            method.getParameterAnnotations();
+        }
+    }
+
+    public void timeGetTypeAnnotation(int reps) {
+        for (int i = 0; i < reps; i++) {
+            type.getAnnotation(Marker.class);
+        }
+    }
+
+    public void timeGetFieldAnnotation(int reps) {
+        for (int i = 0; i < reps; i++) {
+            field.getAnnotation(Marker.class);
+        }
+    }
+
+    public void timeGetMethodAnnotation(int reps) {
+        for (int i = 0; i < reps; i++) {
+            method.getAnnotation(Marker.class);
+        }
+    }
+
+    public void timeIsTypeAnnotationPresent(int reps) {
+        for (int i = 0; i < reps; i++) {
+            type.isAnnotationPresent(Marker.class);
+        }
+    }
+
+    public void timeIsFieldAnnotationPresent(int reps) {
+        for (int i = 0; i < reps; i++) {
+            field.isAnnotationPresent(Marker.class);
+        }
+    }
+
+    public void timeIsMethodAnnotationPresent(int reps) {
+        for (int i = 0; i < reps; i++) {
+            method.isAnnotationPresent(Marker.class);
+        }
+    }
+
+    // get annotations by result size
+
+    public void timeGetAllReturnsLargeAnnotation(int reps) {
+        for (int i = 0; i < reps; i++) {
+            HasLargeAnnotation.class.getAnnotations();
+        }
+    }
+
+    public void timeGetAllReturnsSmallAnnotation(int reps) {
+        for (int i = 0; i < reps; i++) {
+            HasSmallAnnotation.class.getAnnotations();
+        }
+    }
+
+    public void timeGetAllReturnsMarkerAnnotation(int reps) {
+        for (int i = 0; i < reps; i++) {
+            HasMarkerAnnotation.class.getAnnotations();
+        }
+    }
+
+    public void timeGetAllReturnsNoAnnotation(int reps) {
+        for (int i = 0; i < reps; i++) {
+            HasNoAnnotations.class.getAnnotations();
+        }
+    }
+
+    public void timeGetAllReturnsThreeAnnotations(int reps) {
+        for (int i = 0; i < reps; i++) {
+            HasThreeAnnotations.class.getAnnotations();
+        }
+    }
+
+
+    // get annotations with inheritance
+
+    public void timeGetAnnotationsOnSubclass(int reps) {
+        for (int i = 0; i < reps; i++) {
+            ExtendsHasThreeAnnotations.class.getAnnotations();
+        }
+    }
+
+    public void timeGetDeclaredAnnotationsOnSubclass(int reps) {
+        for (int i = 0; i < reps; i++) {
+            ExtendsHasThreeAnnotations.class.getAnnotations();
+        }
+    }
+
+
+    // the annotated elements
+
+    @Marker
+    public class Type {
+        @Marker public String field;
+        @Marker public void method(@Marker String parameter) {}
+    }
+
+    @Large(a = "on class", b = {"A", "B", "C" },
+            c = @Small(e="E1", f=1695938256, g=7264081114510713000L),
+            d = { @Small(e="E2", f=1695938256, g=7264081114510713000L) })
+    public class HasLargeAnnotation {}
+
+    @Small(e="E1", f=1695938256, g=7264081114510713000L)
+    public class HasSmallAnnotation {}
+
+    @Marker
+    public class HasMarkerAnnotation {}
+
+    public class HasNoAnnotations {}
+
+    @Large(a = "on class", b = {"A", "B", "C" },
+            c = @Small(e="E1", f=1695938256, g=7264081114510713000L),
+            d = { @Small(e="E2", f=1695938256, g=7264081114510713000L) })
+    @Small(e="E1", f=1695938256, g=7264081114510713000L)
+    @Marker
+    public class HasThreeAnnotations {}
+
+    public class ExtendsHasThreeAnnotations {}
+
+
+    // the annotations
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Marker {}
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Large {
+        String a() default "";
+        String[] b() default {};
+        Small c() default @Small;
+        Small[] d() default {};
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Small {
+        String e() default "";
+        int f() default 0;
+        long g() default 0L;
+    }
+
+    public static void main(String[] args) throws Exception {
+        Runner.main(AnnotatedElementBenchmark.class, args);
+    }
+}
diff --git a/benchmarks/regression/BigIntegerBenchmark.java b/benchmarks/regression/BigIntegerBenchmark.java
new file mode 100644
index 0000000..841b901
--- /dev/null
+++ b/benchmarks/regression/BigIntegerBenchmark.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.math.BigInteger;
+import java.util.Random;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class BigIntegerBenchmark extends SimpleBenchmark {
+    public void timeRandomDivision(int reps) throws Exception {
+        Random r = new Random();
+        BigInteger x = new BigInteger(1024, r);
+        BigInteger y = new BigInteger(1024, r);
+        for (int i = 0; i < reps; ++i) {
+            x.divide(y);
+        }
+    }
+
+    public void timeRandomGcd(int reps) throws Exception {
+        Random r = new Random();
+        BigInteger x = new BigInteger(1024, r);
+        BigInteger y = new BigInteger(1024, r);
+        for (int i = 0; i < reps; ++i) {
+            x.gcd(y);
+        }
+    }
+
+    public void timeRandomMultiplication(int reps) throws Exception {
+        Random r = new Random();
+        BigInteger x = new BigInteger(1024, r);
+        BigInteger y = new BigInteger(1024, r);
+        for (int i = 0; i < reps; ++i) {
+            x.multiply(y);
+        }
+    }
+}
diff --git a/benchmarks/regression/BitSetBenchmark.java b/benchmarks/regression/BitSetBenchmark.java
new file mode 100644
index 0000000..ee91993
--- /dev/null
+++ b/benchmarks/regression/BitSetBenchmark.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.util.BitSet;
+
+public class BitSetBenchmark extends SimpleBenchmark {
+    @Param({ "1000", "10000" })
+    private int size;
+
+    private BitSet bs;
+
+    @Override protected void setUp() throws Exception {
+        bs = new BitSet(size);
+    }
+
+    public void timeIsEmptyTrue(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            if (!bs.isEmpty()) throw new RuntimeException();
+        }
+    }
+
+    public void timeIsEmptyFalse(int reps) {
+        bs.set(bs.size() - 1);
+        for (int i = 0; i < reps; ++i) {
+            if (bs.isEmpty()) throw new RuntimeException();
+        }
+    }
+
+    public void timeGet(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            bs.get(i % size);
+        }
+    }
+
+    public void timeClear(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            bs.clear(i % size);
+        }
+    }
+
+    public void timeSet(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            bs.set(i % size);
+        }
+    }
+
+    public void timeSetOn(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            bs.set(i % size, true);
+        }
+    }
+
+    public void timeSetOff(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            bs.set(i % size, false);
+        }
+    }
+}
diff --git a/benchmarks/regression/ByteBufferBenchmark.java b/benchmarks/regression/ByteBufferBenchmark.java
new file mode 100644
index 0000000..7812013
--- /dev/null
+++ b/benchmarks/regression/ByteBufferBenchmark.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.Arrays;
+import java.util.Collection;
+
+public class ByteBufferBenchmark extends SimpleBenchmark {
+    public enum MyByteOrder {
+        BIG(ByteOrder.BIG_ENDIAN), LITTLE(ByteOrder.LITTLE_ENDIAN);
+        final ByteOrder byteOrder;
+        MyByteOrder(ByteOrder byteOrder) {
+            this.byteOrder = byteOrder;
+        }
+    }
+
+    @Param private MyByteOrder byteOrder;
+
+    @Param({"true", "false"}) private boolean aligned;
+
+    enum MyBufferType {
+        DIRECT, HEAP, MAPPED;
+    }
+    @Param private MyBufferType bufferType;
+
+    public static ByteBuffer newBuffer(MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws IOException {
+        int size = aligned ? 8192 : 8192 + 8 + 1;
+        ByteBuffer result = null;
+        switch (bufferType) {
+        case DIRECT:
+            result = ByteBuffer.allocateDirect(size);
+            break;
+        case HEAP:
+            result = ByteBuffer.allocate(size);
+            break;
+        case MAPPED:
+            File tmpFile = new File("/sdcard/bm.tmp");
+            if (new File("/tmp").isDirectory()) {
+                // We're running on the desktop.
+                tmpFile = File.createTempFile("MappedByteBufferTest", ".tmp");
+            }
+            tmpFile.createNewFile();
+            tmpFile.deleteOnExit();
+            RandomAccessFile raf = new RandomAccessFile(tmpFile, "rw");
+            raf.setLength(8192*8);
+            FileChannel fc = raf.getChannel();
+            result = fc.map(FileChannel.MapMode.READ_WRITE, 0, fc.size());
+            break;
+        }
+        result.order(byteOrder.byteOrder);
+        result.position(aligned ? 0 : 1);
+        return result;
+    }
+
+    //
+    // peeking
+    //
+
+    public void timeByteBuffer_getByte(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.get();
+            }
+        }
+    }
+
+    public void timeByteBuffer_getByteArray(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        byte[] dst = new byte[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                src.position(aligned ? 0 : 1);
+                src.get(dst);
+            }
+        }
+    }
+
+    public void timeByteBuffer_getByte_indexed(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.get(i);
+            }
+        }
+    }
+
+    public void timeByteBuffer_getChar(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.getChar();
+            }
+        }
+    }
+
+    public void timeCharBuffer_getCharArray(int reps) throws Exception {
+        CharBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asCharBuffer();
+        char[] dst = new char[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                src.position(0);
+                src.get(dst);
+            }
+        }
+    }
+
+    public void timeByteBuffer_getChar_indexed(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.getChar(i * 2);
+            }
+        }
+    }
+
+    public void timeByteBuffer_getDouble(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.getDouble();
+            }
+        }
+    }
+
+    public void timeDoubleBuffer_getDoubleArray(int reps) throws Exception {
+        DoubleBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asDoubleBuffer();
+        double[] dst = new double[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                src.position(0);
+                src.get(dst);
+            }
+        }
+    }
+
+    public void timeByteBuffer_getFloat(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.getFloat();
+            }
+        }
+    }
+
+    public void timeFloatBuffer_getFloatArray(int reps) throws Exception {
+        FloatBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asFloatBuffer();
+        float[] dst = new float[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                src.position(0);
+                src.get(dst);
+            }
+        }
+    }
+
+    public void timeByteBuffer_getInt(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.getInt();
+            }
+        }
+    }
+
+    public void timeIntBuffer_getIntArray(int reps) throws Exception {
+        IntBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asIntBuffer();
+        int[] dst = new int[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                src.position(0);
+                src.get(dst);
+            }
+        }
+    }
+
+    public void timeByteBuffer_getLong(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.getLong();
+            }
+        }
+    }
+
+    public void timeLongBuffer_getLongArray(int reps) throws Exception {
+        LongBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asLongBuffer();
+        long[] dst = new long[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                src.position(0);
+                src.get(dst);
+            }
+        }
+    }
+
+    public void timeByteBuffer_getShort(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.getShort();
+            }
+        }
+    }
+
+    public void timeShortBuffer_getShortArray(int reps) throws Exception {
+        ShortBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asShortBuffer();
+        short[] dst = new short[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                src.position(0);
+                src.get(dst);
+            }
+        }
+    }
+
+    //
+    // poking
+    //
+
+    public void timeByteBuffer_putByte(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(0);
+            for (int i = 0; i < 1024; ++i) {
+                src.put((byte) 0);
+            }
+        }
+    }
+
+    public void timeByteBuffer_putByteArray(int reps) throws Exception {
+        ByteBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        byte[] src = new byte[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                dst.position(aligned ? 0 : 1);
+                dst.put(src);
+            }
+        }
+    }
+
+    public void timeByteBuffer_putChar(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.putChar(' ');
+            }
+        }
+    }
+
+    public void timeCharBuffer_putCharArray(int reps) throws Exception {
+        CharBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asCharBuffer();
+        char[] src = new char[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                dst.position(0);
+                dst.put(src);
+            }
+        }
+    }
+
+    public void timeByteBuffer_putDouble(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.putDouble(0.0);
+            }
+        }
+    }
+
+    public void timeDoubleBuffer_putDoubleArray(int reps) throws Exception {
+        DoubleBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asDoubleBuffer();
+        double[] src = new double[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                dst.position(0);
+                dst.put(src);
+            }
+        }
+    }
+
+    public void timeByteBuffer_putFloat(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.putFloat(0.0f);
+            }
+        }
+    }
+
+    public void timeFloatBuffer_putFloatArray(int reps) throws Exception {
+        FloatBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asFloatBuffer();
+        float[] src = new float[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                dst.position(0);
+                dst.put(src);
+            }
+        }
+    }
+
+    public void timeByteBuffer_putInt(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.putInt(0);
+            }
+        }
+    }
+
+    public void timeIntBuffer_putIntArray(int reps) throws Exception {
+        IntBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asIntBuffer();
+        int[] src = new int[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                dst.position(0);
+                dst.put(src);
+            }
+        }
+    }
+
+    public void timeByteBuffer_putLong(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.putLong(0L);
+            }
+        }
+    }
+
+    public void timeLongBuffer_putLongArray(int reps) throws Exception {
+        LongBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asLongBuffer();
+        long[] src = new long[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                dst.position(0);
+                dst.put(src);
+            }
+        }
+    }
+
+    public void timeByteBuffer_putShort(int reps) throws Exception {
+        ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+        for (int rep = 0; rep < reps; ++rep) {
+            src.position(aligned ? 0 : 1);
+            for (int i = 0; i < 1024; ++i) {
+                src.putShort((short) 0);
+            }
+        }
+    }
+
+    public void timeShortBuffer_putShortArray(int reps) throws Exception {
+        ShortBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asShortBuffer();
+        short[] src = new short[1024];
+        for (int rep = 0; rep < reps; ++rep) {
+            for (int i = 0; i < 1024; ++i) {
+                dst.position(0);
+                dst.put(src);
+            }
+        }
+    }
+
+/*
+    public void time_new_byteArray(int reps) throws Exception {
+        for (int rep = 0; rep < reps; ++rep) {
+            byte[] bs = new byte[8192];
+        }
+    }
+
+    public void time_ByteBuffer_allocate(int reps) throws Exception {
+        for (int rep = 0; rep < reps; ++rep) {
+            ByteBuffer bs = ByteBuffer.allocate(8192);
+        }
+    }
+    */
+}
diff --git a/benchmarks/regression/ByteBufferScalarVersusVectorBenchmark.java b/benchmarks/regression/ByteBufferScalarVersusVectorBenchmark.java
new file mode 100644
index 0000000..7c75deb
--- /dev/null
+++ b/benchmarks/regression/ByteBufferScalarVersusVectorBenchmark.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.Arrays;
+import java.util.Collection;
+
+public class ByteBufferScalarVersusVectorBenchmark extends SimpleBenchmark {
+  @Param private ByteBufferBenchmark.MyByteOrder byteOrder;
+  @Param({"true", "false"}) private boolean aligned;
+  @Param private ByteBufferBenchmark.MyBufferType bufferType;
+
+  public void timeManualByteBufferCopy(int reps) throws Exception {
+    ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+    ByteBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+    for (int rep = 0; rep < reps; ++rep) {
+      src.position(0);
+      dst.position(0);
+      for (int i = 0; i < 8192; ++i) {
+        dst.put(src.get());
+      }
+    }
+  }
+
+  public void timeByteBufferBulkGet(int reps) throws Exception {
+    ByteBuffer src = ByteBuffer.allocate(aligned ? 8192 : 8192 + 1);
+    byte[] dst = new byte[8192];
+    for (int rep = 0; rep < reps; ++rep) {
+      src.position(aligned ? 0 : 1);
+      src.get(dst, 0, dst.length);
+    }
+  }
+
+  public void timeDirectByteBufferBulkGet(int reps) throws Exception {
+    ByteBuffer src = ByteBuffer.allocateDirect(aligned ? 8192 : 8192 + 1);
+    byte[] dst = new byte[8192];
+    for (int rep = 0; rep < reps; ++rep) {
+      src.position(aligned ? 0 : 1);
+      src.get(dst, 0, dst.length);
+    }
+  }
+}
diff --git a/benchmarks/regression/CharacterBenchmark.java b/benchmarks/regression/CharacterBenchmark.java
new file mode 100644
index 0000000..953513b
--- /dev/null
+++ b/benchmarks/regression/CharacterBenchmark.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Tests various Character methods, intended for testing multiple
+ * implementations against each other.
+ */
+public class CharacterBenchmark extends SimpleBenchmark {
+
+    @Param private CharacterSet characterSet;
+
+    @Param private Overload overload;
+
+    private char[] chars;
+
+    @Override protected void setUp() throws Exception {
+        this.chars = characterSet.chars;
+    }
+
+    public enum Overload { CHAR, INT }
+
+    @Override public double nanosToUnits(double nanos) {
+        return nanos / 65536;
+    }
+
+    public enum CharacterSet {
+        ASCII(128),
+        UNICODE(65536);
+        final char[] chars;
+        CharacterSet(int size) {
+            this.chars = new char[65536];
+            for (int i = 0; i < 65536; ++i) {
+                chars[i] = (char) (i % size);
+            }
+        }
+    }
+
+    // A fake benchmark to give us a baseline.
+    public boolean timeIsSpace(int reps) {
+        boolean dummy = false;
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    dummy ^= ((char) ch == ' ');
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    dummy ^= (ch == ' ');
+                }
+            }
+        }
+        return dummy;
+    }
+
+    public void timeDigit(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.digit(chars[ch], 10);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.digit((int) chars[ch], 10);
+                }
+            }
+        }
+    }
+
+    public void timeGetNumericValue(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.getNumericValue(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.getNumericValue((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeIsDigit(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isDigit(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isDigit((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeIsIdentifierIgnorable(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isIdentifierIgnorable(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isIdentifierIgnorable((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeIsJavaIdentifierPart(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isJavaIdentifierPart(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isJavaIdentifierPart((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeIsJavaIdentifierStart(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isJavaIdentifierStart(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isJavaIdentifierStart((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeIsLetter(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isLetter(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isLetter((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeIsLetterOrDigit(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isLetterOrDigit(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isLetterOrDigit((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeIsLowerCase(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isLowerCase(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isLowerCase((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeIsSpaceChar(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isSpaceChar(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isSpaceChar((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeIsUpperCase(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isUpperCase(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isUpperCase((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeIsWhitespace(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isWhitespace(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.isWhitespace((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeToLowerCase(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.toLowerCase(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.toLowerCase((int) chars[ch]);
+                }
+            }
+        }
+    }
+
+    public void timeToUpperCase(int reps) {
+        if (overload == Overload.CHAR) {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.toUpperCase(chars[ch]);
+                }
+            }
+        } else {
+            for (int i = 0; i < reps; ++i) {
+                for (int ch = 0; ch < 65536; ++ch) {
+                    Character.toUpperCase((int) chars[ch]);
+                }
+            }
+        }
+    }
+}
diff --git a/benchmarks/regression/CharsetBenchmark.java b/benchmarks/regression/CharsetBenchmark.java
new file mode 100644
index 0000000..6ecada7
--- /dev/null
+++ b/benchmarks/regression/CharsetBenchmark.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.nio.charset.Charset;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class CharsetBenchmark extends SimpleBenchmark {
+    @Param({ "1", "10", "100", "1000", "10000" })
+    private int length;
+
+    // canonical    => canonical charset name
+    // built-in     => guaranteed-present charset
+    // special-case => libcore treats this charset specially for performance
+    @Param({
+        "UTF-16",     //     canonical,     built-in, non-special-case
+        "UTF-8",      //     canonical,     built-in,     special-case
+        "UTF8",       // non-canonical,     built-in,     special-case
+        "ISO-8859-1", //     canonical,     built-in,     special-case
+        "8859_1",     // non-canonical,     built-in,     special-case
+        "ISO-8859-2", //     canonical, non-built-in, non-special-case
+        "8859_2",     // non-canonical, non-built-in, non-special-case
+        "US-ASCII",   //     canonical,     built-in,     special-case
+        "ASCII"       // non-canonical,     built-in,     special-case
+    })
+    private String name;
+
+    public void time_new_String_BString(int reps) throws Exception {
+        byte[] bytes = makeBytes(makeString(length));
+        for (int i = 0; i < reps; ++i) {
+            new String(bytes, name);
+        }
+    }
+
+    public void time_new_String_BII(int reps) throws Exception {
+      byte[] bytes = makeBytes(makeString(length));
+      for (int i = 0; i < reps; ++i) {
+        new String(bytes, 0, bytes.length);
+      }
+    }
+
+    public void time_new_String_BIIString(int reps) throws Exception {
+      byte[] bytes = makeBytes(makeString(length));
+      for (int i = 0; i < reps; ++i) {
+        new String(bytes, 0, bytes.length, name);
+      }
+    }
+
+    public void time_String_getBytes(int reps) throws Exception {
+        String string = makeString(length);
+        for (int i = 0; i < reps; ++i) {
+            string.getBytes(name);
+        }
+    }
+
+    // FIXME: benchmark this pure-java implementation for US-ASCII and ISO-8859-1 too!
+
+    /**
+     * Translates the given characters to US-ASCII or ISO-8859-1 bytes, using the fact that
+     * Unicode code points between U+0000 and U+007f inclusive are identical to US-ASCII, while
+     * U+0000 to U+00ff inclusive are identical to ISO-8859-1.
+     */
+    private static byte[] toDirectMappedBytes(char[] chars, int offset, int length, int maxValidChar) {
+        byte[] result = new byte[length];
+        int o = offset;
+        for (int i = 0; i < length; ++i) {
+            int ch = chars[o++];
+            result[i] = (byte) ((ch <= maxValidChar) ? ch : '?');
+        }
+        return result;
+    }
+
+    private static byte[] toUtf8Bytes(char[] chars, int offset, int length) {
+        UnsafeByteSequence result = new UnsafeByteSequence(length);
+        toUtf8Bytes(chars, offset, length, result);
+        return result.toByteArray();
+    }
+
+    private static void toUtf8Bytes(char[] chars, int offset, int length, UnsafeByteSequence out) {
+        final int end = offset + length;
+        for (int i = offset; i < end; ++i) {
+            int ch = chars[i];
+            if (ch < 0x80) {
+                // One byte.
+                out.write(ch);
+            } else if (ch < 0x800) {
+                // Two bytes.
+                out.write((ch >> 6) | 0xc0);
+                out.write((ch & 0x3f) | 0x80);
+            } else if (ch >= Character.MIN_SURROGATE && ch <= Character.MAX_SURROGATE) {
+                // A supplementary character.
+                char high = (char) ch;
+                char low = (i + 1 != end) ? chars[i + 1] : '\u0000';
+                if (!Character.isSurrogatePair(high, low)) {
+                    out.write('?');
+                    continue;
+                }
+                // Now we know we have a *valid* surrogate pair, we can consume the low surrogate.
+                ++i;
+                ch = Character.toCodePoint(high, low);
+                // Four bytes.
+                out.write((ch >> 18) | 0xf0);
+                out.write(((ch >> 12) & 0x3f) | 0x80);
+                out.write(((ch >> 6) & 0x3f) | 0x80);
+                out.write((ch & 0x3f) | 0x80);
+            } else {
+                // Three bytes.
+                out.write((ch >> 12) | 0xe0);
+                out.write(((ch >> 6) & 0x3f) | 0x80);
+                out.write((ch & 0x3f) | 0x80);
+            }
+        }
+    }
+
+    private static String makeString(int length) {
+        StringBuilder result = new StringBuilder(length);
+        for (int i = 0; i < length; ++i) {
+            result.append('A' + (i % 26));
+        }
+        return result.toString();
+    }
+
+    private static byte[] makeBytes(String s) {
+        try {
+            return s.getBytes("US-ASCII");
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+}
diff --git a/benchmarks/regression/CharsetForNameBenchmark.java b/benchmarks/regression/CharsetForNameBenchmark.java
new file mode 100644
index 0000000..87dfb8b
--- /dev/null
+++ b/benchmarks/regression/CharsetForNameBenchmark.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.nio.charset.Charset;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class CharsetForNameBenchmark extends SimpleBenchmark {
+    // canonical    => canonical charset name
+    // built-in     => guaranteed-present charset
+    // special-case => libcore treats this charset specially for performance
+    @Param({
+        "UTF-16",     //     canonical,     built-in, non-special-case
+        "UTF-8",      //     canonical,     built-in,     special-case
+        "UTF8",       // non-canonical,     built-in,     special-case
+        "ISO-8859-1", //     canonical,     built-in,     special-case
+        "8859_1",     // non-canonical,     built-in,     special-case
+        "ISO-8859-2", //     canonical, non-built-in, non-special-case
+        "8859_2",     // non-canonical, non-built-in, non-special-case
+        "US-ASCII",   //     canonical,     built-in,     special-case
+        "ASCII"       // non-canonical,     built-in,     special-case
+    })
+    private String charsetName;
+
+    public void timeCharsetForName(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            Charset.forName(charsetName);
+        }
+    }
+}
diff --git a/benchmarks/regression/ChecksumBenchmark.java b/benchmarks/regression/ChecksumBenchmark.java
new file mode 100644
index 0000000..0e7bf8d
--- /dev/null
+++ b/benchmarks/regression/ChecksumBenchmark.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.util.zip.Adler32;
+import java.util.zip.CRC32;
+
+public class ChecksumBenchmark extends SimpleBenchmark {
+    public void timeAdler_block(int reps) throws Exception {
+        byte[] bytes = new byte[10000];
+        Adler32 adler = new Adler32();
+        for (int i = 0; i < reps; ++i) {
+            adler.update(bytes);
+        }
+    }
+    public void timeAdler_byte(int reps) throws Exception {
+        Adler32 adler = new Adler32();
+        for (int i = 0; i < reps; ++i) {
+            adler.update(1);
+        }
+    }
+    public void timeCrc_block(int reps) throws Exception {
+        byte[] bytes = new byte[10000];
+        CRC32 crc = new CRC32();
+        for (int i = 0; i < reps; ++i) {
+            crc.update(bytes);
+        }
+    }
+    public void timeCrc_byte(int reps) throws Exception {
+        CRC32 crc = new CRC32();
+        for (int i = 0; i < reps; ++i) {
+            crc.update(1);
+        }
+    }
+}
diff --git a/benchmarks/regression/CipherBenchmark.java b/benchmarks/regression/CipherBenchmark.java
new file mode 100644
index 0000000..56d549b
--- /dev/null
+++ b/benchmarks/regression/CipherBenchmark.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * Cipher benchmarks. Only runs on AES currently because of the combinatorial
+ * explosion of the test as it stands.
+ */
+public class CipherBenchmark extends SimpleBenchmark {
+
+    private static final int DATA_SIZE = 8192;
+    private static final byte[] DATA = new byte[DATA_SIZE];
+
+    private static final int IV_SIZE = 16;
+
+    private static final byte[] IV = new byte[IV_SIZE];
+
+    static {
+        for (int i = 0; i < DATA_SIZE; i++) {
+            DATA[i] = (byte) i;
+        }
+        for (int i = 0; i < IV_SIZE; i++) {
+            IV[i] = (byte) i;
+        }
+    }
+
+    @Param private Algorithm algorithm;
+
+    public enum Algorithm {
+        AES,
+    };
+
+    @Param private Mode mode;
+
+    public enum Mode {
+        CBC,
+        CFB,
+        CTR,
+        ECB,
+        OFB,
+    };
+
+    @Param private Padding padding;
+
+    public enum Padding {
+        NOPADDING,
+        PKCS1PADDING,
+    };
+
+    @Param({"128", "192", "256"}) private int keySize;
+
+    @Param({"16", "32", "64", "128", "1024", "8192"}) private int inputSize;
+
+    @Param private Implementation implementation;
+
+    public enum Implementation { OpenSSL, BouncyCastle };
+
+    private String providerName;
+
+    // Key generation isn't part of the benchmark so cache the results
+    private static Map<Integer, SecretKey> KEY_SIZES = new HashMap<Integer, SecretKey>();
+
+    private String cipherAlgorithm;
+    private SecretKey key;
+
+    private byte[] output = new byte[DATA.length];
+
+    private Cipher cipherEncrypt;
+
+    private Cipher cipherDecrypt;
+
+    private AlgorithmParameterSpec spec;
+
+    @Override protected void setUp() throws Exception {
+        cipherAlgorithm = algorithm.toString() + "/" + mode.toString() + "/"
+                + padding.toString();
+
+        String keyAlgorithm = algorithm.toString();
+        key = KEY_SIZES.get(keySize);
+        if (key == null) {
+            KeyGenerator generator = KeyGenerator.getInstance(keyAlgorithm);
+            generator.init(keySize);
+            key = generator.generateKey();
+            KEY_SIZES.put(keySize, key);
+        }
+
+        switch (implementation) {
+            case OpenSSL:
+                providerName = "AndroidOpenSSL";
+                break;
+            case BouncyCastle:
+                providerName = "BC";
+                break;
+            default:
+                throw new RuntimeException(implementation.toString());
+        }
+
+        if (mode != Mode.ECB) {
+            spec = new IvParameterSpec(IV);
+        }
+
+        cipherEncrypt = Cipher.getInstance(cipherAlgorithm, providerName);
+        cipherEncrypt.init(Cipher.ENCRYPT_MODE, key, spec);
+
+        cipherDecrypt = Cipher.getInstance(cipherAlgorithm, providerName);
+        cipherDecrypt.init(Cipher.DECRYPT_MODE, key, spec);
+    }
+
+    public void timeEncrypt(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            cipherEncrypt.doFinal(DATA, 0, inputSize, output);
+        }
+    }
+
+    public void timeDecrypt(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            cipherDecrypt.doFinal(DATA, 0, inputSize, output);
+        }
+    }
+}
diff --git a/benchmarks/regression/DateToStringBenchmark.java b/benchmarks/regression/DateToStringBenchmark.java
new file mode 100644
index 0000000..15760c9
--- /dev/null
+++ b/benchmarks/regression/DateToStringBenchmark.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import android.text.format.DateFormat;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+public final class DateToStringBenchmark extends SimpleBenchmark {
+    Date date;
+    Calendar calendar;
+    SimpleDateFormat format;
+
+    @Override
+    protected void setUp() throws Exception {
+        date = new Date(0);
+        calendar = new GregorianCalendar();
+        calendar.setTime(date);
+        format = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
+    }
+
+    public void timeDateToString(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            date.toString();
+        }
+    }
+
+    public void timeDateToString_Formatter(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy").format(date);
+        }
+    }
+
+    public void timeDateToString_ClonedFormatter(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            ((SimpleDateFormat) format.clone()).format(date);
+        }
+    }
+
+    public void timeDateToString_AndroidDateFormat(int reps) {
+        for (int i = 0; i < reps; i++) {
+            DateFormat.format("EEE MMM dd kk:mm:ss zzz yyyy", calendar);
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        Runner.main(DateToStringBenchmark.class, args);
+    }
+}
diff --git a/benchmarks/regression/DefaultCharsetBenchmark.java b/benchmarks/regression/DefaultCharsetBenchmark.java
new file mode 100644
index 0000000..bc7b013
--- /dev/null
+++ b/benchmarks/regression/DefaultCharsetBenchmark.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.nio.charset.Charset;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class DefaultCharsetBenchmark extends SimpleBenchmark {
+    public void time_defaultCharset(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            Charset.defaultCharset();
+        }
+    }
+}
diff --git a/benchmarks/regression/DigestBenchmark.java b/benchmarks/regression/DigestBenchmark.java
new file mode 100644
index 0000000..7d00fec
--- /dev/null
+++ b/benchmarks/regression/DigestBenchmark.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import com.android.org.bouncycastle.crypto.Digest;
+
+public class DigestBenchmark extends SimpleBenchmark {
+
+    private static final int DATA_SIZE = 8192;
+    private static final byte[] DATA = new byte[DATA_SIZE];
+    static {
+        for (int i = 0; i < DATA_SIZE; i++) {
+            DATA[i] = (byte)i;
+        }
+    }
+
+    @Param private Algorithm algorithm;
+
+    public enum Algorithm { MD5, SHA1, SHA256,  SHA384, SHA512 };
+
+    @Param private Implementation implementation;
+
+    public enum Implementation { OPENSSL, BOUNCYCASTLE };
+
+    private Class<? extends Digest> digestClass;
+
+    @Override protected void setUp() throws Exception {
+        String className = "com.android.org.bouncycastle.crypto.digests.";
+        switch (implementation) {
+            case OPENSSL:
+                className += ("OpenSSLDigest$" + algorithm);
+                break;
+            case BOUNCYCASTLE:
+                className += (algorithm + "Digest");
+                break;
+            default:
+                throw new RuntimeException(implementation.toString());
+        }
+        this.digestClass = (Class<? extends Digest>)Class.forName(className);
+    }
+
+    public void time(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            Digest digest = digestClass.newInstance();
+            digest.update(DATA, 0, DATA_SIZE);
+            digest.doFinal(new byte[digest.getDigestSize()], 0);
+        }
+    }
+}
diff --git a/benchmarks/regression/DnsBenchmark.java b/benchmarks/regression/DnsBenchmark.java
new file mode 100644
index 0000000..2a71716
--- /dev/null
+++ b/benchmarks/regression/DnsBenchmark.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+public class DnsBenchmark extends SimpleBenchmark {
+    public void timeDns(int reps) throws Exception {
+        String[] hosts = new String[] {
+            "www.amazon.com",
+            "z-ecx.images-amazon.com",
+            "g-ecx.images-amazon.com",
+            "ecx.images-amazon.com",
+            "ad.doubleclick.com",
+            "bpx.a9.com",
+            "d3dtik4dz1nej0.cloudfront.net",
+            "uac.advertising.com",
+            "servedby.advertising.com",
+            "view.atdmt.com",
+            "rmd.atdmt.com",
+            "spe.atdmt.com",
+            "www.google.com",
+            "www.cnn.com",
+            "bad.host.mtv.corp.google.com",
+        };
+        for (int i = 0; i < reps; ++i) {
+            try {
+                InetAddress.getByName(hosts[i % hosts.length]);
+            } catch (UnknownHostException ex) {
+            }
+        }
+    }
+}
diff --git a/benchmarks/regression/DoPrivilegedBenchmark.java b/benchmarks/regression/DoPrivilegedBenchmark.java
new file mode 100644
index 0000000..effb284
--- /dev/null
+++ b/benchmarks/regression/DoPrivilegedBenchmark.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+public class DoPrivilegedBenchmark extends SimpleBenchmark {
+    public void timeDirect(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            String lineSeparator = System.getProperty("line.separator");
+        }
+    }
+    
+    public void timeFastAndSlow(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            String lineSeparator;
+            if (System.getSecurityManager() == null) {
+                lineSeparator = System.getProperty("line.separator");
+            } else {
+                lineSeparator = AccessController.doPrivileged(new PrivilegedAction<String>() {
+                    public String run() {
+                        return System.getProperty("line.separator");
+                    }
+                });
+            }
+        }
+    }
+    
+    public void timeNewAction(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            String lineSeparator = AccessController.doPrivileged(new PrivilegedAction<String>() {
+                public String run() {
+                    return System.getProperty("line.separator");
+                }
+            });
+        }
+    }
+    
+    public void timeReusedAction(int reps) throws Exception {
+        final PrivilegedAction<String> action = new ReusableAction("line.separator");
+        for (int i = 0; i < reps; ++i) {
+            String lineSeparator = AccessController.doPrivileged(action);
+        }
+    }
+    
+    private static final class ReusableAction implements PrivilegedAction<String> {
+        private final String propertyName;
+        
+        public ReusableAction(String propertyName) {
+            this.propertyName = propertyName;
+        }
+        
+        public String run() {
+            return System.getProperty(propertyName);
+        }
+    }
+}
diff --git a/benchmarks/regression/DoubleBenchmark.java b/benchmarks/regression/DoubleBenchmark.java
new file mode 100644
index 0000000..aa692ac
--- /dev/null
+++ b/benchmarks/regression/DoubleBenchmark.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class DoubleBenchmark extends SimpleBenchmark {
+    private double d = 1.2;
+    private long l = 4608083138725491507L;
+
+    public void timeDoubleToLongBits(int reps) {
+        long result = 123;
+        for (int rep = 0; rep < reps; ++rep) {
+            result = Double.doubleToLongBits(d);
+        }
+        if (result != l) {
+            throw new RuntimeException(Long.toString(result));
+        }
+    }
+
+    public void timeDoubleToRawLongBits(int reps) {
+        long result = 123;
+        for (int rep = 0; rep < reps; ++rep) {
+            result = Double.doubleToRawLongBits(d);
+        }
+        if (result != l) {
+            throw new RuntimeException(Long.toString(result));
+        }
+    }
+
+    public void timeLongBitsToDouble(int reps) {
+        double result = 123.0;
+        for (int rep = 0; rep < reps; ++rep) {
+            result = Double.longBitsToDouble(l);
+        }
+        if (result != d) {
+            throw new RuntimeException(Double.toString(result) + " " + Double.doubleToRawLongBits(result));
+        }
+    }
+}
diff --git a/benchmarks/regression/EqualsHashCodeBenchmark.java b/benchmarks/regression/EqualsHashCodeBenchmark.java
new file mode 100644
index 0000000..a15a41a
--- /dev/null
+++ b/benchmarks/regression/EqualsHashCodeBenchmark.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.net.URI;
+import java.net.URL;
+
+public final class EqualsHashCodeBenchmark extends SimpleBenchmark {
+    private enum Type {
+        URI() {
+            @Override Object newInstance(String text) throws Exception {
+                return new URI(text);
+            }
+        },
+        URL() {
+            @Override Object newInstance(String text) throws Exception {
+                return new URL(text);
+            }
+        };
+        abstract Object newInstance(String text) throws Exception;
+    }
+
+    @Param Type type;
+
+    Object a1;
+    Object a2;
+    Object b1;
+    Object b2;
+
+    @Override protected void setUp() throws Exception {
+        a1 = type.newInstance("https://mail.google.com/mail/u/0/?shva=1#inbox");
+        a2 = type.newInstance("https://mail.google.com/mail/u/0/?shva=1#inbox");
+        b1 = type.newInstance("http://developer.android.com/reference/java/net/URI.html");
+        b2 = type.newInstance("http://developer.android.com/reference/java/net/URI.html");
+    }
+
+    public void timeEquals(int reps) {
+        for (int i = 0; i < reps; i+=3) {
+            a1.equals(b1);
+            a1.equals(a2);
+            b1.equals(b2);
+        }
+    }
+
+    public void timeHashCode(int reps) {
+        for (int i = 0; i < reps; i+=2) {
+            a1.hashCode();
+            b1.hashCode();
+        }
+    }
+}
diff --git a/benchmarks/regression/ExpensiveObjectsBenchmark.java b/benchmarks/regression/ExpensiveObjectsBenchmark.java
new file mode 100644
index 0000000..535e298
--- /dev/null
+++ b/benchmarks/regression/ExpensiveObjectsBenchmark.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.text.*;
+import java.util.*;
+
+/**
+ * Benchmarks creation and cloning various expensive objects.
+ */
+public class ExpensiveObjectsBenchmark extends SimpleBenchmark {
+    public void timeNewDateFormatTimeInstance(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
+            df.format(System.currentTimeMillis());
+        }
+    }
+
+    public void timeClonedDateFormatTimeInstance(int reps) {
+        DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
+        for (int i = 0; i < reps; ++i) {
+            ((DateFormat) df.clone()).format(System.currentTimeMillis());
+        }
+    }
+
+    public void timeReusedDateFormatTimeInstance(int reps) {
+        DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
+        for (int i = 0; i < reps; ++i) {
+            synchronized (df) {
+                df.format(System.currentTimeMillis());
+            }
+        }
+    }
+
+    public void timeNewCollator(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            Collator.getInstance(Locale.US);
+        }
+    }
+
+    public void timeClonedCollator(int reps) {
+        Collator c = Collator.getInstance(Locale.US);
+        for (int i = 0; i < reps; ++i) {
+            c.clone();
+        }
+    }
+
+    public void timeNewDateFormatSymbols(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            new DateFormatSymbols(Locale.US);
+        }
+    }
+
+    public void timeClonedDateFormatSymbols(int reps) {
+        DateFormatSymbols dfs = new DateFormatSymbols(Locale.US);
+        for (int i = 0; i < reps; ++i) {
+            dfs.clone();
+        }
+    }
+
+    public void timeNewDecimalFormatSymbols(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            new DecimalFormatSymbols(Locale.US);
+        }
+    }
+
+    public void timeClonedDecimalFormatSymbols(int reps) {
+        DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
+        for (int i = 0; i < reps; ++i) {
+            dfs.clone();
+        }
+    }
+
+    public void timeNewNumberFormat(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            NumberFormat.getInstance(Locale.US);
+        }
+    }
+
+    public void timeClonedNumberFormat(int reps) {
+        NumberFormat nf = NumberFormat.getInstance(Locale.US);
+        for (int i = 0; i < reps; ++i) {
+            nf.clone();
+        }
+    }
+
+    public void timeNumberFormatTrivialFormatLong(int reps) {
+        NumberFormat nf = NumberFormat.getInstance(Locale.US);
+        for (int i = 0; i < reps; ++i) {
+            nf.format(1024L);
+        }
+    }
+
+    public void timeLongToString(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            Long.toString(1024L);
+        }
+    }
+
+    public void timeNumberFormatTrivialFormatDouble(int reps) {
+        NumberFormat nf = NumberFormat.getInstance(Locale.US);
+        for (int i = 0; i < reps; ++i) {
+            nf.format(1024.0);
+        }
+    }
+
+    public void timeNewSimpleDateFormat(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            new SimpleDateFormat();
+        }
+    }
+
+    public void timeClonedSimpleDateFormat(int reps) {
+        SimpleDateFormat sdf = new SimpleDateFormat();
+        for (int i = 0; i < reps; ++i) {
+            sdf.clone();
+        }
+    }
+
+    public void timeNewGregorianCalendar(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            new GregorianCalendar();
+        }
+    }
+
+    public void timeClonedGregorianCalendar(int reps) {
+        GregorianCalendar gc = new GregorianCalendar();
+        for (int i = 0; i < reps; ++i) {
+            gc.clone();
+        }
+    }
+}
diff --git a/benchmarks/regression/FloatBenchmark.java b/benchmarks/regression/FloatBenchmark.java
new file mode 100644
index 0000000..a92bb69
--- /dev/null
+++ b/benchmarks/regression/FloatBenchmark.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class FloatBenchmark extends SimpleBenchmark {
+    private float f = 1.2f;
+    private int i = 1067030938;
+
+    public void timeFloatToIntBits(int reps) {
+        int result = 123;
+        for (int rep = 0; rep < reps; ++rep) {
+            result = Float.floatToIntBits(f);
+        }
+        if (result != i) {
+            throw new RuntimeException(Integer.toString(result));
+        }
+    }
+
+    public void timeFloatToRawIntBits(int reps) {
+        int result = 123;
+        for (int rep = 0; rep < reps; ++rep) {
+            result = Float.floatToRawIntBits(f);
+        }
+        if (result != i) {
+            throw new RuntimeException(Integer.toString(result));
+        }
+    }
+
+    public void timeIntBitsToFloat(int reps) {
+        float result = 123.0f;
+        for (int rep = 0; rep < reps; ++rep) {
+            result = Float.intBitsToFloat(i);
+        }
+        if (result != f) {
+            throw new RuntimeException(Float.toString(result) + " " + Float.floatToRawIntBits(result));
+        }
+    }
+}
diff --git a/benchmarks/regression/FormatterBenchmark.java b/benchmarks/regression/FormatterBenchmark.java
new file mode 100644
index 0000000..0d4cf26
--- /dev/null
+++ b/benchmarks/regression/FormatterBenchmark.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.util.Formatter;
+import java.util.Locale;
+
+/**
+ * Compares Formatter against hand-written StringBuilder code.
+ */
+public class FormatterBenchmark extends SimpleBenchmark {
+    public void timeFormatter_NoFormatting(int reps) {
+        for (int i = 0; i < reps; i++) {
+            Formatter f = new Formatter();
+            f.format("this is a reasonably short string that doesn't actually need any formatting");
+        }
+    }
+
+    public void timeStringBuilder_NoFormatting(int reps) {
+        for (int i = 0; i < reps; i++) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("this is a reasonably short string that doesn't actually need any formatting");
+        }
+    }
+
+    public void timeFormatter_OneInt(int reps) {
+        Integer value = Integer.valueOf(1024); // We're not trying to benchmark boxing here.
+        for (int i = 0; i < reps; i++) {
+            Formatter f = new Formatter();
+            f.format("this is a reasonably short string that has an int %d in it", value);
+        }
+    }
+
+    public void timeFormatter_OneIntArabic(int reps) {
+        Locale arabic = new Locale("ar");
+        Integer value = Integer.valueOf(1024); // We're not trying to benchmark boxing here.
+        for (int i = 0; i < reps; i++) {
+            Formatter f = new Formatter();
+            f.format(arabic, "this is a reasonably short string that has an int %d in it", value);
+        }
+    }
+
+    public void timeStringBuilder_OneInt(int reps) {
+        for (int i = 0; i < reps; i++) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("this is a reasonably short string that has an int ");
+            sb.append(1024);
+            sb.append(" in it");
+        }
+    }
+
+    public void timeFormatter_OneHexInt(int reps) {
+        Integer value = Integer.valueOf(1024); // We're not trying to benchmark boxing here.
+        for (int i = 0; i < reps; i++) {
+            Formatter f = new Formatter();
+            f.format("this is a reasonably short string that has an int %x in it", value);
+        }
+    }
+
+    public void timeStringBuilder_OneHexInt(int reps) {
+        for (int i = 0; i < reps; i++) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("this is a reasonably short string that has an int ");
+            sb.append(Integer.toHexString(1024));
+            sb.append(" in it");
+        }
+    }
+
+    public void timeFormatter_OneFloat(int reps) {
+        Float value = Float.valueOf(10.24f); // We're not trying to benchmark boxing here.
+        for (int i = 0; i < reps; i++) {
+            Formatter f = new Formatter();
+            f.format("this is a reasonably short string that has a float %f in it", value);
+        }
+    }
+
+    public void timeFormatter_OneFloat_dot2f(int reps) {
+        Float value = Float.valueOf(10.24f); // We're not trying to benchmark boxing here.
+        for (int i = 0; i < reps; i++) {
+            Formatter f = new Formatter();
+            f.format("this is a reasonably short string that has a float %.2f in it", value);
+        }
+    }
+
+    public void timeFormatter_TwoFloats(int reps) {
+        Float value = Float.valueOf(10.24f); // We're not trying to benchmark boxing here.
+        for (int i = 0; i < reps; i++) {
+            Formatter f = new Formatter();
+            f.format("this is a reasonably short string that has two floats %f and %f in it", value, value);
+        }
+    }
+
+    public void timeStringBuilder_OneFloat(int reps) {
+        for (int i = 0; i < reps; i++) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("this is a reasonably short string that has a float ");
+            sb.append(10.24f);
+            sb.append(" in it");
+        }
+    }
+
+    public void timeFormatter_OneString(int reps) {
+        for (int i = 0; i < reps; i++) {
+            Formatter f = new Formatter();
+            f.format("this is a reasonably short string that has a string %s in it", "hello");
+        }
+    }
+
+    public void timeStringBuilder_OneString(int reps) {
+        for (int i = 0; i < reps; i++) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("this is a reasonably short string that has a string ");
+            sb.append("hello");
+            sb.append(" in it");
+        }
+    }
+}
diff --git a/benchmarks/regression/HostnameVerifierBenchmark.java b/benchmarks/regression/HostnameVerifierBenchmark.java
new file mode 100644
index 0000000..e9218c4
--- /dev/null
+++ b/benchmarks/regression/HostnameVerifierBenchmark.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.io.ByteArrayInputStream;
+import java.net.URL;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+
+/**
+ * This benchmark makes a real HTTP connection to a handful of hosts and
+ * captures the served certificates as a byte array. It then verifies each
+ * certificate in the benchmark loop, being careful to convert from the
+ * byte[] to the certificate each time. Otherwise the certificate class
+ * caches previous results which skews the results of the benchmark: In practice
+ * each certificate instance is verified once and then released.
+ */
+public final class HostnameVerifierBenchmark extends SimpleBenchmark {
+
+    @Param({"android.clients.google.com",
+            "m.google.com",
+            "www.google.com",
+            "www.amazon.com",
+            "www.ubs.com"}) String host;
+
+    private String hostname;
+    private HostnameVerifier hostnameVerifier;
+    private byte[][] encodedCertificates;
+
+    @Override protected void setUp() throws Exception {
+        URL url = new URL("https", host, "/");
+        hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
+        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+        connection.setHostnameVerifier(new HostnameVerifier() {
+            public boolean verify(String hostname, SSLSession sslSession) {
+                try {
+                    encodedCertificates = certificatesToBytes(sslSession.getPeerCertificates());
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+                HostnameVerifierBenchmark.this.hostname = hostname;
+                return true;
+            }
+        });
+        connection.getInputStream();
+        connection.disconnect();
+    }
+
+    public void timeVerify(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            final Certificate[] certificates = bytesToCertificates(encodedCertificates);
+            FakeSSLSession sslSession = new FakeSSLSession() {
+                @Override public Certificate[] getPeerCertificates() {
+                    return certificates;
+                }
+            };
+            hostnameVerifier.verify(hostname, sslSession);
+        }
+    }
+
+    private byte[][] certificatesToBytes(Certificate[] certificates) throws Exception {
+        byte[][] result = new byte[certificates.length][];
+        for (int i = 0, certificatesLength = certificates.length; i < certificatesLength; i++) {
+            result[i] = certificates[i].getEncoded();
+        }
+        return result;
+    }
+
+    private Certificate[] bytesToCertificates(byte[][] encodedCertificates) throws Exception {
+        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+        Certificate[] result = new Certificate[encodedCertificates.length];
+        for (int i = 0; i < encodedCertificates.length; i++) {
+            result[i] = certificateFactory.generateCertificate(
+                new ByteArrayInputStream(encodedCertificates[i]));
+        }
+        return result;
+    }
+
+    private static class FakeSSLSession implements SSLSession {
+        public int getApplicationBufferSize() {
+            throw new UnsupportedOperationException();
+        }
+        public String getCipherSuite() {
+            throw new UnsupportedOperationException();
+        }
+        public long getCreationTime() {
+            throw new UnsupportedOperationException();
+        }
+        public byte[] getId() {
+            throw new UnsupportedOperationException();
+        }
+        public long getLastAccessedTime() {
+            throw new UnsupportedOperationException();
+        }
+        public Certificate[] getLocalCertificates() {
+            throw new UnsupportedOperationException();
+        }
+        public Principal getLocalPrincipal() {
+            throw new UnsupportedOperationException();
+        }
+        public int getPacketBufferSize() {
+            throw new UnsupportedOperationException();
+        }
+        public javax.security.cert.X509Certificate[] getPeerCertificateChain() {
+            throw new UnsupportedOperationException();
+        }
+        public Certificate[] getPeerCertificates() {
+            throw new UnsupportedOperationException();
+        }
+        public String getPeerHost() {
+            throw new UnsupportedOperationException();
+        }
+        public int getPeerPort() {
+            throw new UnsupportedOperationException();
+        }
+        public Principal getPeerPrincipal() {
+            throw new UnsupportedOperationException();
+        }
+        public String getProtocol() {
+            throw new UnsupportedOperationException();
+        }
+        public SSLSessionContext getSessionContext() {
+            throw new UnsupportedOperationException();
+        }
+        public Object getValue(String name) {
+            throw new UnsupportedOperationException();
+        }
+        public String[] getValueNames() {
+            throw new UnsupportedOperationException();
+        }
+        public void invalidate() {
+            throw new UnsupportedOperationException();
+        }
+        public boolean isValid() {
+            throw new UnsupportedOperationException();
+        }
+        public void putValue(String name, Object value) {
+            throw new UnsupportedOperationException();
+        }
+        public void removeValue(String name) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    public static void main(String[] args) {
+        Runner.main(HostnameVerifierBenchmark.class, args);
+    }
+}
diff --git a/benchmarks/regression/IntConstantDivisionBenchmark.java b/benchmarks/regression/IntConstantDivisionBenchmark.java
new file mode 100644
index 0000000..498b783
--- /dev/null
+++ b/benchmarks/regression/IntConstantDivisionBenchmark.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class IntConstantDivisionBenchmark extends SimpleBenchmark {
+    public int timeDivideIntByConstant2(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result /= 2;
+        }
+        return result;
+    }
+    public int timeDivideIntByConstant8(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result /= 8;
+        }
+        return result;
+    }
+    public int timeDivideIntByConstant10(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result /= 10;
+        }
+        return result;
+    }
+    public int timeDivideIntByConstant100(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result /= 100;
+        }
+        return result;
+    }
+    public int timeDivideIntByConstant100_HandOptimized(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result = (int) ((0x51eb851fL * result) >>> 37);
+        }
+        return result;
+    }
+    public int timeDivideIntByConstant2048(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result /= 2048;
+        }
+        return result;
+    }
+    public int timeDivideIntByVariable2(int reps) {
+        int result = 1;
+        int factor = 2;
+        for (int i = 0; i < reps; ++i) {
+            result /= factor;
+        }
+        return result;
+    }
+    public int timeDivideIntByVariable10(int reps) {
+        int result = 1;
+        int factor = 10;
+        for (int i = 0; i < reps; ++i) {
+            result /= factor;
+        }
+        return result;
+    }
+}
diff --git a/benchmarks/regression/IntConstantMultiplicationBenchmark.java b/benchmarks/regression/IntConstantMultiplicationBenchmark.java
new file mode 100644
index 0000000..ca1349c
--- /dev/null
+++ b/benchmarks/regression/IntConstantMultiplicationBenchmark.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class IntConstantMultiplicationBenchmark extends SimpleBenchmark {
+    public int timeMultiplyIntByConstant6(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result *= 6;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByConstant7(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result *= 7;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByConstant8(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result *= 8;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByConstant8_Shift(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result <<= 3;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByConstant10(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result *= 10;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByConstant10_Shift(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result = (result + (result << 2)) << 1;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByConstant2047(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result *= 2047;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByConstant2048(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result *= 2048;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByConstant2049(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result *= 2049;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByVariable10(int reps) {
+        int result = 1;
+        int factor = 10;
+        for (int i = 0; i < reps; ++i) {
+            result *= factor;
+        }
+        return result;
+    }
+    public int timeMultiplyIntByVariable8(int reps) {
+        int result = 1;
+        int factor = 8;
+        for (int i = 0; i < reps; ++i) {
+            result *= factor;
+        }
+        return result;
+    }
+}
diff --git a/benchmarks/regression/IntConstantRemainderBenchmark.java b/benchmarks/regression/IntConstantRemainderBenchmark.java
new file mode 100644
index 0000000..6174721
--- /dev/null
+++ b/benchmarks/regression/IntConstantRemainderBenchmark.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class IntConstantRemainderBenchmark extends SimpleBenchmark {
+    public int timeRemainderIntByConstant2(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result %= 2;
+        }
+        return result;
+    }
+    public int timeRemainderIntByConstant8(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result %= 8;
+        }
+        return result;
+    }
+/*
+    public int timeRemainderIntByConstant10(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result %= 10;
+        }
+        return result;
+    }
+    public int timeRemainderIntByConstant100(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result %= 100;
+        }
+        return result;
+    }
+*/
+    public int timeRemainderIntByConstant2048(int reps) {
+        int result = 1;
+        for (int i = 0; i < reps; ++i) {
+            result %= 2048;
+        }
+        return result;
+    }
+    public int timeRemainderIntByVariable2(int reps) {
+        int result = 1;
+        int factor = 2;
+        for (int i = 0; i < reps; ++i) {
+            result %= factor;
+        }
+        return result;
+    }
+/*
+    public int timeRemainderIntByVariable10(int reps) {
+        int result = 1;
+        int factor = 10;
+        for (int i = 0; i < reps; ++i) {
+            result %= factor;
+        }
+        return result;
+    }
+*/
+}
diff --git a/benchmarks/regression/IntegerBenchmark.java b/benchmarks/regression/IntegerBenchmark.java
new file mode 100644
index 0000000..601bb5d
--- /dev/null
+++ b/benchmarks/regression/IntegerBenchmark.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class IntegerBenchmark extends SimpleBenchmark {
+    public int timeLongSignumBranch(int reps) {
+        int t = 0;
+        for (int i = 0; i < reps; ++i) {
+            t += signum1(-i);
+            t += signum1(0);
+            t += signum1(i);
+        }
+        return t;
+    }
+
+    public int timeLongSignumBranchFree(int reps) {
+        int t = 0;
+        for (int i = 0; i < reps; ++i) {
+            t += signum2(-i);
+            t += signum2(0);
+            t += signum2(i);
+        }
+        return t;
+    }
+
+    private static int signum1(long v) {
+        return v < 0 ? -1 : (v == 0 ? 0 : 1);
+    }
+
+    private static int signum2(long v) {
+        return ((int)(v >> 63)) | (int) (-v >>> 63); // Hacker's delight 2-7
+    }
+
+    public int timeLongBitCount_BitSet(int reps) {
+        int t = 0;
+        for (int i = 0; i < reps; ++i) {
+            t += pop((long) i);
+        }
+        return t;
+    }
+
+    private static int pop(long l) {
+        int count = popX(l & 0xffffffffL);
+        count += popX(l >>> 32);
+        return count;
+    }
+
+    private static int popX(long x) {
+        // BEGIN android-note
+        // delegate to Integer.bitCount(i); consider using native code
+        // END android-note
+        x = x - ((x >>> 1) & 0x55555555);
+        x = (x & 0x33333333) + ((x >>> 2) & 0x33333333);
+        x = (x + (x >>> 4)) & 0x0f0f0f0f;
+        x = x + (x >>> 8);
+        x = x + (x >>> 16);
+        return (int) x & 0x0000003f;
+    }
+
+    public int timeLongBitCount_2Int(int reps) {
+        int t = 0;
+        for (int i = 0; i < reps; ++i) {
+            t += pop2((long) i);
+        }
+        return t;
+    }
+
+    private static int pop2(long l) {
+        int count = Integer.bitCount((int) (l & 0xffffffffL));
+        count += Integer.bitCount((int) (l >>> 32));
+        return count;
+    }
+
+    public int timeLongBitCount_Long(int reps) {
+        int t = 0;
+        for (int i = 0; i < reps; ++i) {
+            t += Long.bitCount((long) i);
+        }
+        return t;
+    }
+
+    /**
+     * Table for Seal's algorithm for Number of Trailing Zeros. Hacker's Delight
+     * online, Figure 5-18 (http://www.hackersdelight.org/revisions.pdf)
+     * The entries whose value is -1 are never referenced.
+     */
+    private static final byte[] NTZ_TABLE = {
+        32,  0,  1, 12,  2,  6, -1, 13,   3, -1,  7, -1, -1, -1, -1, 14,
+        10,  4, -1, -1,  8, -1, -1, 25,  -1, -1, -1, -1, -1, 21, 27, 15,
+        31, 11,  5, -1, -1, -1, -1, -1,   9, -1, -1, 24, -1, -1, 20, 26,
+        30, -1, -1, -1, -1, 23, -1, 19,  29, -1, 22, 18, 28, 17, 16, -1
+    };
+
+    private static int numberOfTrailingZerosHD(int i) {
+        // Seal's algorithm - Hacker's Delight 5-18
+        i &= -i;
+        i = (i <<  4) + i;    // x *= 17
+        i = (i <<  6) + i;    // x *= 65
+        i = (i << 16) - i;    // x *= 65535
+        return NTZ_TABLE[i >>> 26];
+    }
+
+    private static int numberOfTrailingZerosOL(int i) {
+        return NTZ_TABLE[((i & -i) * 0x0450FBAF) >>> 26];
+    }
+
+    public int timeNumberOfTrailingZerosHD(int reps) {
+        int t = 0;
+        for (int i = 0; i < reps; ++i) {
+            t += numberOfTrailingZerosHD(i);
+        }
+        return t;
+    }
+
+    public int timeNumberOfTrailingZerosOL(int reps) {
+        int t = 0;
+        for (int i = 0; i < reps; ++i) {
+            t += numberOfTrailingZerosOL(i);
+        }
+        return t;
+    }
+}
diff --git a/benchmarks/regression/IntegralToStringBenchmark.java b/benchmarks/regression/IntegralToStringBenchmark.java
new file mode 100644
index 0000000..cab9e98
--- /dev/null
+++ b/benchmarks/regression/IntegralToStringBenchmark.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class IntegralToStringBenchmark extends SimpleBenchmark {
+
+    private static final int SMALL  = 12;
+    private static final int MEDIUM = 12345;
+    private static final int LARGE  = 12345678;
+
+    public void time_IntegerToString_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(SMALL);
+        }
+    }
+
+    public void time_IntegerToString_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(MEDIUM);
+        }
+    }
+
+    public void time_IntegerToString_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(LARGE);
+        }
+    }
+
+    public void time_IntegerToString2_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(SMALL, 2);
+        }
+    }
+
+    public void time_IntegerToString2_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(MEDIUM, 2);
+        }
+    }
+
+    public void time_IntegerToString2_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(LARGE, 2);
+        }
+    }
+
+    public void time_IntegerToString10_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(SMALL, 10);
+        }
+    }
+
+    public void time_IntegerToString10_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(MEDIUM, 10);
+        }
+    }
+
+    public void time_IntegerToString10_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(LARGE, 10);
+        }
+    }
+
+    public void time_IntegerToString16_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(SMALL, 16);
+        }
+    }
+
+    public void time_IntegerToString16_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(MEDIUM, 16);
+        }
+    }
+
+    public void time_IntegerToString16_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toString(LARGE, 16);
+        }
+    }
+
+    public void time_IntegerToBinaryString_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toBinaryString(SMALL);
+        }
+    }
+
+    public void time_IntegerToBinaryString_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toBinaryString(MEDIUM);
+        }
+    }
+
+    public void time_IntegerToBinaryString_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toBinaryString(LARGE);
+        }
+    }
+
+    public void time_IntegerToHexString_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toHexString(SMALL);
+        }
+    }
+
+    public void time_IntegerToHexString_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toHexString(MEDIUM);
+        }
+    }
+
+    public void time_IntegerToHexString_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Integer.toHexString(LARGE);
+        }
+    }
+
+    public void time_StringBuilder_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            new StringBuilder().append(SMALL);
+        }
+    }
+
+    public void time_StringBuilder_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            new StringBuilder().append(MEDIUM);
+        }
+    }
+
+    public void time_StringBuilder_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            new StringBuilder().append(LARGE);
+        }
+    }
+
+    public void time_Formatter_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            String.format("%d", SMALL);
+        }
+    }
+
+    public void time_Formatter_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            String.format("%d", MEDIUM);
+        }
+    }
+
+    public void time_Formatter_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            String.format("%d", LARGE);
+        }
+    }
+}
diff --git a/benchmarks/regression/JarFileBenchmark.java b/benchmarks/regression/JarFileBenchmark.java
new file mode 100644
index 0000000..626ca21
--- /dev/null
+++ b/benchmarks/regression/JarFileBenchmark.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.io.File;
+import java.util.jar.*;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class JarFileBenchmark extends SimpleBenchmark {
+    @Param({
+        "/system/framework/bouncycastle.jar",
+        "/system/framework/core.jar",
+        "/system/framework/framework.jar"
+    })
+    private String filename;
+
+    public void time(int reps) throws Exception {
+        File f = new File(filename);
+        for (int i = 0; i < reps; ++i) {
+            JarFile jf = new JarFile(f);
+            Manifest m = jf.getManifest();
+            jf.close();
+        }
+    }
+}
diff --git a/benchmarks/regression/KeyPairGeneratorBenchmark.java b/benchmarks/regression/KeyPairGeneratorBenchmark.java
new file mode 100644
index 0000000..762c935
--- /dev/null
+++ b/benchmarks/regression/KeyPairGeneratorBenchmark.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.Map;
+
+public class KeyPairGeneratorBenchmark extends SimpleBenchmark {
+    @Param private Algorithm algorithm;
+
+    public enum Algorithm {
+        RSA,
+        DSA,
+    };
+
+    @Param private Implementation implementation;
+
+    public enum Implementation { OpenSSL, BouncyCastle };
+
+    private String generatorAlgorithm;
+    private KeyPairGenerator generator;
+    private SecureRandom random;
+
+    @Override protected void setUp() throws Exception {
+        this.generatorAlgorithm = algorithm.toString();
+        
+        final String provider;
+        if (implementation == Implementation.BouncyCastle) {
+            provider = "BC";
+        } else {
+            provider = "AndroidOpenSSL";
+        }
+
+        this.generator = KeyPairGenerator.getInstance(generatorAlgorithm, provider);
+        this.random = SecureRandom.getInstance("SHA1PRNG");
+        this.generator.initialize(1024);
+    }
+
+    public void time(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            KeyPair keyPair = generator.generateKeyPair();
+        }
+    }
+}
diff --git a/benchmarks/regression/LoopingBackwardsBenchmark.java b/benchmarks/regression/LoopingBackwardsBenchmark.java
new file mode 100644
index 0000000..054eff9
--- /dev/null
+++ b/benchmarks/regression/LoopingBackwardsBenchmark.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Testing the old canard that looping backwards is faster.
+ *
+ * @author Kevin Bourrillion
+ */
+public class LoopingBackwardsBenchmark extends SimpleBenchmark {
+  @Param({"2", "20", "2000", "20000000"}) int max;
+
+  public int timeForwards(int reps) {
+    int dummy = 0;
+    for (int i = 0; i < reps; i++) {
+      for (int j = 0; j < max; j++) {
+        dummy += j;
+      }
+    }
+    return dummy;
+  }
+
+  public int timeBackwards(int reps) {
+    int dummy = 0;
+    for (int i = 0; i < reps; i++) {
+      for (int j = max - 1; j >= 0; j--) {
+        dummy += j;
+      }
+    }
+    return dummy;
+  }
+}
diff --git a/benchmarks/regression/MathBenchmark.java b/benchmarks/regression/MathBenchmark.java
new file mode 100644
index 0000000..25a871d
--- /dev/null
+++ b/benchmarks/regression/MathBenchmark.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Many of these tests are bogus in that the cost will vary wildly depending on inputs.
+ * For _my_ current purposes, that's okay. But beware!
+ */
+public class MathBenchmark extends SimpleBenchmark {
+    private final double d = 1.2;
+    private final float f = 1.2f;
+    private final int i = 1;
+    private final long l = 1L;
+
+    public void timeAbsD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.abs(d);
+        }
+    }
+
+    public void timeAbsF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.abs(f);
+        }
+    }
+
+    public void timeAbsI(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.abs(i);
+        }
+    }
+
+    public void timeAbsL(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.abs(l);
+        }
+    }
+
+    public void timeAcos(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.acos(d);
+        }
+    }
+
+    public void timeAsin(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.asin(d);
+        }
+    }
+
+    public void timeAtan(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.atan(d);
+        }
+    }
+
+    public void timeAtan2(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.atan2(3, 4);
+        }
+    }
+
+    public void timeCbrt(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.cbrt(d);
+        }
+    }
+
+    public void timeCeil(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.ceil(d);
+        }
+    }
+
+    public void timeCopySignD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.copySign(d, d);
+        }
+    }
+
+    public void timeCopySignF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.copySign(f, f);
+        }
+    }
+
+    public void timeCopySignD_strict(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.copySign(d, d);
+        }
+    }
+
+    public void timeCopySignF_strict(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.copySign(f, f);
+        }
+    }
+
+    public void timeCos(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.cos(d);
+        }
+    }
+
+    public void timeCosh(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.cosh(d);
+        }
+    }
+
+    public void timeExp(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.exp(d);
+        }
+    }
+
+    public void timeExpm1(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.expm1(d);
+        }
+    }
+
+    public void timeFloor(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.floor(d);
+        }
+    }
+
+    public void timeGetExponentD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.getExponent(d);
+        }
+    }
+
+    public void timeGetExponentF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.getExponent(f);
+        }
+    }
+
+    public void timeHypot(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.hypot(d, d);
+        }
+    }
+
+    public void timeIEEEremainder(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.IEEEremainder(d, d);
+        }
+    }
+
+    public void timeLog(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.log(d);
+        }
+    }
+
+    public void timeLog10(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.log10(d);
+        }
+    }
+
+    public void timeLog1p(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.log1p(d);
+        }
+    }
+
+    public void timeMaxD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.max(d, d);
+        }
+    }
+
+    public void timeMaxF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.max(f, f);
+        }
+    }
+
+    public void timeMaxI(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.max(i, i);
+        }
+    }
+
+    public void timeMaxL(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.max(l, l);
+        }
+    }
+
+    public void timeMinD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.min(d, d);
+        }
+    }
+
+    public void timeMinF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.min(f, f);
+        }
+    }
+
+    public void timeMinI(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.min(i, i);
+        }
+    }
+
+    public void timeMinL(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.min(l, l);
+        }
+    }
+
+    public void timeNextAfterD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.nextAfter(d, d);
+        }
+    }
+
+    public void timeNextAfterF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.nextAfter(f, f);
+        }
+    }
+
+    public void timeNextUpD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.nextUp(d);
+        }
+    }
+
+    public void timeNextUpF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.nextUp(f);
+        }
+    }
+
+    public void timePow(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.pow(d, d);
+        }
+    }
+
+    public void timeRandom(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.random();
+        }
+    }
+
+    public void timeRint(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.rint(d);
+        }
+    }
+
+    public void timeRoundD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.round(d);
+        }
+    }
+
+    public void timeRoundF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.round(f);
+        }
+    }
+
+    public void timeScalbD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.scalb(d, 5);
+        }
+    }
+
+    public void timeScalbF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.scalb(f, 5);
+        }
+    }
+
+    public void timeSignumD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.signum(d);
+        }
+    }
+
+    public void timeSignumF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.signum(f);
+        }
+    }
+
+    public void timeSin(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.sin(d);
+        }
+    }
+
+    public void timeSinh(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.sinh(d);
+        }
+    }
+
+    public void timeSqrt(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.sqrt(d);
+        }
+    }
+
+    public void timeTan(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.tan(d);
+        }
+    }
+
+    public void timeTanh(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.tanh(d);
+        }
+    }
+
+    public void timeToDegrees(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.toDegrees(d);
+        }
+    }
+
+    public void timeToRadians(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.toRadians(d);
+        }
+    }
+
+    public void timeUlpD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.ulp(d);
+        }
+    }
+
+    public void timeUlpF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Math.ulp(f);
+        }
+    }
+}
diff --git a/benchmarks/regression/MessageDigestBenchmark.java b/benchmarks/regression/MessageDigestBenchmark.java
new file mode 100644
index 0000000..c9d8074
--- /dev/null
+++ b/benchmarks/regression/MessageDigestBenchmark.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.security.MessageDigest;
+
+public class MessageDigestBenchmark extends SimpleBenchmark {
+
+    private static final int DATA_SIZE = 8192;
+    private static final byte[] DATA = new byte[DATA_SIZE];
+    static {
+        for (int i = 0; i < DATA_SIZE; i++) {
+            DATA[i] = (byte)i;
+        }
+    }
+
+    @Param private Algorithm algorithm;
+
+    public enum Algorithm { MD5, SHA1, SHA256,  SHA384, SHA512 };
+
+    @Param private Provider provider;
+
+    public enum Provider { AndroidOpenSSL, BC };
+
+    public void time(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            MessageDigest digest = MessageDigest.getInstance(algorithm.toString(),
+                                                             provider.toString());
+            digest.update(DATA, 0, DATA_SIZE);
+            digest.digest();
+        }
+    }
+}
diff --git a/benchmarks/regression/MutableIntBenchmark.java b/benchmarks/regression/MutableIntBenchmark.java
new file mode 100644
index 0000000..ee4f2d9
--- /dev/null
+++ b/benchmarks/regression/MutableIntBenchmark.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public final class MutableIntBenchmark extends SimpleBenchmark {
+
+    enum Kind {
+        ARRAY() {
+            int[] value = new int[1];
+
+            @Override void timeCreate(int reps) {
+                for (int i = 0; i < reps; i++) {
+                    value = new int[] { 5 };
+                }
+            }
+            @Override void timeIncrement(int reps) {
+                for (int i = 0; i < reps; i++) {
+                    value[0]++;
+                }
+            }
+            @Override int timeGet(int reps) {
+                int sum = 0;
+                for (int i = 0; i < reps; i++) {
+                    sum += value[0];
+                }
+                return sum;
+            }
+        },
+        ATOMIC() {
+            AtomicInteger value = new AtomicInteger();
+
+            @Override void timeCreate(int reps) {
+                for (int i = 0; i < reps; i++) {
+                    value = new AtomicInteger(5);
+                }
+            }
+            @Override void timeIncrement(int reps) {
+                for (int i = 0; i < reps; i++) {
+                    value.incrementAndGet();
+                }
+            }
+            @Override int timeGet(int reps) {
+                int sum = 0;
+                for (int i = 0; i < reps; i++) {
+                    sum += value.intValue();
+                }
+                return sum;
+            }
+        };
+
+        abstract void timeCreate(int reps);
+        abstract void timeIncrement(int reps);
+        abstract int timeGet(int reps);
+    }
+
+    @Param Kind kind;
+
+    public void timeCreate(int reps) {
+        kind.timeCreate(reps);
+    }
+
+    public void timeIncrement(int reps) {
+        kind.timeIncrement(reps);
+    }
+
+    public void timeGet(int reps) {
+        kind.timeGet(reps);
+    }
+
+    public static void main(String[] args) {
+        Runner.main(MutableIntBenchmark.class, args);
+    }
+}
diff --git a/benchmarks/regression/NativeMethodBenchmark.java b/benchmarks/regression/NativeMethodBenchmark.java
new file mode 100644
index 0000000..2e482ef
--- /dev/null
+++ b/benchmarks/regression/NativeMethodBenchmark.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+import org.apache.harmony.dalvik.NativeTestTarget;
+
+public class NativeMethodBenchmark extends SimpleBenchmark {
+    public void time_emptyJniStaticSynchronizedMethod0(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            NativeTestTarget.emptyJniStaticSynchronizedMethod0();
+        }
+    }
+
+    public void time_emptyJniSynchronizedMethod0(int reps) throws Exception {
+        NativeTestTarget n = new NativeTestTarget();
+        for (int i = 0; i < reps; ++i) {
+            n.emptyJniSynchronizedMethod0();
+        }
+    }
+
+    public void time_emptyJniStaticMethod0(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            NativeTestTarget.emptyJniStaticMethod0();
+        }
+    }
+
+    public void time_emptyJniMethod0(int reps) throws Exception {
+        NativeTestTarget n = new NativeTestTarget();
+        for (int i = 0; i < reps; ++i) {
+            n.emptyJniMethod0();
+        }
+    }
+
+    public void time_emptyJniStaticMethod6(int reps) throws Exception {
+        int a = -1;
+        int b = 0;
+        for (int i = 0; i < reps; ++i) {
+            NativeTestTarget.emptyJniStaticMethod6(a, b, 1, 2, 3, i);
+        }
+    }
+
+    public void time_emptyJniMethod6(int reps) throws Exception {
+        int a = -1;
+        int b = 0;
+        NativeTestTarget n = new NativeTestTarget();
+        for (int i = 0; i < reps; ++i) {
+            n.emptyJniMethod6(a, b, 1, 2, 3, i);
+        }
+    }
+
+    public void time_emptyJniStaticMethod6L(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            NativeTestTarget.emptyJniStaticMethod6L(null, null, null, null, null, null);
+        }
+    }
+
+    public void time_emptyJniMethod6L(int reps) throws Exception {
+        NativeTestTarget n = new NativeTestTarget();
+        for (int i = 0; i < reps; ++i) {
+            n.emptyJniMethod6L(null, null, null, null, null, null);
+        }
+    }
+
+}
diff --git a/benchmarks/regression/ParseBenchmark.java b/benchmarks/regression/ParseBenchmark.java
new file mode 100644
index 0000000..b44b429
--- /dev/null
+++ b/benchmarks/regression/ParseBenchmark.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.SAXParserFactory;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.xml.sax.InputSource;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Measure throughput of various parsers.
+ *
+ * <p>This benchmark requires that ParseBenchmarkData.zip is on the classpath.
+ * That file contains Twitter feed data, which is representative of what
+ * applications will be parsing.
+ */
+public final class ParseBenchmark extends SimpleBenchmark {
+
+    @Param Document document;
+    @Param Api api;
+
+    private enum Document {
+        TWEETS,
+        READER_SHORT,
+        READER_LONG
+    }
+
+    private enum Api {
+        ANDROID_STREAM("json") {
+            @Override Parser newParser() {
+                return new AndroidStreamParser();
+            }
+        },
+        ORG_JSON("json") {
+            @Override Parser newParser() {
+                return new OrgJsonParser();
+            }
+        },
+        XML_PULL("xml") {
+            @Override Parser newParser() {
+                return new GeneralXmlPullParser();
+            }
+        },
+        XML_DOM("xml") {
+            @Override Parser newParser() {
+                return new XmlDomParser();
+            }
+        },
+        XML_SAX("xml") {
+            @Override Parser newParser() {
+                return new XmlSaxParser();
+            }
+        };
+
+        final String extension;
+
+        private Api(String extension) {
+            this.extension = extension;
+        }
+
+        abstract Parser newParser();
+    }
+
+    private String text;
+    private Parser parser;
+
+    @Override protected void setUp() throws Exception {
+        text = resourceToString("/" + document.name() + "." + api.extension);
+        parser = api.newParser();
+    }
+
+    public void timeParse(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            parser.parse(text);
+        }
+    }
+
+    public static void main(String... args) throws Exception {
+        Runner.main(ParseBenchmark.class, args);
+    }
+
+    private static String resourceToString(String path) throws Exception {
+        InputStream in = ParseBenchmark.class.getResourceAsStream(path);
+        if (in == null) {
+            throw new IllegalArgumentException("No such file: " + path);
+        }
+
+        Reader reader = new InputStreamReader(in, "UTF-8");
+        char[] buffer = new char[8192];
+        StringWriter writer = new StringWriter();
+        int count;
+        while ((count = reader.read(buffer)) != -1) {
+            writer.write(buffer, 0, count);
+        }
+        reader.close();
+        return writer.toString();
+    }
+
+    interface Parser {
+        void parse(String data) throws Exception;
+    }
+
+    private static class AndroidStreamParser implements Parser {
+        @Override public void parse(String data) throws Exception {
+            android.util.JsonReader jsonReader
+                    = new android.util.JsonReader(new StringReader(data));
+            readToken(jsonReader);
+            jsonReader.close();
+        }
+
+        public void readObject(android.util.JsonReader reader) throws IOException {
+            reader.beginObject();
+            while (reader.hasNext()) {
+                reader.nextName();
+                readToken(reader);
+            }
+            reader.endObject();
+        }
+
+        public void readArray(android.util.JsonReader reader) throws IOException {
+            reader.beginArray();
+            while (reader.hasNext()) {
+                readToken(reader);
+            }
+            reader.endArray();
+        }
+
+        private void readToken(android.util.JsonReader reader) throws IOException {
+            switch (reader.peek()) {
+            case BEGIN_ARRAY:
+                readArray(reader);
+                break;
+            case BEGIN_OBJECT:
+                readObject(reader);
+                break;
+            case BOOLEAN:
+                reader.nextBoolean();
+                break;
+            case NULL:
+                reader.nextNull();
+                break;
+            case NUMBER:
+                reader.nextLong();
+                break;
+            case STRING:
+                reader.nextString();
+                break;
+            default:
+                throw new IllegalArgumentException("Unexpected token" + reader.peek());
+            }
+        }
+    }
+
+    private static class OrgJsonParser implements Parser {
+        @Override public void parse(String data) throws Exception {
+            if (data.startsWith("[")) {
+                new JSONArray(data);
+            } else if (data.startsWith("{")) {
+                new JSONObject(data);
+            } else {
+                throw new IllegalArgumentException();
+            }
+        }
+    }
+
+    private static class GeneralXmlPullParser implements Parser {
+        @Override public void parse(String data) throws Exception {
+            XmlPullParser xmlParser = android.util.Xml.newPullParser();
+            xmlParser.setInput(new StringReader(data));
+            xmlParser.nextTag();
+            while (xmlParser.next() != XmlPullParser.END_DOCUMENT) {
+                xmlParser.getName();
+                xmlParser.getText();
+            }
+        }
+    }
+
+    private static class XmlDomParser implements Parser {
+        @Override public void parse(String data) throws Exception {
+            DocumentBuilderFactory.newInstance().newDocumentBuilder()
+                    .parse(new InputSource(new StringReader(data)));
+        }
+    }
+
+    private static class XmlSaxParser implements Parser {
+        @Override public void parse(String data) throws Exception {
+            SAXParserFactory.newInstance().newSAXParser().parse(
+                    new InputSource(new StringReader(data)), new DefaultHandler());
+        }
+    }
+}
diff --git a/benchmarks/regression/ParseBenchmarkData.zip b/benchmarks/regression/ParseBenchmarkData.zip
new file mode 100644
index 0000000..7838e8a
--- /dev/null
+++ b/benchmarks/regression/ParseBenchmarkData.zip
Binary files differ
diff --git a/benchmarks/regression/PriorityQueueBenchmark.java b/benchmarks/regression/PriorityQueueBenchmark.java
new file mode 100644
index 0000000..2fe661b
--- /dev/null
+++ b/benchmarks/regression/PriorityQueueBenchmark.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.PriorityQueue;
+import java.util.Random;
+
+public class PriorityQueueBenchmark extends SimpleBenchmark {
+    @Param({"100", "1000", "10000"}) private int queueSize;
+    @Param({"0", "25", "50", "75", "100"}) private int hitRate;
+
+    private PriorityQueue<Integer> pq;
+    private PriorityQueue<Integer> usepq;
+    private List<Integer> seekElements;
+    private Random random = new Random(189279387L);
+
+    @Override protected void setUp() throws Exception {
+        pq = new PriorityQueue<Integer>();
+        usepq = new PriorityQueue<Integer>();
+        seekElements = new ArrayList<Integer>();
+        List<Integer> allElements = new ArrayList<Integer>();
+        int numShared = (int)(queueSize * ((double)hitRate / 100));
+        // the total number of elements we require to engineer a hit rate of hitRate%
+        int totalElements = 2 * queueSize - numShared;
+        for (int i = 0; i < totalElements; i++) {
+            allElements.add(i);
+        }
+        // shuffle these elements so that we get a reasonable distribution of missed elements
+        Collections.shuffle(allElements, random);
+        // add shared elements
+        for (int i = 0; i < numShared; i++) {
+            pq.add(allElements.get(i));
+            seekElements.add(allElements.get(i));
+        }
+        // add priority queue only elements (these won't be touched)
+        for (int i = numShared; i < queueSize; i++) {
+            pq.add(allElements.get(i));
+        }
+        // add non-priority queue elements (these will be misses)
+        for (int i = queueSize; i < totalElements; i++) {
+            seekElements.add(allElements.get(i));
+        }
+        usepq = new PriorityQueue<Integer>(pq);
+        // shuffle again so that elements are accessed in a different pattern than they were
+        // inserted
+        Collections.shuffle(seekElements, random);
+    }
+
+    public boolean timeRemove(int reps) {
+        boolean dummy = false;
+        int elementsSize = seekElements.size();
+        // At most allow the queue to empty 10%.
+        int resizingThreshold = queueSize / 10;
+        for (int i = 0; i < reps; i++) {
+            // Reset queue every so often. This will be called more often for smaller
+            // queueSizes, but since a copy is linear, it will also cost proportionally
+            // less, and hopefully it will approximately balance out.
+            if (i % resizingThreshold == 0) {
+                usepq = new PriorityQueue<Integer>(pq);
+            }
+            dummy = usepq.remove(seekElements.get(i % elementsSize));
+        }
+        return dummy;
+    }
+}
diff --git a/benchmarks/regression/PropertyAccessBenchmark.java b/benchmarks/regression/PropertyAccessBenchmark.java
new file mode 100644
index 0000000..cabd6ed
--- /dev/null
+++ b/benchmarks/regression/PropertyAccessBenchmark.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public final class PropertyAccessBenchmark extends SimpleBenchmark {
+    private View view = new View();
+    private Method setX;
+    private GeneratedProperty generatedSetter = new GeneratedSetter();
+    private GeneratedProperty generatedField = new GeneratedField();
+    private Field x;
+    private Object[] argsBox = new Object[1];
+
+    @Override protected void setUp() throws Exception {
+        setX = View.class.getDeclaredMethod("setX", float.class);
+        x = View.class.getDeclaredField("x");
+    }
+
+    public void timeDirectSetter(int reps) {
+        for (int i = 0; i < reps; i++) {
+            view.setX(0.1f);
+        }
+    }
+
+    public void timeDirectFieldSet(int reps) {
+        for (int i = 0; i < reps; i++) {
+            view.x = 0.1f;
+        }
+    }
+
+    public void timeDirectSetterAndBoxing(int reps) {
+        for (int i = 0; i < reps; i++) {
+            Float value = 0.1f;
+            view.setX(value);
+        }
+    }
+
+    public void timeDirectFieldSetAndBoxing(int reps) {
+        for (int i = 0; i < reps; i++) {
+            Float value = 0.1f;
+            view.x = value;
+        }
+    }
+
+    public void timeReflectionSetterAndTwoBoxes(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            setX.invoke(view, 0.1f);
+        }
+    }
+
+    public void timeReflectionSetterAndOneBox(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            argsBox[0] = 0.1f;
+            setX.invoke(view, argsBox);
+        }
+    }
+
+    public void timeReflectionFieldSet(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            x.setFloat(view, 0.1f);
+        }
+    }
+
+    public void timeGeneratedSetter(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            generatedSetter.setFloat(view, 0.1f);
+        }
+    }
+
+    public void timeGeneratedFieldSet(int reps) throws Exception {
+        for (int i = 0; i < reps; i++) {
+            generatedField.setFloat(view, 0.1f);
+        }
+    }
+
+    static class View {
+        float x;
+
+        public void setX(float x) {
+            this.x = x;
+        }
+    }
+
+    static interface GeneratedProperty {
+        void setFloat(View v, float f);
+    }
+
+    static class GeneratedSetter implements GeneratedProperty {
+        public void setFloat(View v, float f) {
+            v.setX(f);
+        }
+    }
+
+    static class GeneratedField implements GeneratedProperty {
+        public void setFloat(View v, float f) {
+            v.x = f;
+        }
+    }
+}
diff --git a/benchmarks/regression/RandomBenchmark.java b/benchmarks/regression/RandomBenchmark.java
new file mode 100644
index 0000000..0792805
--- /dev/null
+++ b/benchmarks/regression/RandomBenchmark.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.security.SecureRandom;
+import java.util.Random;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class RandomBenchmark extends SimpleBenchmark {
+    public void timeNewRandom(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            Random rng = new Random();
+            rng.nextInt();
+        }
+    }
+
+    public void timeReusedRandom(int reps) throws Exception {
+        Random rng = new Random();
+        for (int i = 0; i < reps; ++i) {
+            rng.nextInt();
+        }
+    }
+
+    public void timeReusedSecureRandom(int reps) throws Exception {
+        SecureRandom rng = new SecureRandom();
+        for (int i = 0; i < reps; ++i) {
+            rng.nextInt();
+        }
+    }
+
+    public void timeNewSecureRandom(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            SecureRandom rng = new SecureRandom();
+            rng.nextInt();
+        }
+    }
+}
diff --git a/benchmarks/regression/RealToStringBenchmark.java b/benchmarks/regression/RealToStringBenchmark.java
new file mode 100644
index 0000000..8914cf6
--- /dev/null
+++ b/benchmarks/regression/RealToStringBenchmark.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class RealToStringBenchmark extends SimpleBenchmark {
+
+    private static final float SMALL  = -123.45f;
+    private static final float MEDIUM = -123.45e8f;
+    private static final float LARGE  = -123.45e36f;
+
+    public void timeFloat_toString_NaN(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Float.toString(Float.NaN);
+        }
+    }
+
+    public void timeFloat_toString_NEGATIVE_INFINITY(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Float.toString(Float.NEGATIVE_INFINITY);
+        }
+    }
+
+    public void timeFloat_toString_POSITIVE_INFINITY(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Float.toString(Float.POSITIVE_INFINITY);
+        }
+    }
+
+    public void timeFloat_toString_zero(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Float.toString(0.0f);
+        }
+    }
+
+    public void timeFloat_toString_minusZero(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Float.toString(-0.0f);
+        }
+    }
+
+    public void timeFloat_toString_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Float.toString(SMALL);
+        }
+    }
+
+    public void timeFloat_toString_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Float.toString(MEDIUM);
+        }
+    }
+
+    public void timeFloat_toString_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Float.toString(LARGE);
+        }
+    }
+
+    public void timeStringBuilder_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            new StringBuilder().append(SMALL);
+        }
+    }
+
+    public void timeStringBuilder_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            new StringBuilder().append(MEDIUM);
+        }
+    }
+
+    public void timeStringBuilder_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            new StringBuilder().append(LARGE);
+        }
+    }
+
+    public void timeFormatter_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            String.format("%f", SMALL);
+        }
+    }
+
+    public void timeFormatter_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            String.format("%f", MEDIUM);
+        }
+    }
+
+    public void timeFormatter_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            String.format("%f", LARGE);
+        }
+    }
+
+    public void timeFormatter_dot2f_small(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            String.format("%.2f", SMALL);
+        }
+    }
+
+    public void timeFormatter_dot2f_medium(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            String.format("%.2f", MEDIUM);
+        }
+    }
+
+    public void timeFormatter_dot2f_large(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            String.format("%.2f", LARGE);
+        }
+    }
+}
diff --git a/benchmarks/regression/ReflectionBenchmark.java b/benchmarks/regression/ReflectionBenchmark.java
new file mode 100644
index 0000000..fe00fa2
--- /dev/null
+++ b/benchmarks/regression/ReflectionBenchmark.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.lang.reflect.*;
+
+public class ReflectionBenchmark extends SimpleBenchmark {
+    public void timeObject_getClass(int reps) throws Exception {
+        C c = new C();
+        for (int rep = 0; rep < reps; ++rep) {
+            c.getClass();
+        }
+    }
+
+    public void timeClass_getField(int reps) throws Exception {
+        Class<?> klass = C.class;
+        for (int rep = 0; rep < reps; ++rep) {
+            klass.getField("f");
+        }
+    }
+
+    public void timeClass_getDeclaredField(int reps) throws Exception {
+        Class<?> klass = C.class;
+        for (int rep = 0; rep < reps; ++rep) {
+            klass.getDeclaredField("f");
+        }
+    }
+
+    public void timeClass_getConstructor(int reps) throws Exception {
+        Class<?> klass = C.class;
+        for (int rep = 0; rep < reps; ++rep) {
+            klass.getConstructor();
+        }
+    }
+
+    public void timeClass_newInstance(int reps) throws Exception {
+        Class<?> klass = C.class;
+        Constructor constructor = klass.getConstructor();
+        for (int rep = 0; rep < reps; ++rep) {
+            constructor.newInstance();
+        }
+    }
+
+    public void timeClass_getMethod(int reps) throws Exception {
+        Class<?> klass = C.class;
+        for (int rep = 0; rep < reps; ++rep) {
+            klass.getMethod("m");
+        }
+    }
+
+    public void timeClass_getDeclaredMethod(int reps) throws Exception {
+        Class<?> klass = C.class;
+        for (int rep = 0; rep < reps; ++rep) {
+            klass.getDeclaredMethod("m");
+        }
+    }
+
+    public void timeField_setInt(int reps) throws Exception {
+        Class<?> klass = C.class;
+        Field f = klass.getDeclaredField("f");
+        C instance = new C();
+        for (int rep = 0; rep < reps; ++rep) {
+            f.setInt(instance, 1);
+        }
+    }
+
+    public void timeField_getInt(int reps) throws Exception {
+        Class<?> klass = C.class;
+        Field f = klass.getDeclaredField("f");
+        C instance = new C();
+        for (int rep = 0; rep < reps; ++rep) {
+            f.getInt(instance);
+        }
+    }
+
+    public void timeMethod_invokeV(int reps) throws Exception {
+        Class<?> klass = C.class;
+        Method m = klass.getDeclaredMethod("m");
+        C instance = new C();
+        for (int rep = 0; rep < reps; ++rep) {
+            m.invoke(instance);
+        }
+    }
+
+    public void timeMethod_invokeStaticV(int reps) throws Exception {
+        Class<?> klass = C.class;
+        Method m = klass.getDeclaredMethod("sm");
+        for (int rep = 0; rep < reps; ++rep) {
+            m.invoke(null);
+        }
+    }
+
+    public void timeMethod_invokeI(int reps) throws Exception {
+        Class<?> klass = C.class;
+        Method m = klass.getDeclaredMethod("setField", int.class);
+        C instance = new C();
+        for (int rep = 0; rep < reps; ++rep) {
+            m.invoke(instance, 1);
+        }
+    }
+
+    public void timeMethod_invokePreBoxedI(int reps) throws Exception {
+        Class<?> klass = C.class;
+        Method m = klass.getDeclaredMethod("setField", int.class);
+        C instance = new C();
+        Integer one = Integer.valueOf(1);
+        for (int rep = 0; rep < reps; ++rep) {
+            m.invoke(instance, one);
+        }
+    }
+
+    public void timeMethod_invokeStaticI(int reps) throws Exception {
+        Class<?> klass = C.class;
+        Method m = klass.getDeclaredMethod("setStaticField", int.class);
+        for (int rep = 0; rep < reps; ++rep) {
+            m.invoke(null, 1);
+        }
+    }
+
+    public void timeMethod_invokeStaticPreBoxedI(int reps) throws Exception {
+        Class<?> klass = C.class;
+        Method m = klass.getDeclaredMethod("setStaticField", int.class);
+        Integer one = Integer.valueOf(1);
+        for (int rep = 0; rep < reps; ++rep) {
+            m.invoke(null, one);
+        }
+    }
+
+    public void timeRegularMethodInvocation(int reps) throws Exception {
+        C instance = new C();
+        for (int rep = 0; rep < reps; ++rep) {
+            instance.setField(1);
+        }
+    }
+
+    public void timeRegularConstructor(int reps) throws Exception {
+        for (int rep = 0; rep < reps; ++rep) {
+            new C();
+        }
+    }
+
+    public static class C {
+        public static int sf = 0;
+        public int f = 0;
+
+        public C() {
+            // A non-empty constructor so we don't get optimized away.
+            f = 1;
+        }
+
+        public void m() {
+        }
+
+        public static void sm() {
+        }
+
+        public void setField(int value) {
+            f = value;
+        }
+
+        public static void setStaticField(int value) {
+            sf = value;
+        }
+    }
+}
diff --git a/benchmarks/regression/SSLSocketBenchmark.java b/benchmarks/regression/SSLSocketBenchmark.java
new file mode 100644
index 0000000..fd72a79
--- /dev/null
+++ b/benchmarks/regression/SSLSocketBenchmark.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.URL;
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+
+public class SSLSocketBenchmark extends SimpleBenchmark {
+
+    private static final int BUFFER_SIZE = 8192;
+
+    final byte[] buffer = new byte[BUFFER_SIZE];
+
+    @Param private WebSite webSite;
+
+    public enum WebSite {
+        DOCS("https://docs.google.com"),
+        MAIL("https://mail.google.com"),
+        SITES("https://sites.google.com"),
+        WWW("https://www.google.com");
+        final InetAddress host;
+        final int port;
+        final byte[] request;
+        WebSite(String uri) {
+            try {
+                URL url = new URL(uri);
+
+                this.host = InetAddress.getByName(url.getHost());
+
+                int p = url.getPort();
+                String portString;
+                if (p == -1) {
+                    this.port = 443;
+                    portString = "";
+                } else {
+                    this.port = p;
+                    portString = ":" + port;
+                }
+
+                this.request = ("GET " + uri + " HTTP/1.0\r\n"
+                                + "Host: " + host + portString + "\r\n"
+                                +"\r\n").getBytes();
+
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @Param private Implementation implementation;
+
+    public enum Implementation { OPENSSL, HARMONY };
+
+    private SocketFactory sf;
+
+    @Override protected void setUp() throws Exception {
+        SSLContext sslContext;
+        switch (implementation) {
+            case OPENSSL:
+                sslContext = SSLContext.getInstance("SSL", "AndroidOpenSSL");
+                break;
+            case HARMONY:
+                sslContext = SSLContext.getInstance("SSL", "HarmonyJSSE");
+                break;
+            default:
+                throw new RuntimeException(implementation.toString());
+        }
+        sslContext.init(null, null, null);
+        this.sf = sslContext.getSocketFactory();
+    }
+
+    public void time(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            Socket s = sf.createSocket(webSite.host, webSite.port);
+            OutputStream out = s.getOutputStream();
+            out.write(webSite.request);
+            InputStream in = s.getInputStream();
+            while (true) {
+                int n = in.read(buffer);
+                if (n == -1) {
+                    break;
+                }
+            }
+            in.close();
+        }
+    }
+}
diff --git a/benchmarks/regression/SchemePrefixBenchmark.java b/benchmarks/regression/SchemePrefixBenchmark.java
new file mode 100644
index 0000000..d90fe29
--- /dev/null
+++ b/benchmarks/regression/SchemePrefixBenchmark.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class SchemePrefixBenchmark extends SimpleBenchmark {
+
+    enum Strategy {
+        JAVA() {
+            @Override String execute(String spec) {
+                int colon = spec.indexOf(':');
+
+                if (colon < 1) {
+                    return null;
+                }
+
+                for (int i = 0; i < colon; i++) {
+                    char c = spec.charAt(i);
+                    if (!isValidSchemeChar(i, c)) {
+                        return null;
+                    }
+                }
+
+                return spec.substring(0, colon).toLowerCase(Locale.US);
+            }
+
+            private boolean isValidSchemeChar(int index, char c) {
+                if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+                    return true;
+                }
+                if (index > 0 && ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')) {
+                    return true;
+                }
+                return false;
+            }
+        },
+
+        REGEX() {
+            private final Pattern pattern = Pattern.compile("^([a-zA-Z][a-zA-Z0-9+\\-.]*):");
+
+            @Override String execute(String spec) {
+                Matcher matcher = pattern.matcher(spec);
+                if (matcher.find()) {
+                    return matcher.group(1).toLowerCase(Locale.US);
+                } else {
+                    return null;
+                }
+            }
+        };
+
+
+        abstract String execute(String spec);
+    }
+
+    @Param Strategy strategy;
+
+    public void timeSchemePrefix(int reps) {
+        for (int i = 0; i < reps; i++) {
+            strategy.execute("http://android.com");
+        }
+    }
+}
diff --git a/benchmarks/regression/SerializationBenchmark.java b/benchmarks/regression/SerializationBenchmark.java
new file mode 100644
index 0000000..bc958eb
--- /dev/null
+++ b/benchmarks/regression/SerializationBenchmark.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+public class SerializationBenchmark extends SimpleBenchmark {
+    private static byte[] bytes(Object o) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+        ObjectOutputStream out = new ObjectOutputStream(baos);
+        out.writeObject(o);
+        out.close();
+        return baos.toByteArray();
+    }
+
+    public void timeReadIntArray(int reps) throws Exception {
+        int[] intArray = new int[256];
+        readSingleObject(reps, intArray);
+    }
+
+    public void timeWriteIntArray(int reps) throws Exception {
+        int[] intArray = new int[256];
+        writeSingleObject(reps, intArray);
+    }
+    public void timeReadArrayListInteger(int reps) throws Exception {
+        ArrayList<Integer> object = new ArrayList<Integer>();
+        for (int i = 0; i < 256; ++i) {
+            object.add(i);
+        }
+        readSingleObject(reps, object);
+    }
+
+    public void timeWriteArrayListInteger(int reps) throws Exception {
+        ArrayList<Integer> object = new ArrayList<Integer>();
+        for (int i = 0; i < 256; ++i) {
+            object.add(i);
+        }
+        writeSingleObject(reps, object);
+    }
+
+    public void timeReadString(int reps) throws Exception {
+        readSingleObject(reps, "hello");
+    }
+
+    public void timeReadObjectStreamClass(int reps) throws Exception {
+        // A special case because serialization itself requires this class.
+        // (This should really be a unit test.)
+        ObjectStreamClass osc = ObjectStreamClass.lookup(String.class);
+        readSingleObject(reps, osc);
+    }
+
+    public void timeWriteString(int reps) throws Exception {
+        // String is a special case that avoids JNI.
+        writeSingleObject(reps, "hello");
+    }
+
+    public void timeWriteObjectStreamClass(int reps) throws Exception {
+        // A special case because serialization itself requires this class.
+        // (This should really be a unit test.)
+        ObjectStreamClass osc = ObjectStreamClass.lookup(String.class);
+        writeSingleObject(reps, osc);
+    }
+
+    // This is a baseline for the others.
+    public void timeWriteNoObjects(int reps) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+        ObjectOutputStream out = new ObjectOutputStream(baos);
+        for (int rep = 0; rep < reps; ++rep) {
+            out.reset();
+            baos.reset();
+        }
+        out.close();
+    }
+
+    private void readSingleObject(int reps, Object object) throws Exception {
+        byte[] bytes = bytes(object);
+        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+        for (int rep = 0; rep < reps; ++rep) {
+            ObjectInputStream in = new ObjectInputStream(bais);
+            in.readObject();
+            in.close();
+            bais.reset();
+        }
+    }
+
+    private void writeSingleObject(int reps, Object o) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+        ObjectOutputStream out = new ObjectOutputStream(baos);
+        for (int rep = 0; rep < reps; ++rep) {
+            out.writeObject(o);
+            out.reset();
+            baos.reset();
+        }
+        out.close();
+    }
+
+    public void timeWriteEveryKindOfField(int reps) throws Exception {
+        writeSingleObject(reps, new LittleBitOfEverything());
+    }
+    public void timeWriteSerializableBoolean(int reps) throws Exception {
+        writeSingleObject(reps, new SerializableBoolean());
+    }
+    public void timeWriteSerializableByte(int reps) throws Exception {
+        writeSingleObject(reps, new SerializableByte());
+    }
+    public void timeWriteSerializableChar(int reps) throws Exception {
+        writeSingleObject(reps, new SerializableChar());
+    }
+    public void timeWriteSerializableDouble(int reps) throws Exception {
+        writeSingleObject(reps, new SerializableDouble());
+    }
+    public void timeWriteSerializableFloat(int reps) throws Exception {
+        writeSingleObject(reps, new SerializableFloat());
+    }
+    public void timeWriteSerializableInt(int reps) throws Exception {
+        writeSingleObject(reps, new SerializableInt());
+    }
+    public void timeWriteSerializableLong(int reps) throws Exception {
+        writeSingleObject(reps, new SerializableLong());
+    }
+    public void timeWriteSerializableShort(int reps) throws Exception {
+        writeSingleObject(reps, new SerializableShort());
+    }
+    public void timeWriteSerializableReference(int reps) throws Exception {
+        writeSingleObject(reps, new SerializableReference());
+    }
+
+    public void timeReadEveryKindOfField(int reps) throws Exception {
+        readSingleObject(reps, new LittleBitOfEverything());
+    }
+    public void timeReadSerializableBoolean(int reps) throws Exception {
+        readSingleObject(reps, new SerializableBoolean());
+    }
+    public void timeReadSerializableByte(int reps) throws Exception {
+        readSingleObject(reps, new SerializableByte());
+    }
+    public void timeReadSerializableChar(int reps) throws Exception {
+        readSingleObject(reps, new SerializableChar());
+    }
+    public void timeReadSerializableDouble(int reps) throws Exception {
+        readSingleObject(reps, new SerializableDouble());
+    }
+    public void timeReadSerializableFloat(int reps) throws Exception {
+        readSingleObject(reps, new SerializableFloat());
+    }
+    public void timeReadSerializableInt(int reps) throws Exception {
+        readSingleObject(reps, new SerializableInt());
+    }
+    public void timeReadSerializableLong(int reps) throws Exception {
+        readSingleObject(reps, new SerializableLong());
+    }
+    public void timeReadSerializableShort(int reps) throws Exception {
+        readSingleObject(reps, new SerializableShort());
+    }
+    public void timeReadSerializableReference(int reps) throws Exception {
+        readSingleObject(reps, new SerializableReference());
+    }
+
+    public static class SerializableBoolean implements Serializable {
+        boolean z;
+    }
+    public static class SerializableByte implements Serializable {
+        byte b;
+    }
+    public static class SerializableChar implements Serializable {
+        char c;
+    }
+    public static class SerializableDouble implements Serializable {
+        double d;
+    }
+    public static class SerializableFloat implements Serializable {
+        float f;
+    }
+    public static class SerializableInt implements Serializable {
+        int i;
+    }
+    public static class SerializableLong implements Serializable {
+        long j;
+    }
+    public static class SerializableShort implements Serializable {
+        short s;
+    }
+    public static class SerializableReference implements Serializable {
+        Object l;
+    }
+
+    public static class LittleBitOfEverything implements Serializable {
+        boolean z;
+        byte b;
+        char c;
+        double d;
+        float f;
+        int i;
+        long j;
+        short s;
+        Object l;
+    }
+}
diff --git a/benchmarks/regression/SignatureBenchmark.java b/benchmarks/regression/SignatureBenchmark.java
new file mode 100644
index 0000000..383dcfd
--- /dev/null
+++ b/benchmarks/regression/SignatureBenchmark.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.harmony.xnet.provider.jsse.OpenSSLSignature;
+
+/**
+ * Tests RSA and DSA signature creation and verification.
+ */
+public class SignatureBenchmark extends SimpleBenchmark {
+
+    private static final int DATA_SIZE = 8192;
+    private static final byte[] DATA = new byte[DATA_SIZE];
+    static {
+        for (int i = 0; i < DATA_SIZE; i++) {
+            DATA[i] = (byte)i;
+        }
+    }
+    @Param private Algorithm algorithm;
+
+    public enum Algorithm {
+        MD5WithRSA,
+        SHA1WithRSA,
+        SHA256WithRSA,
+        SHA384WithRSA,
+        SHA512WithRSA,
+        SHA1withDSA
+    };
+
+    @Param private Implementation implementation;
+
+    public enum Implementation { OpenSSL, BouncyCastle };
+
+    // Key generation and signing aren't part of the benchmark for verification
+    // so cache the results
+    private static Map<String,KeyPair> KEY_PAIRS = new HashMap<String,KeyPair>();
+    private static Map<String,byte[]> SIGNATURES = new HashMap<String,byte[]>();
+
+    private String signatureAlgorithm;
+    private byte[] signature;
+    private PrivateKey privateKey;
+    private PublicKey publicKey;
+
+    @Override protected void setUp() throws Exception {
+        this.signatureAlgorithm = algorithm.toString();
+
+        String keyAlgorithm = signatureAlgorithm.substring(signatureAlgorithm.length() - 3 ,
+                                                           signatureAlgorithm.length());
+        KeyPair keyPair = KEY_PAIRS.get(keyAlgorithm);
+        if (keyPair == null) {
+            KeyPairGenerator generator = KeyPairGenerator.getInstance(keyAlgorithm);
+            keyPair = generator.generateKeyPair();
+            KEY_PAIRS.put(keyAlgorithm, keyPair);
+        }
+        this.privateKey = keyPair.getPrivate();
+        this.publicKey = keyPair.getPublic();
+
+        this.signature = SIGNATURES.get(signatureAlgorithm);
+        if (this.signature == null) {
+            Signature signer = Signature.getInstance(signatureAlgorithm);
+            signer.initSign(keyPair.getPrivate());
+            signer.update(DATA);
+            this.signature = signer.sign();
+            SIGNATURES.put(signatureAlgorithm, signature);
+        }
+    }
+
+    public void timeSign(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            Signature signer;
+            switch (implementation) {
+                case OpenSSL:
+                    signer = Signature.getInstance(signatureAlgorithm, "AndroidOpenSSL");
+                    break;
+                case BouncyCastle:
+                    signer = Signature.getInstance(signatureAlgorithm, "BC");
+                    break;
+                default:
+                    throw new RuntimeException(implementation.toString());
+            }
+            signer.initSign(privateKey);
+            signer.update(DATA);
+            signer.sign();
+        }
+    }
+
+    public void timeVerify(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            Signature verifier;
+            switch (implementation) {
+                case OpenSSL:
+                    verifier = Signature.getInstance(signatureAlgorithm, "AndroidOpenSSL");
+                    break;
+                case BouncyCastle:
+                    verifier = Signature.getInstance(signatureAlgorithm, "BC");
+                    break;
+                default:
+                    throw new RuntimeException(implementation.toString());
+            }
+            verifier.initVerify(publicKey);
+            verifier.update(DATA);
+            verifier.verify(signature);
+        }
+    }
+}
diff --git a/benchmarks/regression/StrictMathBenchmark.java b/benchmarks/regression/StrictMathBenchmark.java
new file mode 100644
index 0000000..44c030a
--- /dev/null
+++ b/benchmarks/regression/StrictMathBenchmark.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Many of these tests are bogus in that the cost will vary wildly depending on inputs.
+ * For _my_ current purposes, that's okay. But beware!
+ */
+public class StrictMathBenchmark extends SimpleBenchmark {
+    private final double d = 1.2;
+    private final float f = 1.2f;
+    private final int i = 1;
+    private final long l = 1L;
+
+    public void timeAbsD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.abs(d);
+        }
+    }
+
+    public void timeAbsF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.abs(f);
+        }
+    }
+
+    public void timeAbsI(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.abs(i);
+        }
+    }
+
+    public void timeAbsL(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.abs(l);
+        }
+    }
+
+    public void timeAcos(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.acos(d);
+        }
+    }
+
+    public void timeAsin(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.asin(d);
+        }
+    }
+
+    public void timeAtan(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.atan(d);
+        }
+    }
+
+    public void timeAtan2(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.atan2(3, 4);
+        }
+    }
+
+    public void timeCbrt(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.cbrt(d);
+        }
+    }
+
+    public void timeCeil(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.ceil(d);
+        }
+    }
+
+    public void timeCopySignD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.copySign(d, d);
+        }
+    }
+
+    public void timeCopySignF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.copySign(f, f);
+        }
+    }
+
+    public void timeCos(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.cos(d);
+        }
+    }
+
+    public void timeCosh(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.cosh(d);
+        }
+    }
+
+    public void timeExp(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.exp(d);
+        }
+    }
+
+    public void timeExpm1(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.expm1(d);
+        }
+    }
+
+    public void timeFloor(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.floor(d);
+        }
+    }
+
+    public void timeGetExponentD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.getExponent(d);
+        }
+    }
+
+    public void timeGetExponentF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.getExponent(f);
+        }
+    }
+
+    public void timeHypot(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.hypot(d, d);
+        }
+    }
+
+    public void timeIEEEremainder(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.IEEEremainder(d, d);
+        }
+    }
+
+    public void timeLog(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.log(d);
+        }
+    }
+
+    public void timeLog10(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.log10(d);
+        }
+    }
+
+    public void timeLog1p(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.log1p(d);
+        }
+    }
+
+    public void timeMaxD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.max(d, d);
+        }
+    }
+
+    public void timeMaxF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.max(f, f);
+        }
+    }
+
+    public void timeMaxI(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.max(i, i);
+        }
+    }
+
+    public void timeMaxL(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.max(l, l);
+        }
+    }
+
+    public void timeMinD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.min(d, d);
+        }
+    }
+
+    public void timeMinF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.min(f, f);
+        }
+    }
+
+    public void timeMinI(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.min(i, i);
+        }
+    }
+
+    public void timeMinL(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.min(l, l);
+        }
+    }
+
+    public void timeNextAfterD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.nextAfter(d, d);
+        }
+    }
+
+    public void timeNextAfterF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.nextAfter(f, f);
+        }
+    }
+
+    public void timeNextUpD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.nextUp(d);
+        }
+    }
+
+    public void timeNextUpF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.nextUp(f);
+        }
+    }
+
+    public void timePow(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.pow(d, d);
+        }
+    }
+
+    public void timeRandom(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.random();
+        }
+    }
+
+    public void timeRint(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.rint(d);
+        }
+    }
+
+    public void timeRoundD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.round(d);
+        }
+    }
+
+    public void timeRoundF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.round(f);
+        }
+    }
+
+    public void timeScalbD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.scalb(d, 5);
+        }
+    }
+
+    public void timeScalbF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.scalb(f, 5);
+        }
+    }
+
+    public void timeSignumD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.signum(d);
+        }
+    }
+
+    public void timeSignumF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.signum(f);
+        }
+    }
+
+    public void timeSin(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.sin(d);
+        }
+    }
+
+    public void timeSinh(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.sinh(d);
+        }
+    }
+
+    public void timeSqrt(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.sqrt(d);
+        }
+    }
+
+    public void timeTan(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.tan(d);
+        }
+    }
+
+    public void timeTanh(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.tanh(d);
+        }
+    }
+
+    public void timeToDegrees(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.toDegrees(d);
+        }
+    }
+
+    public void timeToRadians(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.toRadians(d);
+        }
+    }
+
+    public void timeUlpD(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.ulp(d);
+        }
+    }
+
+    public void timeUlpF(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            StrictMath.ulp(f);
+        }
+    }
+}
diff --git a/benchmarks/regression/StringBenchmark.java b/benchmarks/regression/StringBenchmark.java
new file mode 100644
index 0000000..e09ee8b
--- /dev/null
+++ b/benchmarks/regression/StringBenchmark.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class StringBenchmark extends SimpleBenchmark {
+    enum StringLengths {
+        EMPTY(""),
+        SHORT("short"),
+        EIGHTY(makeString(80)),
+        EIGHT_KI(makeString(8192));
+        final String value;
+        private StringLengths(String value) { this.value = value; }
+    }
+    @Param private StringLengths s;
+
+    private static String makeString(int length) {
+        StringBuilder result = new StringBuilder(length);
+        for (int i = 0; i < length; ++i) {
+            result.append((char) i);
+        }
+        return result.toString();
+    }
+
+    public void timeHashCode(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            s.value.hashCode();
+        }
+    }
+}
diff --git a/benchmarks/regression/StringBuilderBenchmark.java b/benchmarks/regression/StringBuilderBenchmark.java
new file mode 100644
index 0000000..79eff2a
--- /dev/null
+++ b/benchmarks/regression/StringBuilderBenchmark.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Tests the performance of various StringBuilder methods.
+ */
+public class StringBuilderBenchmark extends SimpleBenchmark {
+
+    @Param({"1", "10", "100"}) private int length;
+
+    public void timeAppendBoolean(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < length; ++j) {
+                sb.append(true);
+            }
+        }
+    }
+
+    public void timeAppendChar(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < length; ++j) {
+                sb.append('c');
+            }
+        }
+    }
+
+    public void timeAppendCharArray(int reps) {
+        char[] chars = "chars".toCharArray();
+        for (int i = 0; i < reps; ++i) {
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < length; ++j) {
+                sb.append(chars);
+            }
+        }
+    }
+
+    public void timeAppendCharSequence(int reps) {
+        CharSequence cs = "chars";
+        for (int i = 0; i < reps; ++i) {
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < length; ++j) {
+                sb.append(cs);
+            }
+        }
+    }
+
+    public void timeAppendDouble(int reps) {
+        double d = 1.2;
+        for (int i = 0; i < reps; ++i) {
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < length; ++j) {
+                sb.append(d);
+            }
+        }
+    }
+
+    public void timeAppendFloat(int reps) {
+        float f = 1.2f;
+        for (int i = 0; i < reps; ++i) {
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < length; ++j) {
+                sb.append(f);
+            }
+        }
+    }
+
+    public void timeAppendInt(int reps) {
+        int n = 123;
+        for (int i = 0; i < reps; ++i) {
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < length; ++j) {
+                sb.append(n);
+            }
+        }
+    }
+
+    public void timeAppendLong(int reps) {
+        long l = 123;
+        for (int i = 0; i < reps; ++i) {
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < length; ++j) {
+                sb.append(l);
+            }
+        }
+    }
+
+    public void timeAppendObject(int reps) {
+        // We don't want to time the toString, so ensure we're calling a trivial one...
+        Object o = new Object() {
+            @Override public String toString() {
+                return "constant";
+            }
+        };
+        for (int i = 0; i < reps; ++i) {
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < length; ++j) {
+                sb.append(o);
+            }
+        }
+    }
+
+    public void timeAppendString(int reps) {
+        String s = "chars";
+        for (int i = 0; i < reps; ++i) {
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < length; ++j) {
+                sb.append(s);
+            }
+        }
+    }
+}
diff --git a/benchmarks/regression/StringCaseMappingBenchmark.java b/benchmarks/regression/StringCaseMappingBenchmark.java
new file mode 100644
index 0000000..ba5b59e
--- /dev/null
+++ b/benchmarks/regression/StringCaseMappingBenchmark.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.util.Locale;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class StringCaseMappingBenchmark extends SimpleBenchmark {
+    enum Inputs {
+        EMPTY(""),
+
+        // TODO: include hairy inputs like turkish and greek.
+        // TODO: locale makes a difference too.
+
+        LOWER2(lower(2)),
+        UPPER2(upper(2)),
+        MIXED2(mixed(2)),
+
+        LOWER8(lower(8)),
+        UPPER8(upper(8)),
+        MIXED8(mixed(8)),
+
+        LOWER32(lower(32)),
+        UPPER32(upper(32)),
+        MIXED32(mixed(32)),
+
+        LOWER512(lower(512)),
+        UPPER512(upper(512)),
+        MIXED512(mixed(512)),
+
+        LOWER2048(lower(2048)),
+        UPPER2048(upper(2048)),
+        MIXED2048(mixed(2048)),
+
+        LOWER_1M(lower(1024*1024)),
+        UPPER_1M(upper(1024*1024)),
+        MIXED_1M(mixed(1024*1024));
+
+        final String value;
+        private Inputs(String value) { this.value = value; }
+        private static String lower(int length) {
+            return makeString(length, "a0b1c2d3e4f5g6h7i8j9klmnopqrstuvwxyz");
+        }
+        private static String upper(int length) {
+            return makeString(length, "A0B1C2D3E4F5G6H7I8J9KLMNOPQRSTUVWXYZ");
+        }
+        private static String mixed(int length) {
+            return makeString(length, "Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9KkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz");
+        }
+        private static String makeString(int length, String alphabet) {
+            StringBuilder sb = new StringBuilder(length);
+            for (int i = 0; i < length; ++i) {
+                sb.append(alphabet.charAt(i % alphabet.length()));
+            }
+            return sb.toString();
+        }
+    }
+    @Param private Inputs s;
+
+    public void timeToUpperCase_US(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            s.value.toUpperCase(Locale.US);
+        }
+    }
+
+    public void timeToLowerCase_US(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            s.value.toUpperCase(Locale.US);
+        }
+    }
+
+    public void timeToUpperCase_Ascii(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            toUpperCaseAscii(s.value);
+        }
+    }
+
+    public void timeToLowerCase_Ascii(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            toUpperCaseAscii(s.value);
+        }
+    }
+
+    public void timeToUpperCase_ICU(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            libcore.icu.ICU.toUpperCase(s.value, Locale.US.toString());
+        }
+    }
+
+    public void timeToLowerCase_ICU(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            libcore.icu.ICU.toLowerCase(s.value, Locale.US.toString());
+        }
+    }
+
+    public static String toUpperCaseAscii(String s) {
+        for (int i = 0, length = s.length(); i < length; i++) {
+            char c = s.charAt(i);
+            if (c < 'a' || c > 'z') {
+                continue; // fast path avoids allocation
+            }
+
+            // slow path: s contains lower case chars
+            char[] result = s.toCharArray();
+            for (; i < length; i++) {
+                c = result[i];
+                if (c >= 'a' && c <= 'z') {
+                    result[i] -= ('a' - 'A');
+                }
+            }
+            return new String(result);
+        }
+        return s;
+    }
+
+    public static String toLowerCaseAscii(String s) {
+        for (int i = 0, length = s.length(); i < length; i++) {
+            char c = s.charAt(i);
+            if (c < 'A' || c > 'Z') {
+                continue; // fast path avoids allocation
+            }
+
+            // slow path: s contains upper case chars
+            char[] result = s.toCharArray();
+            for (; i < length; i++) {
+                c = result[i];
+                if (c >= 'A' && c <= 'Z') {
+                    result[i] += ('a' - 'A');
+                }
+            }
+            return new String(result);
+        }
+        return s;
+    }
+}
diff --git a/benchmarks/regression/StringIsEmptyBenchmark.java b/benchmarks/regression/StringIsEmptyBenchmark.java
new file mode 100644
index 0000000..a3be80a
--- /dev/null
+++ b/benchmarks/regression/StringIsEmptyBenchmark.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class StringIsEmptyBenchmark extends SimpleBenchmark {
+    public void timeIsEmpty_NonEmpty(int reps) {
+        boolean result = true;
+        for (int i = 0; i < reps; ++i) {
+            result &= !("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".isEmpty());
+        }
+        if (!result) throw new RuntimeException();
+    }
+    
+    public void timeIsEmpty_Empty(int reps) {
+        boolean result = true;
+        for (int i = 0; i < reps; ++i) {
+            result &= ("".isEmpty());
+        }
+        if (!result) throw new RuntimeException();
+    }
+    
+    public void timeLengthEqualsZero(int reps) {
+        boolean result = true;
+        for (int i = 0; i < reps; ++i) {
+            result &= !("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".length() == 0);
+        }
+        if (!result) throw new RuntimeException();
+    }
+ 
+    public void timeEqualsEmpty(int reps) {
+        boolean result = true;
+        for (int i = 0; i < reps; ++i) {
+            result &= !"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".equals("");
+        }
+        if (!result) throw new RuntimeException();
+    }
+}
diff --git a/benchmarks/regression/StringLengthBenchmark.java b/benchmarks/regression/StringLengthBenchmark.java
new file mode 100644
index 0000000..962e395
--- /dev/null
+++ b/benchmarks/regression/StringLengthBenchmark.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class StringLengthBenchmark extends SimpleBenchmark {
+    public void timeLength(int reps) {
+        int length = 0;
+        for (int i = 0; i < reps; ++i) {
+            length = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".length();
+        }
+        if (length != 51) throw new RuntimeException();
+    }
+}
diff --git a/benchmarks/regression/StringSplitBenchmark.java b/benchmarks/regression/StringSplitBenchmark.java
new file mode 100644
index 0000000..37fdf8f
--- /dev/null
+++ b/benchmarks/regression/StringSplitBenchmark.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+import java.util.regex.Pattern;
+
+public class StringSplitBenchmark extends SimpleBenchmark {
+    public void timeStringSplitComma(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            "this,is,a,simple,example".split(",");
+        }
+    }
+
+    public void timeStringSplitLiteralDot(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            "this.is.a.simple.example".split("\\.");
+        }
+    }
+
+    public void timeStringSplitNewline(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            "this\nis\na\nsimple\nexample\n".split("\n");
+        }
+    }
+
+    public void timePatternSplitComma(int reps) {
+        Pattern p = Pattern.compile(",");
+        for (int i = 0; i < reps; ++i) {
+            p.split("this,is,a,simple,example");
+        }
+    }
+
+    public void timePatternSplitLiteralDot(int reps) {
+        Pattern p = Pattern.compile("\\.");
+        for (int i = 0; i < reps; ++i) {
+            p.split("this.is.a.simple.example");
+        }
+    }
+
+    public void timeStringSplitHard(int reps) {
+        for (int i = 0; i < reps; ++i) {
+            "this,is,a,harder,example".split("[,]");
+        }
+    }
+}
diff --git a/benchmarks/regression/StringToRealBenchmark.java b/benchmarks/regression/StringToRealBenchmark.java
new file mode 100644
index 0000000..ef7a633
--- /dev/null
+++ b/benchmarks/regression/StringToRealBenchmark.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class StringToRealBenchmark extends SimpleBenchmark {
+
+    @Param({
+        "NaN",
+        "-1",
+        "0",
+        "1",
+        "1.2",
+        "-123.45",
+        "-123.45e8",
+        "-123.45e36"
+    }) String string;
+
+    public void timeFloat_parseFloat(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Float.parseFloat(string);
+        }
+    }
+
+    public void timeDouble_parseDouble(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            Double.parseDouble(string);
+        }
+    }
+}
diff --git a/benchmarks/regression/SystemPropertiesBenchmark.java b/benchmarks/regression/SystemPropertiesBenchmark.java
new file mode 100644
index 0000000..9c675e1
--- /dev/null
+++ b/benchmarks/regression/SystemPropertiesBenchmark.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+
+public class SystemPropertiesBenchmark extends SimpleBenchmark {
+    public void timeGetProperties(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            // Force the properties to be recreated.
+            System.setProperties(null);
+            System.getProperties();
+        }
+    }
+}
diff --git a/benchmarks/regression/ThreadLocalBenchmark.java b/benchmarks/regression/ThreadLocalBenchmark.java
new file mode 100644
index 0000000..df0715b
--- /dev/null
+++ b/benchmarks/regression/ThreadLocalBenchmark.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class ThreadLocalBenchmark extends SimpleBenchmark {
+    private static final ThreadLocal<char[]> BUFFER = new ThreadLocal<char[]>() {
+        @Override protected char[] initialValue() {
+            return new char[20];
+        }
+    };
+
+    public void timeThreadLocal_get(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            BUFFER.get();
+        }
+    }
+}
diff --git a/benchmarks/regression/TimeZoneBenchmark.java b/benchmarks/regression/TimeZoneBenchmark.java
new file mode 100644
index 0000000..ccef392
--- /dev/null
+++ b/benchmarks/regression/TimeZoneBenchmark.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+import java.util.TimeZone;
+
+public class TimeZoneBenchmark extends SimpleBenchmark {
+    public void timeTimeZone_getDefault(int reps) throws Exception {
+        for (int rep = 0; rep < reps; ++rep) {
+            TimeZone.getDefault();
+        }
+    }
+
+    public void timeTimeZone_getTimeZoneUTC(int reps) throws Exception {
+        for (int rep = 0; rep < reps; ++rep) {
+            TimeZone.getTimeZone("UTC");
+        }
+    }
+
+    public void timeTimeZone_getTimeZone_default(int reps) throws Exception {
+        String defaultId = TimeZone.getDefault().getID();
+        for (int rep = 0; rep < reps; ++rep) {
+            TimeZone.getTimeZone(defaultId);
+        }
+    }
+
+    // A time zone with relatively few transitions.
+    public void timeTimeZone_getTimeZone_America_Caracas(int reps) throws Exception {
+        for (int rep = 0; rep < reps; ++rep) {
+            TimeZone.getTimeZone("America/Caracas");
+        }
+    }
+
+    // A time zone with a lot of transitions.
+    public void timeTimeZone_getTimeZone_America_Santiago(int reps) throws Exception {
+        for (int rep = 0; rep < reps; ++rep) {
+            TimeZone.getTimeZone("America/Santiago");
+        }
+    }
+
+    public void timeTimeZone_getTimeZone_GMT_plus_10(int reps) throws Exception {
+        for (int rep = 0; rep < reps; ++rep) {
+            TimeZone.getTimeZone("GMT+10");
+        }
+    }
+}
diff --git a/benchmarks/regression/URLConnectionBenchmark.java b/benchmarks/regression/URLConnectionBenchmark.java
new file mode 100644
index 0000000..e5ceec4
--- /dev/null
+++ b/benchmarks/regression/URLConnectionBenchmark.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import tests.http.MockResponse;
+import tests.http.MockWebServer;
+
+public final class URLConnectionBenchmark extends SimpleBenchmark {
+
+    @Param({"0", "1024", "1048576"}) private int bodySize;
+    @Param({"2048"}) private int chunkSize;
+    @Param({"1024"}) private int readBufferSize;
+    @Param private ResponseHeaders responseHeaders;
+    @Param private TransferEncoding transferEncoding;
+    private byte[] readBuffer;
+
+    private MockWebServer server;
+    private URL url;
+
+    protected void setUp() throws Exception {
+        readBuffer = new byte[readBufferSize];
+        server = new MockWebServer();
+
+        MockResponse response = new MockResponse();
+        responseHeaders.apply(response);
+        transferEncoding.setBody(response, bodySize, chunkSize);
+        server.enqueue(response);
+
+        server.setContinuousServing(true);
+        server.play();
+
+        url = server.getUrl("/");
+        get(); // ensure the server has started its threads, etc.
+    }
+
+    protected void tearDown() throws Exception {
+        /*
+         * Entice the server to shut itself down gracefully. The shutdown method
+         * doesn't work on Dalvik because socket.close() doesn't release blocked
+         * threads. Instead, read the last continuously-served request, and then
+         * cause the server to close the otherwise-reusable HTTP connection.
+         */
+        server.setContinuousServing(false);
+        server.enqueue(new MockResponse().setDisconnectAtEnd(true));
+        get();
+        get();
+        server.shutdown();
+    }
+
+    public int timeGet(int reps) throws IOException {
+        int totalBytesRead = 0;
+        for (int i = 0; i < reps; i++) {
+            totalBytesRead += get();
+        }
+        return totalBytesRead;
+    }
+
+    private int get() throws IOException {
+        int totalBytesRead = 0;
+        URLConnection connection = url.openConnection();
+        InputStream in = connection.getInputStream();
+        int count;
+        while ((count = in.read(readBuffer)) != -1) {
+            totalBytesRead += count;
+        }
+        return totalBytesRead;
+    }
+
+    enum TransferEncoding {
+        FIXED_LENGTH,
+        CHUNKED;
+
+        void setBody(MockResponse response, int bodySize, int chunkSize) throws IOException {
+            if (this == TransferEncoding.FIXED_LENGTH) {
+                response.setBody(new byte[bodySize]);
+            } else if (this == TransferEncoding.CHUNKED) {
+                response.setChunkedBody(new byte[bodySize], chunkSize);
+            }
+        }
+    }
+
+    enum ResponseHeaders {
+        MINIMAL,
+        TYPICAL;
+
+        void apply(MockResponse response) {
+            if (this == TYPICAL) {
+                /* from http://api.twitter.com/1/statuses/public_timeline.json */
+                response.addHeader("Date: Wed, 30 Jun 2010 17:57:39 GMT");
+                response.addHeader("Server: hi");
+                response.addHeader("X-RateLimit-Remaining: 0");
+                response.addHeader("X-Runtime: 0.01637");
+                response.addHeader("Content-Type: application/json; charset=utf-8");
+                response.addHeader("X-RateLimit-Class: api_whitelisted");
+                response.addHeader("Cache-Control: no-cache, max-age=300");
+                response.addHeader("X-RateLimit-Reset: 1277920980");
+                response.addHeader("Set-Cookie: _twitter_sess=BAh7EDoOcmV0dXJuX3RvIjZodHRwOi8vZGV2L"
+                        + "nR3aXR0ZXIuY29tL3BhZ2Vz%250AL3NpZ25faW5fd2l0aF90d2l0dGVyOgxjc3JmX2lkIiUw"
+                        + "ODFhNGY2NTM5NjRm%250ANjY1N2M2NzcwNWI0MDlmZGZjZjoVaW5fbmV3X3VzZXJfZmxvdzA"
+                        + "6EXRyYW5z%250AX3Byb21wdDAiKXNob3dfZGlzY292ZXJhYmlsaXR5X2Zvcl9qZXNzZXdpbH"
+                        + "Nv%250AbjA6E3Nob3dfaGVscF9saW5rMDoTcGFzc3dvcmRfdG9rZW4iLWUyYjlhNmM3%250A"
+                        + "MWJiNzI3NWNlZDI1NDY3MGMzZWNmMTE0MjI4N2EyNGE6D2NyZWF0ZWRfYXRs%250AKwhiM%2"
+                        + "52F6JKQE6CXVzZXJpA8tE3iIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxl%250Acjo6Rmxhc2"
+                        + "g6OkZsYXNoSGFzaHsABjoKQHVzZWR7ADoHaWQiJWZmMTNhM2Qx%250AZTU1YTkzMmYyMWM0M"
+                        + "GNhZjU4NDVjMTQz--11250628c85830219438eb7eba96a541a9af4098; domain=.twitt"
+                        + "er.com; path=/");
+                response.addHeader("Expires: Wed, 30 Jun 2010 18:02:39 GMT");
+                response.addHeader("Vary: Accept-Encoding");
+            }
+        }
+    }
+}
diff --git a/benchmarks/regression/XmlEntitiesBenchmark.java b/benchmarks/regression/XmlEntitiesBenchmark.java
new file mode 100644
index 0000000..4c5cc18
--- /dev/null
+++ b/benchmarks/regression/XmlEntitiesBenchmark.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.io.StringReader;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.xml.sax.InputSource;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+// http://code.google.com/p/android/issues/detail?id=18102
+public final class XmlEntitiesBenchmark extends SimpleBenchmark {
+  
+  @Param({"10", "100", "1000"}) int length;
+  @Param({"0", "0.5", "1.0"}) float entityFraction;
+
+  private XmlPullParserFactory xmlPullParserFactory;
+  private DocumentBuilderFactory documentBuilderFactory;
+
+  /** a string like {@code <doc>&amp;&amp;++</doc>}. */
+  private String xml;
+
+  @Override protected void setUp() throws Exception {
+    xmlPullParserFactory = XmlPullParserFactory.newInstance();
+    documentBuilderFactory = DocumentBuilderFactory.newInstance();
+
+    StringBuilder xmlBuilder = new StringBuilder();
+    xmlBuilder.append("<doc>");
+    for (int i = 0; i < (length * entityFraction); i++) {
+      xmlBuilder.append("&amp;");
+    }
+    while (xmlBuilder.length() < length) {
+      xmlBuilder.append("+");
+    }
+    xmlBuilder.append("</doc>");
+    xml = xmlBuilder.toString();
+  }
+  
+  public void timeXmlParser(int reps) throws Exception {
+    for (int i = 0; i < reps; i++) {
+      XmlPullParser parser = xmlPullParserFactory.newPullParser();
+      parser.setInput(new StringReader(xml));
+      while (parser.next() != XmlPullParser.END_DOCUMENT) {
+      }
+    }
+  }
+  
+  public void timeDocumentBuilder(int reps) throws Exception {
+    for (int i = 0; i < reps; i++) {
+      DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+      documentBuilder.parse(new InputSource(new StringReader(xml)));
+    }
+  }
+}
diff --git a/dalvik/MODULE_LICENSE_APACHE2 b/dalvik/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dalvik/MODULE_LICENSE_APACHE2
diff --git a/dalvik/src/main/java/dalvik/annotation/AndroidOnly.java b/dalvik/src/main/java/dalvik/annotation/AndroidOnly.java
new file mode 100644
index 0000000..da3c1c5
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/AndroidOnly.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test-case as Android-only, that is, it should not be executed on
+ * other systems.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface AndroidOnly {
+
+    /**
+     * Plain text reason for adding this annotation.
+     */
+    String value();
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/AnnotationDefault.java b/dalvik/src/main/java/dalvik/annotation/AnnotationDefault.java
new file mode 100644
index 0000000..98062fb
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/AnnotationDefault.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide the AnnotationDefault attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface AnnotationDefault {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/BrokenTest.java b/dalvik/src/main/java/dalvik/annotation/BrokenTest.java
new file mode 100644
index 0000000..401d652
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/BrokenTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test case as broken. This means the test case should be fixed.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface BrokenTest {
+
+    /**
+     * Plain text reason for adding this annotation.
+     */
+    String value();
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/EnclosingClass.java b/dalvik/src/main/java/dalvik/annotation/EnclosingClass.java
new file mode 100644
index 0000000..2546c4c
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/EnclosingClass.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide part of the InnerClasses attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface EnclosingClass {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/EnclosingMethod.java b/dalvik/src/main/java/dalvik/annotation/EnclosingMethod.java
new file mode 100644
index 0000000..1a97155
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/EnclosingMethod.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide the EnclosingMethod attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface EnclosingMethod {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/InnerClass.java b/dalvik/src/main/java/dalvik/annotation/InnerClass.java
new file mode 100644
index 0000000..054842f
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/InnerClass.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide part of the InnerClasses attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface InnerClass {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/KnownFailure.java b/dalvik/src/main/java/dalvik/annotation/KnownFailure.java
new file mode 100644
index 0000000..11e813d
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/KnownFailure.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test case as a known failure. This means the underlying
+ * implementation should be fixed. Seems to be similar to @code{@ToBeFixed}, so
+ * maybe the two can be merged at some point.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface KnownFailure {
+
+    /**
+     * Plain text reason for adding this annotation.
+     */
+    String value();
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/MemberClasses.java b/dalvik/src/main/java/dalvik/annotation/MemberClasses.java
new file mode 100644
index 0000000..b213be7
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/MemberClasses.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide the MemberClasses list.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface MemberClasses {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/SideEffect.java b/dalvik/src/main/java/dalvik/annotation/SideEffect.java
new file mode 100644
index 0000000..b92e9bc
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/SideEffect.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test-case as either having a side-effect that other tests might
+ * notice or suffering from such a side effect. Such tests should be run in an
+ * isolated manner.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface SideEffect {
+
+    /**
+     * Plain text reason for adding this annotation.
+     */
+    String value();
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/Signature.java b/dalvik/src/main/java/dalvik/annotation/Signature.java
new file mode 100644
index 0000000..b96d7dd
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/Signature.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide the Signature attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface Signature {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/TestLevel.java b/dalvik/src/main/java/dalvik/annotation/TestLevel.java
new file mode 100644
index 0000000..f62ea2f
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/TestLevel.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+/**
+ * Defines an enumeration of possible states a test case can be in.
+ *
+ * @hide
+ */
+public enum TestLevel {
+
+    /**
+     * Indicates that a test method completely tests its target API method.
+     */
+    COMPLETE,
+
+    /**
+     * Indicates that a test method partially tests its target API method and
+     * that together with all other {@code PARTIAL_COMPLETE} tests for the same
+     * method it is sufficient.
+     */
+    PARTIAL_COMPLETE,
+
+    /**
+     * Just for compatibility purposes, will be removed later.
+     */
+    PARTIAL_OK,
+
+    /**
+     * Indicates that a test method partially tests its target API method. It
+     * needs a second review phase to find out if the sum of all partial tests
+     * is sufficient for completely testing the target API method. If yes, the
+     * level has to be changed to {@code PARTIAL_COMPLETE}.
+     */
+    PARTIAL,
+
+    /**
+     * Indicates that a test method is known to not completely test an API
+     * method but the missing test steps are too complex and costly to
+     * implement. This level is positioned somewhere between {@code PARTIAL}
+     * and {@code COMPLETE}.
+     */
+    SUFFICIENT,
+
+    /**
+     * Indicates that a test method provides additional testing for an API
+     * method for which there already exists one {@code COMPLETE} or a set of
+     * {@code PARTIAL_COMPLETE} tests. This level may also be used for test
+     * methods that test a concept which can not be directly attributed to
+     * the specification of an API method or class.
+     */
+    ADDITIONAL,
+
+    /**
+     * Indicates that there is nothing to test in an API method, for example if
+     * the specification states that a method does nothing.
+     */
+    NOT_NECESSARY,
+
+    /**
+     * Indicates that it is very hard or impossible to test an API method.
+     */
+    NOT_FEASIBLE,
+
+    /**
+     * Indicates that the tests is either insufficient or wrong. Something needs
+     * to be done about it.
+     */
+    TODO,
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/TestTarget.java b/dalvik/src/main/java/dalvik/annotation/TestTarget.java
new file mode 100644
index 0000000..dcfd20c
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/TestTarget.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines an annotation used be used within the TestInfo annotation. It
+ * specifies a single method target for the test (but can be used multiple
+ * times).
+ *
+ * @deprecated Obsolete
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.ANNOTATION_TYPE })
+@Deprecated
+public @interface TestTarget {
+
+    /**
+     * Specifies the name of the method that is being tested.
+     */
+    String methodName() default "";
+
+    /**
+     * Specifies the name of a concept being tested. Use this if
+     * {@code methodName} is not accurate enough. E.g. for
+     * {@link java.util.regex.Pattern#compile(String)} {@code methodName} is not
+     * sufficient since the String contains a pattern with its own syntax which
+     * has to be tested with different aspects. Areas concerned are e.g. JDBC
+     * (SELECT, INSERT, UPDATE, DELETE, ...), regex (character sets,
+     * operators,...), formatters (DecimalFormat, DateFormat, ChoiceFormat,
+     * ...), ...
+     */
+    String conceptName() default "";
+
+    /**
+     * Specifies the signature of the method that is being tested, in terms of
+     * Java classes.
+     */
+    Class<?>[] methodArgs() default {};
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/TestTargetClass.java b/dalvik/src/main/java/dalvik/annotation/TestTargetClass.java
new file mode 100644
index 0000000..e88040e
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/TestTargetClass.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines an annotation for test classes that allows to link them to the class
+ * that is being tested. The current assumption is that the test are somewhat
+ * organized according to the API classes they test. Might be too strict for
+ * some cases.
+ *
+ * @deprecated Obsolete
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+@Deprecated
+public @interface TestTargetClass {
+
+    /**
+     * Specifies the class being tested.
+     */
+    Class<?> value();
+
+    /**
+     * Option to specify untested methods for the class.
+     * @hide
+     */
+    TestTargetNew[] untestedMethods() default {};
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/TestTargetNew.java b/dalvik/src/main/java/dalvik/annotation/TestTargetNew.java
new file mode 100644
index 0000000..80aebee
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/TestTargetNew.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines an annotation used be used within the TestInfo annotation. It
+ * specifies a single method target for the test (but can be used multiple
+ * times).
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
+public @interface TestTargetNew {
+
+    /**
+     * Specifies the name of the API method that is being tested. This field
+     * may be left empty if the test target is a concept implemented in a
+     * class rather than a concrete API method.
+     */
+    String method() default "";
+
+    /**
+     * Specifies the signature of the API method that is being tested, in terms
+     * of Java classes.
+     */
+    Class<?>[] args() default {};
+
+    /**
+     * Specifies the class to which the tested method belongs. If this value is
+     * not provided, the class identified in @TestTargetClass is used by the
+     * test progress doclet.
+     */
+    Class<?> clazz() default void.class;
+
+    /**
+     * Specifies the level of coverage the tested API method has.
+     */
+    TestLevel level();
+
+    /**
+     * Specifies noteworthy plain-text information about the test, for example
+     * if something is NOT covered by the test method.
+     */
+    String notes() default "";
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/TestTargets.java b/dalvik/src/main/java/dalvik/annotation/TestTargets.java
new file mode 100644
index 0000000..207572c
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/TestTargets.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines an annotation for test classes that allows to link them to the class
+ * that is being tested. The current assumption is that the test are somewhat
+ * organized according to the API classes they test. Might be too strict for
+ * some cases.
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface TestTargets {
+
+    /**
+     * Specifies the API methods that are tested by the annotated test method.
+     */
+    TestTargetNew[] value();
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/Throws.java b/dalvik/src/main/java/dalvik/annotation/Throws.java
new file mode 100644
index 0000000..161116a
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/Throws.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide the Exceptions attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface Throws {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/ToBeFixed.java b/dalvik/src/main/java/dalvik/annotation/ToBeFixed.java
new file mode 100644
index 0000000..751a038
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/ToBeFixed.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines an annotation for test methods that indicate the test method
+ * need to be fixed in future.
+ * {@hide pending API Council approval}
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface ToBeFixed {
+
+    /**
+     * Specifies the related bug number on CTS bug tracking system.
+     */
+    String bug() default "";
+
+    /**
+     * Specifies why this method need to be fixed. If we think it's a bug, explain
+     * the expectation and the actual result.
+     */
+    String explanation() default "";
+}
diff --git a/dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java b/dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java
new file mode 100644
index 0000000..1209b2e
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.bytecode;
+
+/**
+ * Information about Dalvik opcodes.
+ */
+public final class OpcodeInfo {
+    /**
+     * The maximum possible value of a Dalvik opcode.
+     *
+     * <p><b>Note:</b>: This is constant in any given VM incarnation,
+     * but it is subject to change over time, so it is not appropriate
+     * to represent as a compile-time constant value.</p>
+     */
+    public static final int MAXIMUM_VALUE;
+
+    /**
+     * The maximum possible "packed value" of a Dalvik opcode. The
+     * packed value of an opcode is a denser representation that is
+     * only used when reporting usage statistics. The mapping between
+     * packed opcode values and regular opcode values is
+     * implementation-specific and may vary over time.
+     *
+     * <p><b>Note:</b>: This is constant in any given VM incarnation,
+     * but it is subject to change over time, so it is not appropriate
+     * to represent as a compile-time constant value.</p>
+     *
+     * @see dalvik.system.VMDebug.getInstructionCount()
+     */
+    public static final int MAXIMUM_PACKED_VALUE;
+
+    static {
+        /*
+         * See note on the definition of MAXIMUM_VALUE, above, for
+         * why it's not assigned directly on the declaration line.
+         *
+         * IMPORTANT NOTE: This assignment is generated automatically
+         * by the opcode-gen tool. Any edits will get wiped out the
+         * next time the tool is run.
+         */
+        // BEGIN(libcore-maximum-values); GENERATED AUTOMATICALLY BY opcode-gen
+        MAXIMUM_VALUE = 65535;
+        MAXIMUM_PACKED_VALUE = 255;
+        // END(libcore-maximum-values)
+    }
+
+    /**
+     * This class is not instantiable.
+     */
+    private OpcodeInfo() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Returns whether the given packed opcode value represents a
+     * method invocation operation. This includes most things that
+     * look like method invocation at the source level, but it notably
+     * excludes methods that are implemented directly in the VM as
+     * well as ones the VM knows to have empty implementations.
+     *
+     * @hide Unclear if this is useful enough to publish as supported API.
+     *
+     * @param opcode one of the values defined in {@link Opcodes}
+     */
+    public static native boolean isInvoke(int packedOpcode);
+}
diff --git a/dalvik/src/main/java/dalvik/bytecode/Opcodes.java b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
new file mode 100644
index 0000000..f758d65
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.bytecode;
+
+/**
+ * A list of all normal (not implementation-specific) Dalvik opcodes.
+ */
+public interface Opcodes {
+    /*
+     * IMPORTANT NOTE: The contents of this file are mostly generated
+     * automatically by the opcode-gen tool. Any edits to the generated
+     * sections will get wiped out the next time the tool is run.
+     */
+
+    // BEGIN(libcore-opcodes); GENERATED AUTOMATICALLY BY opcode-gen
+    int OP_NOP                          = 0x0000;
+    int OP_MOVE                         = 0x0001;
+    int OP_MOVE_FROM16                  = 0x0002;
+    int OP_MOVE_16                      = 0x0003;
+    int OP_MOVE_WIDE                    = 0x0004;
+    int OP_MOVE_WIDE_FROM16             = 0x0005;
+    int OP_MOVE_WIDE_16                 = 0x0006;
+    int OP_MOVE_OBJECT                  = 0x0007;
+    int OP_MOVE_OBJECT_FROM16           = 0x0008;
+    int OP_MOVE_OBJECT_16               = 0x0009;
+    int OP_MOVE_RESULT                  = 0x000a;
+    int OP_MOVE_RESULT_WIDE             = 0x000b;
+    int OP_MOVE_RESULT_OBJECT           = 0x000c;
+    int OP_MOVE_EXCEPTION               = 0x000d;
+    int OP_RETURN_VOID                  = 0x000e;
+    int OP_RETURN                       = 0x000f;
+    int OP_RETURN_WIDE                  = 0x0010;
+    int OP_RETURN_OBJECT                = 0x0011;
+    int OP_CONST_4                      = 0x0012;
+    int OP_CONST_16                     = 0x0013;
+    int OP_CONST                        = 0x0014;
+    int OP_CONST_HIGH16                 = 0x0015;
+    int OP_CONST_WIDE_16                = 0x0016;
+    int OP_CONST_WIDE_32                = 0x0017;
+    int OP_CONST_WIDE                   = 0x0018;
+    int OP_CONST_WIDE_HIGH16            = 0x0019;
+    int OP_CONST_STRING                 = 0x001a;
+    int OP_CONST_STRING_JUMBO           = 0x001b;
+    int OP_CONST_CLASS                  = 0x001c;
+    int OP_MONITOR_ENTER                = 0x001d;
+    int OP_MONITOR_EXIT                 = 0x001e;
+    int OP_CHECK_CAST                   = 0x001f;
+    int OP_INSTANCE_OF                  = 0x0020;
+    int OP_ARRAY_LENGTH                 = 0x0021;
+    int OP_NEW_INSTANCE                 = 0x0022;
+    int OP_NEW_ARRAY                    = 0x0023;
+    int OP_FILLED_NEW_ARRAY             = 0x0024;
+    int OP_FILLED_NEW_ARRAY_RANGE       = 0x0025;
+    int OP_FILL_ARRAY_DATA              = 0x0026;
+    int OP_THROW                        = 0x0027;
+    int OP_GOTO                         = 0x0028;
+    int OP_GOTO_16                      = 0x0029;
+    int OP_GOTO_32                      = 0x002a;
+    int OP_PACKED_SWITCH                = 0x002b;
+    int OP_SPARSE_SWITCH                = 0x002c;
+    int OP_CMPL_FLOAT                   = 0x002d;
+    int OP_CMPG_FLOAT                   = 0x002e;
+    int OP_CMPL_DOUBLE                  = 0x002f;
+    int OP_CMPG_DOUBLE                  = 0x0030;
+    int OP_CMP_LONG                     = 0x0031;
+    int OP_IF_EQ                        = 0x0032;
+    int OP_IF_NE                        = 0x0033;
+    int OP_IF_LT                        = 0x0034;
+    int OP_IF_GE                        = 0x0035;
+    int OP_IF_GT                        = 0x0036;
+    int OP_IF_LE                        = 0x0037;
+    int OP_IF_EQZ                       = 0x0038;
+    int OP_IF_NEZ                       = 0x0039;
+    int OP_IF_LTZ                       = 0x003a;
+    int OP_IF_GEZ                       = 0x003b;
+    int OP_IF_GTZ                       = 0x003c;
+    int OP_IF_LEZ                       = 0x003d;
+    int OP_AGET                         = 0x0044;
+    int OP_AGET_WIDE                    = 0x0045;
+    int OP_AGET_OBJECT                  = 0x0046;
+    int OP_AGET_BOOLEAN                 = 0x0047;
+    int OP_AGET_BYTE                    = 0x0048;
+    int OP_AGET_CHAR                    = 0x0049;
+    int OP_AGET_SHORT                   = 0x004a;
+    int OP_APUT                         = 0x004b;
+    int OP_APUT_WIDE                    = 0x004c;
+    int OP_APUT_OBJECT                  = 0x004d;
+    int OP_APUT_BOOLEAN                 = 0x004e;
+    int OP_APUT_BYTE                    = 0x004f;
+    int OP_APUT_CHAR                    = 0x0050;
+    int OP_APUT_SHORT                   = 0x0051;
+    int OP_IGET                         = 0x0052;
+    int OP_IGET_WIDE                    = 0x0053;
+    int OP_IGET_OBJECT                  = 0x0054;
+    int OP_IGET_BOOLEAN                 = 0x0055;
+    int OP_IGET_BYTE                    = 0x0056;
+    int OP_IGET_CHAR                    = 0x0057;
+    int OP_IGET_SHORT                   = 0x0058;
+    int OP_IPUT                         = 0x0059;
+    int OP_IPUT_WIDE                    = 0x005a;
+    int OP_IPUT_OBJECT                  = 0x005b;
+    int OP_IPUT_BOOLEAN                 = 0x005c;
+    int OP_IPUT_BYTE                    = 0x005d;
+    int OP_IPUT_CHAR                    = 0x005e;
+    int OP_IPUT_SHORT                   = 0x005f;
+    int OP_SGET                         = 0x0060;
+    int OP_SGET_WIDE                    = 0x0061;
+    int OP_SGET_OBJECT                  = 0x0062;
+    int OP_SGET_BOOLEAN                 = 0x0063;
+    int OP_SGET_BYTE                    = 0x0064;
+    int OP_SGET_CHAR                    = 0x0065;
+    int OP_SGET_SHORT                   = 0x0066;
+    int OP_SPUT                         = 0x0067;
+    int OP_SPUT_WIDE                    = 0x0068;
+    int OP_SPUT_OBJECT                  = 0x0069;
+    int OP_SPUT_BOOLEAN                 = 0x006a;
+    int OP_SPUT_BYTE                    = 0x006b;
+    int OP_SPUT_CHAR                    = 0x006c;
+    int OP_SPUT_SHORT                   = 0x006d;
+    int OP_INVOKE_VIRTUAL               = 0x006e;
+    int OP_INVOKE_SUPER                 = 0x006f;
+    int OP_INVOKE_DIRECT                = 0x0070;
+    int OP_INVOKE_STATIC                = 0x0071;
+    int OP_INVOKE_INTERFACE             = 0x0072;
+    int OP_INVOKE_VIRTUAL_RANGE         = 0x0074;
+    int OP_INVOKE_SUPER_RANGE           = 0x0075;
+    int OP_INVOKE_DIRECT_RANGE          = 0x0076;
+    int OP_INVOKE_STATIC_RANGE          = 0x0077;
+    int OP_INVOKE_INTERFACE_RANGE       = 0x0078;
+    int OP_NEG_INT                      = 0x007b;
+    int OP_NOT_INT                      = 0x007c;
+    int OP_NEG_LONG                     = 0x007d;
+    int OP_NOT_LONG                     = 0x007e;
+    int OP_NEG_FLOAT                    = 0x007f;
+    int OP_NEG_DOUBLE                   = 0x0080;
+    int OP_INT_TO_LONG                  = 0x0081;
+    int OP_INT_TO_FLOAT                 = 0x0082;
+    int OP_INT_TO_DOUBLE                = 0x0083;
+    int OP_LONG_TO_INT                  = 0x0084;
+    int OP_LONG_TO_FLOAT                = 0x0085;
+    int OP_LONG_TO_DOUBLE               = 0x0086;
+    int OP_FLOAT_TO_INT                 = 0x0087;
+    int OP_FLOAT_TO_LONG                = 0x0088;
+    int OP_FLOAT_TO_DOUBLE              = 0x0089;
+    int OP_DOUBLE_TO_INT                = 0x008a;
+    int OP_DOUBLE_TO_LONG               = 0x008b;
+    int OP_DOUBLE_TO_FLOAT              = 0x008c;
+    int OP_INT_TO_BYTE                  = 0x008d;
+    int OP_INT_TO_CHAR                  = 0x008e;
+    int OP_INT_TO_SHORT                 = 0x008f;
+    int OP_ADD_INT                      = 0x0090;
+    int OP_SUB_INT                      = 0x0091;
+    int OP_MUL_INT                      = 0x0092;
+    int OP_DIV_INT                      = 0x0093;
+    int OP_REM_INT                      = 0x0094;
+    int OP_AND_INT                      = 0x0095;
+    int OP_OR_INT                       = 0x0096;
+    int OP_XOR_INT                      = 0x0097;
+    int OP_SHL_INT                      = 0x0098;
+    int OP_SHR_INT                      = 0x0099;
+    int OP_USHR_INT                     = 0x009a;
+    int OP_ADD_LONG                     = 0x009b;
+    int OP_SUB_LONG                     = 0x009c;
+    int OP_MUL_LONG                     = 0x009d;
+    int OP_DIV_LONG                     = 0x009e;
+    int OP_REM_LONG                     = 0x009f;
+    int OP_AND_LONG                     = 0x00a0;
+    int OP_OR_LONG                      = 0x00a1;
+    int OP_XOR_LONG                     = 0x00a2;
+    int OP_SHL_LONG                     = 0x00a3;
+    int OP_SHR_LONG                     = 0x00a4;
+    int OP_USHR_LONG                    = 0x00a5;
+    int OP_ADD_FLOAT                    = 0x00a6;
+    int OP_SUB_FLOAT                    = 0x00a7;
+    int OP_MUL_FLOAT                    = 0x00a8;
+    int OP_DIV_FLOAT                    = 0x00a9;
+    int OP_REM_FLOAT                    = 0x00aa;
+    int OP_ADD_DOUBLE                   = 0x00ab;
+    int OP_SUB_DOUBLE                   = 0x00ac;
+    int OP_MUL_DOUBLE                   = 0x00ad;
+    int OP_DIV_DOUBLE                   = 0x00ae;
+    int OP_REM_DOUBLE                   = 0x00af;
+    int OP_ADD_INT_2ADDR                = 0x00b0;
+    int OP_SUB_INT_2ADDR                = 0x00b1;
+    int OP_MUL_INT_2ADDR                = 0x00b2;
+    int OP_DIV_INT_2ADDR                = 0x00b3;
+    int OP_REM_INT_2ADDR                = 0x00b4;
+    int OP_AND_INT_2ADDR                = 0x00b5;
+    int OP_OR_INT_2ADDR                 = 0x00b6;
+    int OP_XOR_INT_2ADDR                = 0x00b7;
+    int OP_SHL_INT_2ADDR                = 0x00b8;
+    int OP_SHR_INT_2ADDR                = 0x00b9;
+    int OP_USHR_INT_2ADDR               = 0x00ba;
+    int OP_ADD_LONG_2ADDR               = 0x00bb;
+    int OP_SUB_LONG_2ADDR               = 0x00bc;
+    int OP_MUL_LONG_2ADDR               = 0x00bd;
+    int OP_DIV_LONG_2ADDR               = 0x00be;
+    int OP_REM_LONG_2ADDR               = 0x00bf;
+    int OP_AND_LONG_2ADDR               = 0x00c0;
+    int OP_OR_LONG_2ADDR                = 0x00c1;
+    int OP_XOR_LONG_2ADDR               = 0x00c2;
+    int OP_SHL_LONG_2ADDR               = 0x00c3;
+    int OP_SHR_LONG_2ADDR               = 0x00c4;
+    int OP_USHR_LONG_2ADDR              = 0x00c5;
+    int OP_ADD_FLOAT_2ADDR              = 0x00c6;
+    int OP_SUB_FLOAT_2ADDR              = 0x00c7;
+    int OP_MUL_FLOAT_2ADDR              = 0x00c8;
+    int OP_DIV_FLOAT_2ADDR              = 0x00c9;
+    int OP_REM_FLOAT_2ADDR              = 0x00ca;
+    int OP_ADD_DOUBLE_2ADDR             = 0x00cb;
+    int OP_SUB_DOUBLE_2ADDR             = 0x00cc;
+    int OP_MUL_DOUBLE_2ADDR             = 0x00cd;
+    int OP_DIV_DOUBLE_2ADDR             = 0x00ce;
+    int OP_REM_DOUBLE_2ADDR             = 0x00cf;
+    int OP_ADD_INT_LIT16                = 0x00d0;
+    int OP_RSUB_INT                     = 0x00d1;
+    int OP_MUL_INT_LIT16                = 0x00d2;
+    int OP_DIV_INT_LIT16                = 0x00d3;
+    int OP_REM_INT_LIT16                = 0x00d4;
+    int OP_AND_INT_LIT16                = 0x00d5;
+    int OP_OR_INT_LIT16                 = 0x00d6;
+    int OP_XOR_INT_LIT16                = 0x00d7;
+    int OP_ADD_INT_LIT8                 = 0x00d8;
+    int OP_RSUB_INT_LIT8                = 0x00d9;
+    int OP_MUL_INT_LIT8                 = 0x00da;
+    int OP_DIV_INT_LIT8                 = 0x00db;
+    int OP_REM_INT_LIT8                 = 0x00dc;
+    int OP_AND_INT_LIT8                 = 0x00dd;
+    int OP_OR_INT_LIT8                  = 0x00de;
+    int OP_XOR_INT_LIT8                 = 0x00df;
+    int OP_SHL_INT_LIT8                 = 0x00e0;
+    int OP_SHR_INT_LIT8                 = 0x00e1;
+    int OP_USHR_INT_LIT8                = 0x00e2;
+    // END(libcore-opcodes)
+
+    /** Never implemented; do not use. */
+    int OP_CONST_CLASS_JUMBO            = 0x00ff;
+    /** Never implemented; do not use. */
+    int OP_CHECK_CAST_JUMBO             = 0x01ff;
+    /** Never implemented; do not use. */
+    int OP_INSTANCE_OF_JUMBO            = 0x02ff;
+    /** Never implemented; do not use. */
+    int OP_NEW_INSTANCE_JUMBO           = 0x03ff;
+    /** Never implemented; do not use. */
+    int OP_NEW_ARRAY_JUMBO              = 0x04ff;
+    /** Never implemented; do not use. */
+    int OP_FILLED_NEW_ARRAY_JUMBO       = 0x05ff;
+    /** Never implemented; do not use. */
+    int OP_IGET_JUMBO                   = 0x06ff;
+    /** Never implemented; do not use. */
+    int OP_IGET_WIDE_JUMBO              = 0x07ff;
+    /** Never implemented; do not use. */
+    int OP_IGET_OBJECT_JUMBO            = 0x08ff;
+    /** Never implemented; do not use. */
+    int OP_IGET_BOOLEAN_JUMBO           = 0x09ff;
+    /** Never implemented; do not use. */
+    int OP_IGET_BYTE_JUMBO              = 0x0aff;
+    /** Never implemented; do not use. */
+    int OP_IGET_CHAR_JUMBO              = 0x0bff;
+    /** Never implemented; do not use. */
+    int OP_IGET_SHORT_JUMBO             = 0x0cff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_JUMBO                   = 0x0dff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_WIDE_JUMBO              = 0x0eff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_OBJECT_JUMBO            = 0x0fff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_BOOLEAN_JUMBO           = 0x10ff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_BYTE_JUMBO              = 0x11ff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_CHAR_JUMBO              = 0x12ff;
+    /** Never implemented; do not use. */
+    int OP_IPUT_SHORT_JUMBO             = 0x13ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_JUMBO                   = 0x14ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_WIDE_JUMBO              = 0x15ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_OBJECT_JUMBO            = 0x16ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_BOOLEAN_JUMBO           = 0x17ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_BYTE_JUMBO              = 0x18ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_CHAR_JUMBO              = 0x19ff;
+    /** Never implemented; do not use. */
+    int OP_SGET_SHORT_JUMBO             = 0x1aff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_JUMBO                   = 0x1bff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_WIDE_JUMBO              = 0x1cff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_OBJECT_JUMBO            = 0x1dff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_BOOLEAN_JUMBO           = 0x1eff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_BYTE_JUMBO              = 0x1fff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_CHAR_JUMBO              = 0x20ff;
+    /** Never implemented; do not use. */
+    int OP_SPUT_SHORT_JUMBO             = 0x21ff;
+    /** Never implemented; do not use. */
+    int OP_INVOKE_VIRTUAL_JUMBO         = 0x22ff;
+    /** Never implemented; do not use. */
+    int OP_INVOKE_SUPER_JUMBO           = 0x23ff;
+    /** Never implemented; do not use. */
+    int OP_INVOKE_DIRECT_JUMBO          = 0x24ff;
+    /** Never implemented; do not use. */
+    int OP_INVOKE_STATIC_JUMBO          = 0x25ff;
+    /** Never implemented; do not use. */
+    int OP_INVOKE_INTERFACE_JUMBO       = 0x26ff;
+
+    /*
+     * The rest of these are either generated by dexopt for optimized
+     * code, or inserted by the VM at runtime.  They are never generated
+     * by "dx".
+     *
+     * They are all deprecated and will be removed in a future
+     * release, since these declarations are really of private implementation
+     * details that are subject to change.
+     */
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_IGET_WIDE_VOLATILE           = 0xe8;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_IPUT_WIDE_VOLATILE           = 0xe9;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_SGET_WIDE_VOLATILE           = 0xea;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_SPUT_WIDE_VOLATILE           = 0xeb;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_BREAKPOINT                   = 0xec;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_THROW_VERIFICATION_ERROR     = 0xed;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_EXECUTE_INLINE               = 0xee;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_EXECUTE_INLINE_RANGE         = 0xef;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_INVOKE_DIRECT_EMPTY          = 0xf0;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_IGET_QUICK                   = 0xf2;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_IGET_WIDE_QUICK              = 0xf3;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_IGET_OBJECT_QUICK            = 0xf4;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_IPUT_QUICK                   = 0xf5;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_IPUT_WIDE_QUICK              = 0xf6;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_IPUT_OBJECT_QUICK            = 0xf7;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_INVOKE_VIRTUAL_QUICK         = 0xf8;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_INVOKE_VIRTUAL_QUICK_RANGE   = 0xf9;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_INVOKE_SUPER_QUICK           = 0xfa;
+
+    /**
+     * Implementation detail.
+     * @deprecated Implementation detail.
+     */
+    @Deprecated int OP_INVOKE_SUPER_QUICK_RANGE     = 0xfb;
+}
diff --git a/dalvik/src/main/java/dalvik/system/AllocationLimitError.java b/dalvik/src/main/java/dalvik/system/AllocationLimitError.java
new file mode 100644
index 0000000..b3f8947
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/AllocationLimitError.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package dalvik.system;
+
+/**
+ * Is thrown when an allocation limit is exceeded.
+ *
+ * @hide
+ */
+public class AllocationLimitError extends VirtualMachineError {
+    /**
+     * Creates a new exception instance and initializes it with default values.
+     */
+    public AllocationLimitError() {
+        super();
+    }
+
+    /**
+     * Creates a new exception instance and initializes it with a given message.
+     *
+     * @param detailMessage the error message
+     */
+    public AllocationLimitError(String detailMessage) {
+        super(detailMessage);
+    }
+}
+
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
new file mode 100644
index 0000000..d3ec95a
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Enumeration;
+
+/**
+ * Base class for common functionality between various dex-based
+ * {@link ClassLoader} implementations.
+ */
+public class BaseDexClassLoader extends ClassLoader {
+    private final DexPathList pathList;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param dexPath the list of jar/apk files containing classes and
+     * resources, delimited by {@code File.pathSeparator}, which
+     * defaults to {@code ":"} on Android
+     * @param optimizedDirectory directory where optimized dex files
+     * should be written; may be {@code null}
+     * @param libraryPath the list of directories containing native
+     * libraries, delimited by {@code File.pathSeparator}; may be
+     * {@code null}
+     * @param parent the parent class loader
+     */
+    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
+            String libraryPath, ClassLoader parent) {
+        super(parent);
+        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        Class c = pathList.findClass(name);
+        if (c == null) {
+            throw new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
+        }
+        return c;
+    }
+
+    @Override
+    protected URL findResource(String name) {
+        return pathList.findResource(name);
+    }
+
+    @Override
+    protected Enumeration<URL> findResources(String name) {
+        return pathList.findResources(name);
+    }
+
+    @Override
+    public String findLibrary(String name) {
+        return pathList.findLibrary(name);
+    }
+
+    /**
+     * Returns package information for the given package.
+     * Unfortunately, instances of this class don't really have this
+     * information, and as a non-secure {@code ClassLoader}, it isn't
+     * even required to, according to the spec. Yet, we want to
+     * provide it, in order to make all those hopeful callers of
+     * {@code myClass.getPackage().getName()} happy. Thus we construct
+     * a {@code Package} object the first time it is being requested
+     * and fill most of the fields with dummy values. The {@code
+     * Package} object is then put into the {@code ClassLoader}'s
+     * package cache, so we see the same one next time. We don't
+     * create {@code Package} objects for {@code null} arguments or
+     * for the default package.
+     *
+     * <p>There is a limited chance that we end up with multiple
+     * {@code Package} objects representing the same package: It can
+     * happen when when a package is scattered across different JAR
+     * files which were loaded by different {@code ClassLoader}
+     * instances. This is rather unlikely, and given that this whole
+     * thing is more or less a workaround, probably not worth the
+     * effort to address.
+     *
+     * @param name the name of the class
+     * @return the package information for the class, or {@code null}
+     * if there is no package information available for it
+     */
+    @Override
+    protected synchronized Package getPackage(String name) {
+        if (name != null && !name.isEmpty()) {
+            Package pack = super.getPackage(name);
+
+            if (pack == null) {
+                pack = definePackage(name, "Unknown", "0.0", "Unknown",
+                        "Unknown", "0.0", "Unknown", null);
+            }
+
+            return pack;
+        }
+
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    public String getLdLibraryPath() {
+        StringBuilder result = new StringBuilder();
+        for (File directory : pathList.getNativeLibraryDirectories()) {
+            if (result.length() > 0) {
+                result.append(':');
+            }
+            result.append(directory);
+        }
+        return result.toString();
+    }
+
+    @Override public String toString() {
+        return getClass().getName() + "[" + pathList + "]";
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/BlockGuard.java b/dalvik/src/main/java/dalvik/system/BlockGuard.java
new file mode 100644
index 0000000..d61f0e1
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/BlockGuard.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.SocketException;
+import java.nio.charset.Charsets;
+
+/**
+ * Mechanism to let threads set restrictions on what code is allowed
+ * to do in their thread.
+ *
+ * <p>This is meant for applications to prevent certain blocking
+ * operations from running on their main event loop (or "UI") threads.
+ *
+ * <p>Note that this is all best-effort to catch most accidental mistakes
+ * and isn't intended to be a perfect mechanism, nor provide any sort of
+ * security.
+ *
+ * @hide
+ */
+public final class BlockGuard {
+
+    // TODO: refactor class name to something more generic, since its scope is
+    // growing beyond just blocking/logging.
+
+    public static final int DISALLOW_DISK_WRITE = 0x01;
+    public static final int DISALLOW_DISK_READ = 0x02;
+    public static final int DISALLOW_NETWORK = 0x04;
+    public static final int PASS_RESTRICTIONS_VIA_RPC = 0x08;
+    public static final int PENALTY_LOG = 0x10;
+    public static final int PENALTY_DIALOG = 0x20;
+    public static final int PENALTY_DEATH = 0x40;
+
+    public interface Policy {
+        /**
+         * Called on disk writes.
+         */
+        void onWriteToDisk();
+
+        /**
+         * Called on disk reads.
+         */
+        void onReadFromDisk();
+
+        /**
+         * Called on network operations.
+         */
+        void onNetwork();
+
+        /**
+         * Returns the policy bitmask, for shipping over Binder calls
+         * to remote threads/processes and reinstantiating the policy
+         * there.  The bits in the mask are from the DISALLOW_* and
+         * PENALTY_* constants.
+         */
+        int getPolicyMask();
+    }
+
+    public static class BlockGuardPolicyException extends RuntimeException {
+        // bitmask of DISALLOW_*, PENALTY_*, etc flags
+        private final int mPolicyState;
+        private final int mPolicyViolated;
+        private final String mMessage;   // may be null
+
+        public BlockGuardPolicyException(int policyState, int policyViolated) {
+            this(policyState, policyViolated, null);
+        }
+
+        public BlockGuardPolicyException(int policyState, int policyViolated, String message) {
+            mPolicyState = policyState;
+            mPolicyViolated = policyViolated;
+            mMessage = message;
+            fillInStackTrace();
+        }
+
+        public int getPolicy() {
+            return mPolicyState;
+        }
+
+        public int getPolicyViolation() {
+            return mPolicyViolated;
+        }
+
+        public String getMessage() {
+            // Note: do not change this format casually.  It's
+            // somewhat unfortunately Parceled and passed around
+            // Binder calls and parsed back into an Exception by
+            // Android's StrictMode.  This was the least invasive
+            // option and avoided a gross mix of Java Serialization
+            // combined with Parcels.
+            return "policy=" + mPolicyState + " violation=" + mPolicyViolated +
+                    (mMessage == null ? "" : (" msg=" + mMessage));
+        }
+    }
+
+    /**
+     * The default, permissive policy that doesn't prevent any operations.
+     */
+    public static final Policy LAX_POLICY = new Policy() {
+            public void onWriteToDisk() {}
+            public void onReadFromDisk() {}
+            public void onNetwork() {}
+            public int getPolicyMask() {
+                return 0;
+            }
+        };
+
+    private static ThreadLocal<Policy> threadPolicy = new ThreadLocal<Policy>() {
+        @Override protected Policy initialValue() {
+            return LAX_POLICY;
+        }
+    };
+
+    /**
+     * Get the current thread's policy.
+     *
+     * @return the current thread's policy.  Never returns null.
+     *     Will return the LAX_POLICY instance if nothing else is set.
+     */
+    public static Policy getThreadPolicy() {
+        return threadPolicy.get();
+    }
+
+    /**
+     * Sets the current thread's block guard policy.
+     *
+     * @param policy policy to set.  May not be null.  Use the public LAX_POLICY
+     *   if you want to unset the active policy.
+     */
+    public static void setThreadPolicy(Policy policy) {
+        if (policy == null) {
+            throw new NullPointerException("policy == null");
+        }
+        threadPolicy.set(policy);
+    }
+
+    private BlockGuard() {}
+}
diff --git a/dalvik/src/main/java/dalvik/system/CloseGuard.java b/dalvik/src/main/java/dalvik/system/CloseGuard.java
new file mode 100644
index 0000000..df36867
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/CloseGuard.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+/**
+ * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
+ * resources that should have been cleaned up by explicit close
+ * methods (aka "explicit termination methods" in Effective Java).
+ * <p>
+ * A simple example: <pre>   {@code
+ *   class Foo {
+ *
+ *       private final CloseGuard guard = CloseGuard.get();
+ *
+ *       ...
+ *
+ *       public Foo() {
+ *           ...;
+ *           guard.open("cleanup");
+ *       }
+ *
+ *       public void cleanup() {
+ *          guard.close();
+ *          ...;
+ *       }
+ *
+ *       protected void finalize() throws Throwable {
+ *           try {
+ *               if (guard != null) {
+ *                   guard.warnIfOpen();
+ *               }
+ *               cleanup();
+ *           } finally {
+ *               super.finalize();
+ *           }
+ *       }
+ *   }
+ * }</pre>
+ *
+ * In usage where the resource to be explicitly cleaned up are
+ * allocated after object construction, CloseGuard protection can
+ * be deferred. For example: <pre>   {@code
+ *   class Bar {
+ *
+ *       private final CloseGuard guard = CloseGuard.get();
+ *
+ *       ...
+ *
+ *       public Bar() {
+ *           ...;
+ *       }
+ *
+ *       public void connect() {
+ *          ...;
+ *          guard.open("cleanup");
+ *       }
+ *
+ *       public void cleanup() {
+ *          guard.close();
+ *          ...;
+ *       }
+ *
+ *       protected void finalize() throws Throwable {
+ *           try {
+ *               if (guard != null) {
+ *                   guard.warnIfOpen();
+ *               }
+ *               cleanup();
+ *           } finally {
+ *               super.finalize();
+ *           }
+ *       }
+ *   }
+ * }</pre>
+ *
+ * When used in a constructor calls to {@code open} should occur at
+ * the end of the constructor since an exception that would cause
+ * abrupt termination of the constructor will mean that the user will
+ * not have a reference to the object to cleanup explicitly. When used
+ * in a method, the call to {@code open} should occur just after
+ * resource acquisition.
+ *
+ * <p>
+ *
+ * Note that the null check on {@code guard} in the finalizer is to
+ * cover cases where a constructor throws an exception causing the
+ * {@code guard} to be uninitialized.
+ *
+ * @hide
+ */
+public final class CloseGuard {
+
+    /**
+     * Instance used when CloseGuard is disabled to avoid allocation.
+     */
+    private static final CloseGuard NOOP = new CloseGuard();
+
+    /**
+     * Enabled by default so we can catch issues early in VM startup.
+     * Note, however, that Android disables this early in its startup,
+     * but enables it with DropBoxing for system apps on debug builds.
+     */
+    private static volatile boolean ENABLED = true;
+
+    /**
+     * Hook for customizing how CloseGuard issues are reported.
+     */
+    private static volatile Reporter REPORTER = new DefaultReporter();
+
+    /**
+     * Returns a CloseGuard instance. If CloseGuard is enabled, {@code
+     * #open(String)} can be used to set up the instance to warn on
+     * failure to close. If CloseGuard is disabled, a non-null no-op
+     * instance is returned.
+     */
+    public static CloseGuard get() {
+        if (!ENABLED) {
+            return NOOP;
+        }
+        return new CloseGuard();
+    }
+
+    /**
+     * Used to enable or disable CloseGuard. Note that CloseGuard only
+     * warns if it is enabled for both allocation and finalization.
+     */
+    public static void setEnabled(boolean enabled) {
+        ENABLED = enabled;
+    }
+
+    /**
+     * Used to replace default Reporter used to warn of CloseGuard
+     * violations. Must be non-null.
+     */
+    public static void setReporter(Reporter reporter) {
+        if (reporter == null) {
+            throw new NullPointerException("reporter == null");
+        }
+        REPORTER = reporter;
+    }
+
+    /**
+     * Returns non-null CloseGuard.Reporter.
+     */
+    public static Reporter getReporter() {
+        return REPORTER;
+    }
+
+    private CloseGuard() {}
+
+    /**
+     * If CloseGuard is enabled, {@code open} initializes the instance
+     * with a warning that the caller should have explicitly called the
+     * {@code closer} method instead of relying on finalization.
+     *
+     * @param closer non-null name of explicit termination method
+     * @throws NullPointerException if closer is null, regardless of
+     * whether or not CloseGuard is enabled
+     */
+    public void open(String closer) {
+        // always perform the check for valid API usage...
+        if (closer == null) {
+            throw new NullPointerException("closer == null");
+        }
+        // ...but avoid allocating an allocationSite if disabled
+        if (this == NOOP || !ENABLED) {
+            return;
+        }
+        String message = "Explicit termination method '" + closer + "' not called";
+        allocationSite = new Throwable(message);
+    }
+
+    private Throwable allocationSite;
+
+    /**
+     * Marks this CloseGuard instance as closed to avoid warnings on
+     * finalization.
+     */
+    public void close() {
+        allocationSite = null;
+    }
+
+    /**
+     * If CloseGuard is enabled, logs a warning if the caller did not
+     * properly cleanup by calling an explicit close method
+     * before finalization. If CloseGuard is disabled, no action is
+     * performed.
+     */
+    public void warnIfOpen() {
+        if (allocationSite == null || !ENABLED) {
+            return;
+        }
+
+        String message =
+                ("A resource was acquired at attached stack trace but never released. "
+                 + "See java.io.Closeable for information on avoiding resource leaks.");
+
+        REPORTER.report(message, allocationSite);
+    }
+
+    /**
+     * Interface to allow customization of reporting behavior.
+     */
+    public static interface Reporter {
+        public void report (String message, Throwable allocationSite);
+    }
+
+    /**
+     * Default Reporter which reports CloseGuard violations to the log.
+     */
+    private static final class DefaultReporter implements Reporter {
+        @Override public void report (String message, Throwable allocationSite) {
+            System.logW(message, allocationSite);
+        }
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/DalvikLogHandler.java b/dalvik/src/main/java/dalvik/system/DalvikLogHandler.java
new file mode 100644
index 0000000..a62ca3b
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/DalvikLogHandler.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * An optimized handler for efficient publishing of basic log messages.
+ * Implementers should also be subclasses of {@link java.util.logging.Handler}.
+ *
+ * <p>Unlike the default log handler, this API doesn't require intermediate
+ * objects to be allocated for log handling. It also includes a short tag, which
+ * may otherwise need to be calculated for each published message.
+ *
+ * @hide
+ */
+public interface DalvikLogHandler {
+
+    /**
+     * Publishes a log message. Unlike {@link
+     * java.util.logging.Handler#publish(java.util.logging.LogRecord)}, this
+     * method includes only the raw log message. Log messages that were created
+     * with additional fields (parameters, source methods, etc.) will flow
+     * through the conventional channels instead.
+     *
+     * @param tag the short (23 characters or fewer) logger tag identifying
+     *      {@code logger}.
+     */
+    void publish(Logger source, String tag, Level level, String message);
+
+    // TODO: support messages with throwables?
+}
\ No newline at end of file
diff --git a/dalvik/src/main/java/dalvik/system/DalvikLogging.java b/dalvik/src/main/java/dalvik/system/DalvikLogging.java
new file mode 100644
index 0000000..b5c7ad5
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/DalvikLogging.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+/**
+ * Utility methods for logging to {@code DalvikHandlers}.
+ *
+ * @hide
+ */
+public final class DalvikLogging {
+    private DalvikLogging() {}
+
+    /**
+     * Returns the short logger tag (up to 23 chars) for the given logger name.
+     * Traditionally loggers are named by fully-qualified Java classes; this
+     * method attempts to return a concise identifying part of such names.
+     */
+    public static String loggerNameToTag(String loggerName) {
+        // Anonymous logger.
+        if (loggerName == null) {
+            return "null";
+        }
+
+        int length = loggerName.length();
+        if (length <= 23) {
+            return loggerName;
+        }
+
+        int lastPeriod = loggerName.lastIndexOf(".");
+        return length - (lastPeriod + 1) <= 23
+                ? loggerName.substring(lastPeriod + 1)
+                : loggerName.substring(loggerName.length() - 23);
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/DexClassLoader.java b/dalvik/src/main/java/dalvik/system/DexClassLoader.java
new file mode 100644
index 0000000..ac2a70a
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/DexClassLoader.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package dalvik.system;
+
+import java.io.File;
+
+/**
+ * A class loader that loads classes from {@code .jar} and {@code .apk} files
+ * containing a {@code classes.dex} entry. This can be used to execute code not
+ * installed as part of an application.
+ *
+ * <p>This class loader requires an application-private, writable directory to
+ * cache optimized classes. Use {@code Context.getDir(String, int)} to create
+ * such a directory: <pre>   {@code
+ *   File dexOutputDir = context.getDir("dex", 0);
+ * }</pre>
+ *
+ * <p><strong>Do not cache optimized classes on external storage.</strong>
+ * External storage does not provide access controls necessary to protect your
+ * application from code injection attacks.
+ */
+public class DexClassLoader extends BaseDexClassLoader {
+    /**
+     * Creates a {@code DexClassLoader} that finds interpreted and native
+     * code.  Interpreted classes are found in a set of DEX files contained
+     * in Jar or APK files.
+     *
+     * <p>The path lists are separated using the character specified by the
+     * {@code path.separator} system property, which defaults to {@code :}.
+     *
+     * @param dexPath the list of jar/apk files containing classes and
+     *     resources, delimited by {@code File.pathSeparator}, which
+     *     defaults to {@code ":"} on Android
+     * @param optimizedDirectory directory where optimized dex files
+     *     should be written; must not be {@code null}
+     * @param libraryPath the list of directories containing native
+     *     libraries, delimited by {@code File.pathSeparator}; may be
+     *     {@code null}
+     * @param parent the parent class loader
+     */
+    public DexClassLoader(String dexPath, String optimizedDirectory,
+            String libraryPath, ClassLoader parent) {
+        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
new file mode 100644
index 0000000..8db3985
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Enumeration;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.StructStat;
+
+/**
+ * Manipulates DEX files. The class is similar in principle to
+ * {@link java.util.zip.ZipFile}. It is used primarily by class loaders.
+ * <p>
+ * Note we don't directly open and read the DEX file here. They're memory-mapped
+ * read-only by the VM.
+ */
+public final class DexFile {
+    private int mCookie;
+    private final String mFileName;
+    private final CloseGuard guard = CloseGuard.get();
+
+    /**
+     * Opens a DEX file from a given File object. This will usually be a ZIP/JAR
+     * file with a "classes.dex" inside.
+     *
+     * The VM will generate the name of the corresponding file in
+     * /data/dalvik-cache and open it, possibly creating or updating
+     * it first if system permissions allow.  Don't pass in the name of
+     * a file in /data/dalvik-cache, as the named file is expected to be
+     * in its original (pre-dexopt) state.
+     *
+     * @param file
+     *            the File object referencing the actual DEX file
+     *
+     * @throws IOException
+     *             if an I/O error occurs, such as the file not being found or
+     *             access rights missing for opening it
+     */
+    public DexFile(File file) throws IOException {
+        this(file.getPath());
+    }
+
+    /**
+     * Opens a DEX file from a given filename. This will usually be a ZIP/JAR
+     * file with a "classes.dex" inside.
+     *
+     * The VM will generate the name of the corresponding file in
+     * /data/dalvik-cache and open it, possibly creating or updating
+     * it first if system permissions allow.  Don't pass in the name of
+     * a file in /data/dalvik-cache, as the named file is expected to be
+     * in its original (pre-dexopt) state.
+     *
+     * @param fileName
+     *            the filename of the DEX file
+     *
+     * @throws IOException
+     *             if an I/O error occurs, such as the file not being found or
+     *             access rights missing for opening it
+     */
+    public DexFile(String fileName) throws IOException {
+        mCookie = openDexFile(fileName, null, 0);
+        mFileName = fileName;
+        guard.open("close");
+        //System.out.println("DEX FILE cookie is " + mCookie);
+    }
+
+    /**
+     * Opens a DEX file from a given filename, using a specified file
+     * to hold the optimized data.
+     *
+     * @param sourceName
+     *  Jar or APK file with "classes.dex".
+     * @param outputName
+     *  File that will hold the optimized form of the DEX data.
+     * @param flags
+     *  Enable optional features.
+     */
+    private DexFile(String sourceName, String outputName, int flags) throws IOException {
+        if (outputName != null) {
+            try {
+                String parent = new File(outputName).getParent();
+                if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
+                    throw new IllegalArgumentException("Optimized data directory " + parent
+                            + " is not owned by the current user. Shared storage cannot protect"
+                            + " your application from code injection attacks.");
+                }
+            } catch (ErrnoException ignored) {
+                // assume we'll fail with a more contextual error later
+            }
+        }
+
+        mCookie = openDexFile(sourceName, outputName, flags);
+        mFileName = sourceName;
+        guard.open("close");
+        //System.out.println("DEX FILE cookie is " + mCookie);
+    }
+
+    /**
+     * Open a DEX file, specifying the file in which the optimized DEX
+     * data should be written.  If the optimized form exists and appears
+     * to be current, it will be used; if not, the VM will attempt to
+     * regenerate it.
+     *
+     * This is intended for use by applications that wish to download
+     * and execute DEX files outside the usual application installation
+     * mechanism.  This function should not be called directly by an
+     * application; instead, use a class loader such as
+     * dalvik.system.DexClassLoader.
+     *
+     * @param sourcePathName
+     *  Jar or APK file with "classes.dex".  (May expand this to include
+     *  "raw DEX" in the future.)
+     * @param outputPathName
+     *  File that will hold the optimized form of the DEX data.
+     * @param flags
+     *  Enable optional features.  (Currently none defined.)
+     * @return
+     *  A new or previously-opened DexFile.
+     * @throws IOException
+     *  If unable to open the source or output file.
+     */
+    static public DexFile loadDex(String sourcePathName, String outputPathName,
+        int flags) throws IOException {
+
+        /*
+         * TODO: we may want to cache previously-opened DexFile objects.
+         * The cache would be synchronized with close().  This would help
+         * us avoid mapping the same DEX more than once when an app
+         * decided to open it multiple times.  In practice this may not
+         * be a real issue.
+         */
+        return new DexFile(sourcePathName, outputPathName, flags);
+    }
+
+    /**
+     * Gets the name of the (already opened) DEX file.
+     *
+     * @return the file name
+     */
+    public String getName() {
+        return mFileName;
+    }
+
+    /**
+     * Closes the DEX file.
+     * <p>
+     * This may not be able to release any resources. If classes from this
+     * DEX file are still resident, the DEX file can't be unmapped.
+     *
+     * @throws IOException
+     *             if an I/O error occurs during closing the file, which
+     *             normally should not happen
+     */
+    public void close() throws IOException {
+        guard.close();
+        closeDexFile(mCookie);
+        mCookie = 0;
+    }
+
+    /**
+     * Loads a class. Returns the class on success, or a {@code null} reference
+     * on failure.
+     * <p>
+     * If you are not calling this from a class loader, this is most likely not
+     * going to do what you want. Use {@link Class#forName(String)} instead.
+     * <p>
+     * The method does not throw {@link ClassNotFoundException} if the class
+     * isn't found because it isn't reasonable to throw exceptions wildly every
+     * time a class is not found in the first DEX file we look at.
+     *
+     * @param name
+     *            the class name, which should look like "java/lang/String"
+     *
+     * @param loader
+     *            the class loader that tries to load the class (in most cases
+     *            the caller of the method
+     *
+     * @return the {@link Class} object representing the class, or {@code null}
+     *         if the class cannot be loaded
+     */
+    public Class loadClass(String name, ClassLoader loader) {
+        String slashName = name.replace('.', '/');
+        return loadClassBinaryName(slashName, loader);
+    }
+
+    /**
+     * See {@link #loadClass(String, ClassLoader)}.
+     *
+     * This takes a "binary" class name to better match ClassLoader semantics.
+     *
+     * @hide
+     */
+    public Class loadClassBinaryName(String name, ClassLoader loader) {
+        return defineClass(name, loader, mCookie);
+    }
+
+    private native static Class defineClass(String name, ClassLoader loader, int cookie);
+
+    /**
+     * Enumerate the names of the classes in this DEX file.
+     *
+     * @return an enumeration of names of classes contained in the DEX file, in
+     *         the usual internal form (like "java/lang/String").
+     */
+    public Enumeration<String> entries() {
+        return new DFEnum(this);
+    }
+
+    /*
+     * Helper class.
+     */
+    private class DFEnum implements Enumeration<String> {
+        private int mIndex;
+        private String[] mNameList;
+
+        DFEnum(DexFile df) {
+            mIndex = 0;
+            mNameList = getClassNameList(mCookie);
+        }
+
+        public boolean hasMoreElements() {
+            return (mIndex < mNameList.length);
+        }
+
+        public String nextElement() {
+            return mNameList[mIndex++];
+        }
+    }
+
+    /* return a String array with class names */
+    native private static String[] getClassNameList(int cookie);
+
+    /**
+     * Called when the class is finalized. Makes sure the DEX file is closed.
+     *
+     * @throws IOException
+     *             if an I/O error occurs during closing the file, which
+     *             normally should not happen
+     */
+    @Override protected void finalize() throws Throwable {
+        try {
+            if (guard != null) {
+                guard.warnIfOpen();
+            }
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /*
+     * Open a DEX file.  The value returned is a magic VM cookie.  On
+     * failure, an IOException is thrown.
+     */
+    native private static int openDexFile(String sourceName, String outputName,
+        int flags) throws IOException;
+
+    /*
+     * Open a DEX file based on a {@code byte[]}. The value returned
+     * is a magic VM cookie. On failure, a RuntimeException is thrown.
+     */
+    native private static int openDexFile(byte[] fileContents);
+
+    /*
+     * Close DEX file.
+     */
+    native private static void closeDexFile(int cookie);
+
+    /**
+     * Returns true if the VM believes that the apk/jar file is out of date
+     * and should be passed through "dexopt" again.
+     *
+     * @param fileName the absolute path to the apk/jar file to examine.
+     * @return true if dexopt should be called on the file, false otherwise.
+     * @throws java.io.FileNotFoundException if fileName is not readable,
+     *         not a file, or not present.
+     * @throws java.io.IOException if fileName is not a valid apk/jar file or
+     *         if problems occur while parsing it.
+     * @throws java.lang.NullPointerException if fileName is null.
+     * @throws dalvik.system.StaleDexCacheError if the optimized dex file
+     *         is stale but exists on a read-only partition.
+     */
+    native public static boolean isDexOptNeeded(String fileName)
+            throws FileNotFoundException, IOException;
+}
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
new file mode 100644
index 0000000..3d9ee3e
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.regex.Pattern;
+import java.util.zip.ZipFile;
+import libcore.io.ErrnoException;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+import libcore.io.StructStat;
+import static libcore.io.OsConstants.*;
+
+/**
+ * A pair of lists of entries, associated with a {@code ClassLoader}.
+ * One of the lists is a dex/resource path &mdash; typically referred
+ * to as a "class path" &mdash; list, and the other names directories
+ * containing native code libraries. Class path entries may be any of:
+ * a {@code .jar} or {@code .zip} file containing an optional
+ * top-level {@code classes.dex} file as well as arbitrary resources,
+ * or a plain {@code .dex} file (with no possibility of associated
+ * resources).
+ *
+ * <p>This class also contains methods to use these lists to look up
+ * classes and resources.</p>
+ */
+/*package*/ final class DexPathList {
+    private static final String DEX_SUFFIX = ".dex";
+    private static final String JAR_SUFFIX = ".jar";
+    private static final String ZIP_SUFFIX = ".zip";
+    private static final String APK_SUFFIX = ".apk";
+
+    /** class definition context */
+    private final ClassLoader definingContext;
+
+    /**
+     * List of dex/resource (class path) elements.
+     * Should be called pathElements, but the Facebook app uses reflection
+     * to modify 'dexElements' (http://b/7726934).
+     */
+    private final Element[] dexElements;
+
+    /** List of native library directories. */
+    private final File[] nativeLibraryDirectories;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param definingContext the context in which any as-yet unresolved
+     * classes should be defined
+     * @param dexPath list of dex/resource path elements, separated by
+     * {@code File.pathSeparator}
+     * @param libraryPath list of native library directory path elements,
+     * separated by {@code File.pathSeparator}
+     * @param optimizedDirectory directory where optimized {@code .dex} files
+     * should be found and written to, or {@code null} to use the default
+     * system directory for same
+     */
+    public DexPathList(ClassLoader definingContext, String dexPath,
+            String libraryPath, File optimizedDirectory) {
+        if (definingContext == null) {
+            throw new NullPointerException("definingContext == null");
+        }
+
+        if (dexPath == null) {
+            throw new NullPointerException("dexPath == null");
+        }
+
+        if (optimizedDirectory != null) {
+            if (!optimizedDirectory.exists())  {
+                throw new IllegalArgumentException(
+                        "optimizedDirectory doesn't exist: "
+                        + optimizedDirectory);
+            }
+
+            if (!(optimizedDirectory.canRead()
+                            && optimizedDirectory.canWrite())) {
+                throw new IllegalArgumentException(
+                        "optimizedDirectory not readable/writable: "
+                        + optimizedDirectory);
+            }
+        }
+
+        this.definingContext = definingContext;
+        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory);
+        this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
+    }
+
+    @Override public String toString() {
+        return "DexPathList[" + Arrays.toString(dexElements) +
+            ",nativeLibraryDirectories=" + Arrays.toString(nativeLibraryDirectories) + "]";
+    }
+
+    /**
+     * For BaseDexClassLoader.getLdLibraryPath.
+     */
+    public File[] getNativeLibraryDirectories() {
+        return nativeLibraryDirectories;
+    }
+
+    /**
+     * Splits the given dex path string into elements using the path
+     * separator, pruning out any elements that do not refer to existing
+     * and readable files. (That is, directories are not included in the
+     * result.)
+     */
+    private static ArrayList<File> splitDexPath(String path) {
+        return splitPaths(path, null, false);
+    }
+
+    /**
+     * Splits the given library directory path string into elements
+     * using the path separator ({@code File.pathSeparator}, which
+     * defaults to {@code ":"} on Android, appending on the elements
+     * from the system library path, and pruning out any elements that
+     * do not refer to existing and readable directories.
+     */
+    private static File[] splitLibraryPath(String path) {
+        /*
+         * Native libraries may exist in both the system and
+         * application library paths, and we use this search order:
+         *
+         *   1. this class loader's library path for application
+         *      libraries
+         *   2. the VM's library path from the system
+         *      property for system libraries
+         *
+         * This order was reversed prior to Gingerbread; see http://b/2933456.
+         */
+        ArrayList<File> result = splitPaths(
+                path, System.getProperty("java.library.path", "."), true);
+        return result.toArray(new File[result.size()]);
+    }
+
+    /**
+     * Splits the given path strings into file elements using the path
+     * separator, combining the results and filtering out elements
+     * that don't exist, aren't readable, or aren't either a regular
+     * file or a directory (as specified). Either string may be empty
+     * or {@code null}, in which case it is ignored. If both strings
+     * are empty or {@code null}, or all elements get pruned out, then
+     * this returns a zero-element list.
+     */
+    private static ArrayList<File> splitPaths(String path1, String path2,
+            boolean wantDirectories) {
+        ArrayList<File> result = new ArrayList<File>();
+
+        splitAndAdd(path1, wantDirectories, result);
+        splitAndAdd(path2, wantDirectories, result);
+        return result;
+    }
+
+    /**
+     * Helper for {@link #splitPaths}, which does the actual splitting
+     * and filtering and adding to a result.
+     */
+    private static void splitAndAdd(String searchPath, boolean directoriesOnly,
+            ArrayList<File> resultList) {
+        if (searchPath == null) {
+            return;
+        }
+        for (String path : searchPath.split(":")) {
+            try {
+                StructStat sb = Libcore.os.stat(path);
+                if (!directoriesOnly || S_ISDIR(sb.st_mode)) {
+                    resultList.add(new File(path));
+                }
+            } catch (ErrnoException ignored) {
+            }
+        }
+    }
+
+    /**
+     * Makes an array of dex/resource path elements, one per element of
+     * the given array.
+     */
+    private static Element[] makeDexElements(ArrayList<File> files,
+            File optimizedDirectory) {
+        ArrayList<Element> elements = new ArrayList<Element>();
+
+        /*
+         * Open all files and load the (direct or contained) dex files
+         * up front.
+         */
+        for (File file : files) {
+            File zip = null;
+            DexFile dex = null;
+            String name = file.getName();
+
+            if (name.endsWith(DEX_SUFFIX)) {
+                // Raw dex file (not inside a zip/jar).
+                try {
+                    dex = loadDexFile(file, optimizedDirectory);
+                } catch (IOException ex) {
+                    System.logE("Unable to load dex file: " + file, ex);
+                }
+            } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
+                    || name.endsWith(ZIP_SUFFIX)) {
+                zip = file;
+
+                try {
+                    dex = loadDexFile(file, optimizedDirectory);
+                } catch (IOException ignored) {
+                    /*
+                     * IOException might get thrown "legitimately" by
+                     * the DexFile constructor if the zip file turns
+                     * out to be resource-only (that is, no
+                     * classes.dex file in it). Safe to just ignore
+                     * the exception here, and let dex == null.
+                     */
+                }
+            } else if (file.isDirectory()) {
+                // We support directories for looking up resources.
+                // This is only useful for running libcore tests.
+                elements.add(new Element(file, true, null, null));
+            } else {
+                System.logW("Unknown file type for: " + file);
+            }
+
+            if ((zip != null) || (dex != null)) {
+                elements.add(new Element(file, false, zip, dex));
+            }
+        }
+
+        return elements.toArray(new Element[elements.size()]);
+    }
+
+    /**
+     * Constructs a {@code DexFile} instance, as appropriate depending
+     * on whether {@code optimizedDirectory} is {@code null}.
+     */
+    private static DexFile loadDexFile(File file, File optimizedDirectory)
+            throws IOException {
+        if (optimizedDirectory == null) {
+            return new DexFile(file);
+        } else {
+            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
+            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
+        }
+    }
+
+    /**
+     * Converts a dex/jar file path and an output directory to an
+     * output file path for an associated optimized dex file.
+     */
+    private static String optimizedPathFor(File path,
+            File optimizedDirectory) {
+        /*
+         * Get the filename component of the path, and replace the
+         * suffix with ".dex" if that's not already the suffix.
+         *
+         * We don't want to use ".odex", because the build system uses
+         * that for files that are paired with resource-only jar
+         * files. If the VM can assume that there's no classes.dex in
+         * the matching jar, it doesn't need to open the jar to check
+         * for updated dependencies, providing a slight performance
+         * boost at startup. The use of ".dex" here matches the use on
+         * files in /data/dalvik-cache.
+         */
+        String fileName = path.getName();
+        if (!fileName.endsWith(DEX_SUFFIX)) {
+            int lastDot = fileName.lastIndexOf(".");
+            if (lastDot < 0) {
+                fileName += DEX_SUFFIX;
+            } else {
+                StringBuilder sb = new StringBuilder(lastDot + 4);
+                sb.append(fileName, 0, lastDot);
+                sb.append(DEX_SUFFIX);
+                fileName = sb.toString();
+            }
+        }
+
+        File result = new File(optimizedDirectory, fileName);
+        return result.getPath();
+    }
+
+    /**
+     * Finds the named class in one of the dex files pointed at by
+     * this instance. This will find the one in the earliest listed
+     * path element. If the class is found but has not yet been
+     * defined, then this method will define it in the defining
+     * context that this instance was constructed with.
+     *
+     * @return the named class or {@code null} if the class is not
+     * found in any of the dex files
+     */
+    public Class findClass(String name) {
+        for (Element element : dexElements) {
+            DexFile dex = element.dexFile;
+
+            if (dex != null) {
+                Class clazz = dex.loadClassBinaryName(name, definingContext);
+                if (clazz != null) {
+                    return clazz;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Finds the named resource in one of the zip/jar files pointed at
+     * by this instance. This will find the one in the earliest listed
+     * path element.
+     *
+     * @return a URL to the named resource or {@code null} if the
+     * resource is not found in any of the zip/jar files
+     */
+    public URL findResource(String name) {
+        for (Element element : dexElements) {
+            URL url = element.findResource(name);
+            if (url != null) {
+                return url;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Finds all the resources with the given name, returning an
+     * enumeration of them. If there are no resources with the given
+     * name, then this method returns an empty enumeration.
+     */
+    public Enumeration<URL> findResources(String name) {
+        ArrayList<URL> result = new ArrayList<URL>();
+
+        for (Element element : dexElements) {
+            URL url = element.findResource(name);
+            if (url != null) {
+                result.add(url);
+            }
+        }
+
+        return Collections.enumeration(result);
+    }
+
+    /**
+     * Finds the named native code library on any of the library
+     * directories pointed at by this instance. This will find the
+     * one in the earliest listed directory, ignoring any that are not
+     * readable regular files.
+     *
+     * @return the complete path to the library or {@code null} if no
+     * library was found
+     */
+    public String findLibrary(String libraryName) {
+        String fileName = System.mapLibraryName(libraryName);
+        for (File directory : nativeLibraryDirectories) {
+            String path = new File(directory, fileName).getPath();
+            if (IoUtils.canOpenReadOnly(path)) {
+                return path;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Element of the dex/resource file path
+     */
+    /*package*/ static class Element {
+        private final File file;
+        private final boolean isDirectory;
+        private final File zip;
+        private final DexFile dexFile;
+
+        private ZipFile zipFile;
+        private boolean initialized;
+
+        public Element(File file, boolean isDirectory, File zip, DexFile dexFile) {
+            this.file = file;
+            this.isDirectory = isDirectory;
+            this.zip = zip;
+            this.dexFile = dexFile;
+        }
+
+        @Override public String toString() {
+            if (isDirectory) {
+                return "directory \"" + file + "\"";
+            } else if (zip != null) {
+                return "zip file \"" + zip + "\"";
+            } else {
+                return "dex file \"" + dexFile + "\"";
+            }
+        }
+
+        public synchronized void maybeInit() {
+            if (initialized) {
+                return;
+            }
+
+            initialized = true;
+
+            if (isDirectory || zip == null) {
+                return;
+            }
+
+            try {
+                zipFile = new ZipFile(zip);
+            } catch (IOException ioe) {
+                /*
+                 * Note: ZipException (a subclass of IOException)
+                 * might get thrown by the ZipFile constructor
+                 * (e.g. if the file isn't actually a zip/jar
+                 * file).
+                 */
+                System.logE("Unable to open zip file: " + file, ioe);
+                zipFile = null;
+            }
+        }
+
+        public URL findResource(String name) {
+            maybeInit();
+
+            // We support directories so we can run tests and/or legacy code
+            // that uses Class.getResource.
+            if (isDirectory) {
+                File resourceFile = new File(file, name);
+                if (resourceFile.exists()) {
+                    try {
+                        return resourceFile.toURI().toURL();
+                    } catch (MalformedURLException ex) {
+                        throw new RuntimeException(ex);
+                    }
+                }
+            }
+
+            if (zipFile == null || zipFile.getEntry(name) == null) {
+                /*
+                 * Either this element has no zip/jar file (first
+                 * clause), or the zip/jar file doesn't have an entry
+                 * for the given name (second clause).
+                 */
+                return null;
+            }
+
+            try {
+                /*
+                 * File.toURL() is compliant with RFC 1738 in
+                 * always creating absolute path names. If we
+                 * construct the URL by concatenating strings, we
+                 * might end up with illegal URLs for relative
+                 * names.
+                 */
+                return new URL("jar:" + file.toURL() + "!/" + name);
+            } catch (MalformedURLException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/NativeStart.java b/dalvik/src/main/java/dalvik/system/NativeStart.java
new file mode 100644
index 0000000..1382823
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/NativeStart.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+/**
+ * Dummy class used during JNI initialization.  The JNI functions want
+ * to be able to create objects, and the VM needs to discard the references
+ * when the function returns.  That gets a little weird when we're
+ * calling JNI functions from the C main(), and there's no Java stack frame
+ * to hitch the references onto.
+ *
+ * Rather than having some special-case code, we create this simple little
+ * class and pretend that it called the C main().
+ *
+ * This also comes in handy when a native thread attaches itself with the
+ * JNI AttachCurrentThread call.  If they attach the thread and start
+ * creating objects, we need a fake frame to store stuff in.
+ */
+class NativeStart {
+    private NativeStart() {}
+
+    private static native void main(String[] dummy);
+
+    private static native void run();
+}
diff --git a/dalvik/src/main/java/dalvik/system/PathClassLoader.java b/dalvik/src/main/java/dalvik/system/PathClassLoader.java
new file mode 100644
index 0000000..32c5586
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/PathClassLoader.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+/**
+ * Provides a simple {@link ClassLoader} implementation that operates on a list
+ * of files and directories in the local file system, but does not attempt to
+ * load classes from the network. Android uses this class for its system class
+ * loader and for its application class loader(s).
+ */
+public class PathClassLoader extends BaseDexClassLoader {
+    /**
+     * Creates a {@code PathClassLoader} that operates on a given list of files
+     * and directories. This method is equivalent to calling
+     * {@link #PathClassLoader(String, String, ClassLoader)} with a
+     * {@code null} value for the second argument (see description there).
+     *
+     * @param dexPath the list of jar/apk files containing classes and
+     * resources, delimited by {@code File.pathSeparator}, which
+     * defaults to {@code ":"} on Android
+     * @param parent the parent class loader
+     */
+    public PathClassLoader(String dexPath, ClassLoader parent) {
+        super(dexPath, null, null, parent);
+    }
+
+    /**
+     * Creates a {@code PathClassLoader} that operates on two given
+     * lists of files and directories. The entries of the first list
+     * should be one of the following:
+     *
+     * <ul>
+     * <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file as
+     * well as arbitrary resources.
+     * <li>Raw ".dex" files (not inside a zip file).
+     * </ul>
+     *
+     * The entries of the second list should be directories containing
+     * native library files.
+     *
+     * @param dexPath the list of jar/apk files containing classes and
+     * resources, delimited by {@code File.pathSeparator}, which
+     * defaults to {@code ":"} on Android
+     * @param libraryPath the list of directories containing native
+     * libraries, delimited by {@code File.pathSeparator}; may be
+     * {@code null}
+     * @param parent the parent class loader
+     */
+    public PathClassLoader(String dexPath, String libraryPath,
+            ClassLoader parent) {
+        super(dexPath, null, libraryPath, parent);
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java b/dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java
new file mode 100644
index 0000000..7a18bb5
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package dalvik.system;
+
+/**
+ * Is thrown when the VM identifies a potential deadlock.
+ *
+ * @hide
+ */
+public class PotentialDeadlockError extends VirtualMachineError {
+    /**
+     * Creates a new exception instance and initializes it with default values.
+     */
+    public PotentialDeadlockError() {
+        super();
+    }
+
+    /**
+     * Creates a new exception instance and initializes it with a given message.
+     *
+     * @param detailMessage the error message
+     */
+    public PotentialDeadlockError(String detailMessage) {
+        super(detailMessage);
+    }
+}
+
diff --git a/dalvik/src/main/java/dalvik/system/SocketTagger.java b/dalvik/src/main/java/dalvik/system/SocketTagger.java
new file mode 100644
index 0000000..75242ce
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/SocketTagger.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+import java.io.FileDescriptor;
+import java.net.Socket;
+import java.net.SocketException;
+
+/**
+ * Callbacks for socket assignment and reassignment.
+ *
+ * @hide
+ */
+public abstract class SocketTagger {
+
+    private static SocketTagger tagger = new SocketTagger() {
+        @Override public void tag(FileDescriptor socketDescriptor) throws SocketException {}
+        @Override public void untag(FileDescriptor socketDescriptor) throws SocketException {}
+    };
+
+    /**
+     * Notified when {@code socketDescriptor} is either assigned to the current
+     * thread. The socket is either newly connected or reused from a connection
+     * pool. Implementations of this method should be thread-safe.
+     */
+    public abstract void tag(FileDescriptor socketDescriptor) throws SocketException;
+
+    /**
+     * Notified when {@code socketDescriptor} is released from the current
+     * thread to a connection pool. Implementations of this method should be
+     * thread-safe.
+     *
+     * <p><strong>Note:</strong> this method will not be invoked when the socket
+     * is closed.
+     */
+    public abstract void untag(FileDescriptor socketDescriptor) throws SocketException;
+
+    public final void tag(Socket socket) throws SocketException {
+        if (!socket.isClosed()) {
+            tag(socket.getFileDescriptor$());
+        }
+    }
+
+    public final void untag(Socket socket) throws SocketException {
+        if (!socket.isClosed()) {
+            untag(socket.getFileDescriptor$());
+        }
+    }
+
+    /**
+     * Sets this process' socket tagger to {@code tagger}.
+     */
+    public static synchronized void set(SocketTagger tagger) {
+        if (tagger == null) {
+            throw new NullPointerException("tagger == null");
+        }
+        SocketTagger.tagger = tagger;
+    }
+
+    /**
+     * Returns this process socket tagger.
+     */
+    public static synchronized SocketTagger get() {
+        return tagger;
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/StaleDexCacheError.java b/dalvik/src/main/java/dalvik/system/StaleDexCacheError.java
new file mode 100644
index 0000000..8bdd86a
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/StaleDexCacheError.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package dalvik.system;
+
+/**
+ * Is thrown when the VM determines that a DEX file's cache is out of date, and
+ * that there is no way to recreate it.
+ *
+ * @hide
+ */
+public class StaleDexCacheError extends VirtualMachineError {
+    /**
+     * Creates a new exception instance and initializes it with default values.
+     */
+    public StaleDexCacheError() {
+        super();
+    }
+
+    /**
+     * Creates a new exception instance and initializes it with a given message.
+     *
+     * @param detailMessage the error message
+     */
+    public StaleDexCacheError(String detailMessage) {
+        super(detailMessage);
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/TemporaryDirectory.java b/dalvik/src/main/java/dalvik/system/TemporaryDirectory.java
new file mode 100644
index 0000000..f8fb0b1
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/TemporaryDirectory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package dalvik.system;
+
+import java.io.File;
+
+/**
+ * Obsolete, for binary compatibility only.
+ *
+ * @hide
+ */
+public class TemporaryDirectory {
+    /**
+     * This method exists for binary compatibility only.
+     */
+    public static void setUpDirectory(String baseDir) {
+    }
+
+    /**
+     * This method exists for binary compatibility only.
+     */
+    public static synchronized void setUpDirectory(File baseDir) {
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java
new file mode 100644
index 0000000..8f40165
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * Provides access to some VM-specific debug features. Though this class and
+ * many of its members are public, this class is meant to be wrapped in a more
+ * friendly way for use by application developers. On the Android platform, the
+ * recommended way to access this functionality is through the class
+ * <code>android.os.Debug</code>.
+ *
+ * @hide
+ */
+public final class VMDebug {
+    /**
+     * Specifies the default method trace data file name.
+     *
+     * @deprecated only used in one place, which is unused and deprecated
+     */
+    @Deprecated
+    static public final String DEFAULT_METHOD_TRACE_FILE_NAME = "/sdcard/dmtrace.trace";
+
+    /**
+     * flag for startMethodTracing(), which adds the results from
+     * startAllocCounting to the trace key file.
+     */
+    public static final int TRACE_COUNT_ALLOCS = 1;
+
+    /* constants for getAllocCount */
+    private static final int KIND_ALLOCATED_OBJECTS     = 1<<0;
+    private static final int KIND_ALLOCATED_BYTES       = 1<<1;
+    private static final int KIND_FREED_OBJECTS         = 1<<2;
+    private static final int KIND_FREED_BYTES           = 1<<3;
+    private static final int KIND_GC_INVOCATIONS        = 1<<4;
+    private static final int KIND_CLASS_INIT_COUNT      = 1<<5;
+    private static final int KIND_CLASS_INIT_TIME       = 1<<6;
+    private static final int KIND_EXT_ALLOCATED_OBJECTS = 1<<12;
+    private static final int KIND_EXT_ALLOCATED_BYTES   = 1<<13;
+    private static final int KIND_EXT_FREED_OBJECTS     = 1<<14;
+    private static final int KIND_EXT_FREED_BYTES       = 1<<15;
+
+    public static final int KIND_GLOBAL_ALLOCATED_OBJECTS =
+        KIND_ALLOCATED_OBJECTS;
+    public static final int KIND_GLOBAL_ALLOCATED_BYTES =
+        KIND_ALLOCATED_BYTES;
+    public static final int KIND_GLOBAL_FREED_OBJECTS =
+        KIND_FREED_OBJECTS;
+    public static final int KIND_GLOBAL_FREED_BYTES =
+        KIND_FREED_BYTES;
+    public static final int KIND_GLOBAL_GC_INVOCATIONS =
+        KIND_GC_INVOCATIONS;
+    public static final int KIND_GLOBAL_CLASS_INIT_COUNT =
+        KIND_CLASS_INIT_COUNT;
+    public static final int KIND_GLOBAL_CLASS_INIT_TIME =
+        KIND_CLASS_INIT_TIME;
+    public static final int KIND_GLOBAL_EXT_ALLOCATED_OBJECTS =
+        KIND_EXT_ALLOCATED_OBJECTS;
+    public static final int KIND_GLOBAL_EXT_ALLOCATED_BYTES =
+        KIND_EXT_ALLOCATED_BYTES;
+    public static final int KIND_GLOBAL_EXT_FREED_OBJECTS =
+        KIND_EXT_FREED_OBJECTS;
+    public static final int KIND_GLOBAL_EXT_FREED_BYTES =
+        KIND_EXT_FREED_BYTES;
+
+    public static final int KIND_THREAD_ALLOCATED_OBJECTS =
+        KIND_ALLOCATED_OBJECTS << 16;
+    public static final int KIND_THREAD_ALLOCATED_BYTES =
+        KIND_ALLOCATED_BYTES << 16;
+    public static final int KIND_THREAD_FREED_OBJECTS =
+        KIND_FREED_OBJECTS << 16;
+    public static final int KIND_THREAD_FREED_BYTES =
+        KIND_FREED_BYTES << 16;
+    public static final int KIND_THREAD_GC_INVOCATIONS =
+        KIND_GC_INVOCATIONS << 16;
+    public static final int KIND_THREAD_CLASS_INIT_COUNT =
+        KIND_CLASS_INIT_COUNT << 16;
+    public static final int KIND_THREAD_CLASS_INIT_TIME =
+        KIND_CLASS_INIT_TIME << 16;
+    public static final int KIND_THREAD_EXT_ALLOCATED_OBJECTS =
+        KIND_EXT_ALLOCATED_OBJECTS << 16;
+    public static final int KIND_THREAD_EXT_ALLOCATED_BYTES =
+        KIND_EXT_ALLOCATED_BYTES << 16;
+    public static final int KIND_THREAD_EXT_FREED_OBJECTS =
+        KIND_EXT_FREED_OBJECTS << 16;
+    public static final int KIND_THREAD_EXT_FREED_BYTES =
+        KIND_EXT_FREED_BYTES << 16;
+
+    public static final int KIND_ALL_COUNTS = 0xffffffff;
+
+    /* all methods are static */
+    private VMDebug() {}
+
+    /**
+     * Returns the time since the last known debugger activity.
+     *
+     * @return the time in milliseconds, or -1 if the debugger is not connected
+     */
+    public static native long lastDebuggerActivity();
+
+    /**
+     * Determines if debugging is enabled in this VM.  If debugging is not
+     * enabled, a debugger cannot be attached.
+     *
+     * @return true if debugging is enabled
+     */
+    public static native boolean isDebuggingEnabled();
+
+    /**
+     * Determines if a debugger is currently attached.
+     *
+     * @return true if (and only if) a debugger is connected
+     */
+    public static native boolean isDebuggerConnected();
+
+    /**
+     * Returns an array of strings that identify VM features.  This is
+     * used by DDMS to determine what sorts of operations the VM can
+     * perform.
+     */
+    public static native String[] getVmFeatureList();
+
+    /**
+     * Start method tracing with default name, size, and with <code>0</code>
+     * flags.
+     *
+     * @deprecated not used, not needed
+     */
+    @Deprecated
+    public static void startMethodTracing() {
+        startMethodTracing(DEFAULT_METHOD_TRACE_FILE_NAME, 0, 0);
+    }
+
+    /**
+     * Start method tracing, specifying a file name as well as a default
+     * buffer size. See <a
+     * href="{@docRoot}guide/developing/tools/traceview.html"> Running the
+     * Traceview Debugging Program</a> for information about reading
+     * trace files.
+     *
+     * <p>You can use either a fully qualified path and
+     * name, or just a name. If only a name is specified, the file will
+     * be created under the /sdcard/ directory. If a name is not given,
+     * the default is /sdcard/dmtrace.trace.</p>
+     *
+     * @param traceFileName name to give the trace file
+     * @param bufferSize the maximum size of both files combined. If passed
+     * as <code>0</code>, it defaults to 8MB.
+     * @param flags flags to control method tracing. The only one that
+     * is currently defined is {@link #TRACE_COUNT_ALLOCS}.
+     */
+    public static void startMethodTracing(String traceFileName, int bufferSize, int flags) {
+
+        if (traceFileName == null) {
+            throw new NullPointerException("traceFileName == null");
+        }
+
+        startMethodTracingNative(traceFileName, null, bufferSize, flags);
+    }
+
+    /**
+     * Like startMethodTracing(String, int, int), but taking an already-opened
+     * FileDescriptor in which the trace is written.  The file name is also
+     * supplied simply for logging.  Makes a dup of the file descriptor.
+     */
+    public static void startMethodTracing(String traceFileName,
+        FileDescriptor fd, int bufferSize, int flags)
+    {
+        if (traceFileName == null) {
+            throw new NullPointerException("traceFileName == null");
+        }
+        if (fd == null) {
+            throw new NullPointerException("fd == null");
+        }
+
+        startMethodTracingNative(traceFileName, fd, bufferSize, flags);
+    }
+
+    /**
+     * Starts method tracing without a backing file.  When stopMethodTracing
+     * is called, the result is sent directly to DDMS.  (If DDMS is not
+     * attached when tracing ends, the profiling data will be discarded.)
+     */
+    public static void startMethodTracingDdms(int bufferSize, int flags) {
+        startMethodTracingNative(null, null, bufferSize, flags);
+    }
+
+    /**
+     * Implements all startMethodTracing variants.
+     */
+    private static native void startMethodTracingNative(String traceFileName,
+        FileDescriptor fd, int bufferSize, int flags);
+
+    /**
+     * Determine whether method tracing is currently active.
+     */
+    public static native boolean isMethodTracingActive();
+
+    /**
+     * Stops method tracing.
+     */
+    public static native void stopMethodTracing();
+
+    /**
+     * Starts sending Dalvik method trace info to the emulator.
+     */
+    public static native void startEmulatorTracing();
+
+    /**
+     * Stops sending Dalvik method trace info to the emulator.
+     */
+    public static native void stopEmulatorTracing();
+
+    /**
+     * Get an indication of thread CPU usage. The value returned indicates the
+     * amount of time that the current thread has spent executing code or
+     * waiting for certain types of I/O.
+     * <p>
+     * The time is expressed in nanoseconds, and is only meaningful when
+     * compared to the result from an earlier call. Note that nanosecond
+     * resolution does not imply nanosecond accuracy.
+     *
+     * @return the CPU usage. A value of -1 means the system does not support
+     *         this feature.
+     */
+    public static native long threadCpuTimeNanos();
+
+    /**
+     * Count the number and aggregate size of memory allocations between
+     * two points.
+     */
+    public static native void startAllocCounting();
+    public static native void stopAllocCounting();
+    public static native int getAllocCount(int kind);
+    public static native void resetAllocCount(int kinds);
+
+    /**
+     * This method exists for binary compatibility.  It was part of
+     * the allocation limits API which was removed in Honeycomb.
+     */
+    @Deprecated
+    public static int setAllocationLimit(int limit) {
+        return -1;
+    }
+
+    /**
+     * This method exists for binary compatibility.  It was part of
+     * the allocation limits API which was removed in Honeycomb.
+     */
+    @Deprecated
+    public static int setGlobalAllocationLimit(int limit) {
+        return -1;
+    }
+
+    /**
+     * Count the number of instructions executed between two points.
+     */
+    public static native void startInstructionCounting();
+    public static native void stopInstructionCounting();
+    public static native void getInstructionCount(int[] counts);
+    public static native void resetInstructionCount();
+
+    /**
+     * Dumps a list of loaded class to the log file.
+     */
+    public static native void printLoadedClasses(int flags);
+
+    /**
+     * Gets the number of loaded classes.
+     *
+     * @return the number of loaded classes
+     */
+    public static native int getLoadedClassCount();
+
+    /**
+     * Dumps "hprof" data to the specified file.  This may cause a GC.
+     *
+     * The VM may create a temporary file in the same directory.
+     *
+     * @param filename Full pathname of output file (e.g. "/sdcard/dump.hprof").
+     * @throws UnsupportedOperationException if the VM was built without
+     *         HPROF support.
+     * @throws IOException if an error occurs while opening or writing files.
+     */
+    public static void dumpHprofData(String filename) throws IOException {
+        if (filename == null) {
+            throw new NullPointerException("filename == null");
+        }
+        dumpHprofData(filename, null);
+    }
+
+    /**
+     * Collects "hprof" heap data and sends it to DDMS.  This may cause a GC.
+     *
+     * @throws UnsupportedOperationException if the VM was built without
+     *         HPROF support.
+     */
+    public static native void dumpHprofDataDdms();
+
+    /**
+     * Dumps "hprof" heap data to a file, by name or descriptor.
+     *
+     * @param fileName Name of output file.  If fd is non-null, the
+     *        file name is only used in log messages (and may be null).
+     * @param fd Descriptor of open file that will receive the output.
+     *        If this is null, the fileName is used instead.
+     */
+    public static native void dumpHprofData(String fileName, FileDescriptor fd)
+            throws IOException;
+
+    /**
+     * Primes the register map cache.
+     */
+    public static native boolean cacheRegisterMap(String classAndMethodDesc);
+
+    /**
+     * Dumps the contents of the VM reference tables (e.g. JNI locals and
+     * globals) to the log file.
+     */
+    public static native void dumpReferenceTables();
+
+    /**
+     * Crashes the VM.  Seriously.  Dumps the interpreter stack trace for
+     * the current thread and then aborts the VM so you can see the native
+     * stack trace.  Useful for figuring out how you got somewhere when
+     * lots of native code is involved.
+     */
+    public static native void crash();
+
+    /**
+     * Together with gdb, provide a handy way to stop the VM at user-tagged
+     * locations.
+     */
+    public static native void infopoint(int id);
+
+    /*
+     * Fake method, inserted into dmtrace output when the garbage collector
+     * runs.  Not actually called.
+     */
+    private static void startGC() {}
+
+    /*
+     * Fake method, inserted into dmtrace output during class preparation
+     * (loading and linking, but not verification or initialization).  Not
+     * actually called.
+     */
+    private static void startClassPrep() {}
+
+    /**
+     * Counts the instances of a class.
+     *
+     * @param klass the class to be counted.
+     * @param assignable if false, direct instances of klass are
+     *                   counted.  If true, instances that are
+     *                   assignable to klass, as defined by
+     *                   {@link Class#isAssignableFrom} are counted.
+     * @return the number of matching instances.
+     */
+    public static native long countInstancesOfClass(Class klass, boolean assignable);
+}
diff --git a/dalvik/src/main/java/dalvik/system/VMRuntime.java b/dalvik/src/main/java/dalvik/system/VMRuntime.java
new file mode 100644
index 0000000..71098be
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/VMRuntime.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+/**
+ * Provides an interface to VM-global, Dalvik-specific features.
+ * An application cannot create its own Runtime instance, and must obtain
+ * one from the getRuntime method.
+ *
+ * @hide
+ */
+public final class VMRuntime {
+
+    /**
+     * Holds the VMRuntime singleton.
+     */
+    private static final VMRuntime THE_ONE = new VMRuntime();
+
+    /**
+     * Prevents this class from being instantiated.
+     */
+    private VMRuntime() {
+    }
+
+    /**
+     * Returns the object that represents the VM instance's Dalvik-specific
+     * runtime environment.
+     *
+     * @return the runtime object
+     */
+    public static VMRuntime getRuntime() {
+        return THE_ONE;
+    }
+
+    /**
+     * Returns a copy of the VM's command-line property settings.
+     * These are in the form "name=value" rather than "-Dname=value".
+     */
+    public native String[] properties();
+
+    /**
+     * Returns the VM's boot class path.
+     */
+    public native String bootClassPath();
+
+    /**
+     * Returns the VM's class path.
+     */
+    public native String classPath();
+
+    /**
+     * Returns the VM's version.
+     */
+    public native String vmVersion();
+
+    /**
+     * Gets the current ideal heap utilization, represented as a number
+     * between zero and one.  After a GC happens, the Dalvik heap may
+     * be resized so that (size of live objects) / (size of heap) is
+     * equal to this number.
+     *
+     * @return the current ideal heap utilization
+     */
+    public native float getTargetHeapUtilization();
+
+    /**
+     * Sets the current ideal heap utilization, represented as a number
+     * between zero and one.  After a GC happens, the Dalvik heap may
+     * be resized so that (size of live objects) / (size of heap) is
+     * equal to this number.
+     *
+     * <p>This is only a hint to the garbage collector and may be ignored.
+     *
+     * @param newTarget the new suggested ideal heap utilization.
+     *                  This value may be adjusted internally.
+     * @return the previous ideal heap utilization
+     * @throws IllegalArgumentException if newTarget is &lt;= 0.0 or &gt;= 1.0
+     */
+    public float setTargetHeapUtilization(float newTarget) {
+        if (newTarget <= 0.0f || newTarget >= 1.0f) {
+            throw new IllegalArgumentException(newTarget +
+                    " out of range (0,1)");
+        }
+        /* Synchronize to make sure that only one thread gets
+         * a given "old" value if both update at the same time.
+         * Allows for reliable save-and-restore semantics.
+         */
+        synchronized (this) {
+            float oldTarget = getTargetHeapUtilization();
+            nativeSetTargetHeapUtilization(newTarget);
+            return oldTarget;
+        }
+    }
+
+    /**
+     * Sets the target SDK version. Should only be called before the
+     * app starts to run, because it may change the VM's behavior in
+     * dangerous ways. Use 0 to mean "current" (since callers won't
+     * necessarily know the actual current SDK version, and the
+     * allocated version numbers start at 1).
+     */
+    public native void setTargetSdkVersion(int targetSdkVersion);
+
+    /**
+     * This method exists for binary compatibility.  It was part of a
+     * heap sizing API which was removed in Honeycomb.
+     */
+    @Deprecated
+    public long getMinimumHeapSize() {
+        return 0;
+    }
+
+    /**
+     * This method exists for binary compatibility.  It was part of a
+     * heap sizing API which was removed in Honeycomb.
+     */
+    @Deprecated
+    public long setMinimumHeapSize(long size) {
+        return 0;
+    }
+
+    /**
+     * This method exists for binary compatibility.  It used to
+     * perform a garbage collection that cleared SoftReferences.
+     */
+    @Deprecated
+    public void gcSoftReferences() {}
+
+    /**
+     * This method exists for binary compatibility.  It is equivalent
+     * to {@link System#runFinalization}.
+     */
+    @Deprecated
+    public void runFinalizationSync() {
+        System.runFinalization();
+    }
+
+    /**
+     * Implements setTargetHeapUtilization().
+     *
+     * @param newTarget the new suggested ideal heap utilization.
+     *                  This value may be adjusted internally.
+     */
+    private native void nativeSetTargetHeapUtilization(float newTarget);
+
+    /**
+     * This method exists for binary compatibility.  It was part of
+     * the external allocation API which was removed in Honeycomb.
+     */
+    @Deprecated
+    public boolean trackExternalAllocation(long size) {
+        return true;
+    }
+
+    /**
+     * This method exists for binary compatibility.  It was part of
+     * the external allocation API which was removed in Honeycomb.
+     */
+    @Deprecated
+    public void trackExternalFree(long size) {}
+
+    /**
+     * This method exists for binary compatibility.  It was part of
+     * the external allocation API which was removed in Honeycomb.
+     */
+    @Deprecated
+    public long getExternalBytesAllocated() {
+        return 0;
+    }
+
+    /**
+     * Tells the VM to enable the JIT compiler. If the VM does not have a JIT
+     * implementation, calling this method should have no effect.
+     */
+    public native void startJitCompilation();
+
+    /**
+     * Tells the VM to disable the JIT compiler. If the VM does not have a JIT
+     * implementation, calling this method should have no effect.
+     */
+    public native void disableJitCompilation();
+
+    /**
+     * Returns an array allocated in an area of the Java heap where it will never be moved.
+     * This is used to implement native allocations on the Java heap, such as DirectByteBuffers
+     * and Bitmaps.
+     */
+    public native Object newNonMovableArray(Class<?> componentType, int length);
+
+    /**
+     * Returns the address of array[0]. This differs from using JNI in that JNI might lie and
+     * give you the address of a copy of the array when in forcecopy mode.
+     */
+    public native long addressOf(Object array);
+
+    /**
+     * Removes any growth limits, allowing the application to allocate
+     * up to the maximum heap size.
+     */
+    public native void clearGrowthLimit();
+
+    /**
+     * Returns true if either a Java debugger or native debugger is active.
+     */
+    public native boolean isDebuggerActive();
+}
diff --git a/dalvik/src/main/java/dalvik/system/VMStack.java b/dalvik/src/main/java/dalvik/system/VMStack.java
new file mode 100644
index 0000000..9a2be23
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/VMStack.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+/**
+ * Provides a limited interface to the Dalvik VM stack. This class is mostly
+ * used for implementing security checks.
+ *
+ * @hide
+ */
+public final class VMStack {
+    /**
+     * Returns the defining class loader of the caller's caller.
+     *
+     * @return the requested class loader, or {@code null} if this is the
+     *         bootstrap class loader.
+     */
+    native public static ClassLoader getCallingClassLoader();
+
+    /**
+     * Returns the class of the caller's caller's caller.
+     *
+     * @return the requested class, or {@code null}.
+     */
+    native public static Class<?> getStackClass2();
+
+    /**
+     * Creates an array of classes from the methods at the top of the stack.
+     * We continue until we reach the bottom of the stack or exceed the
+     * specified maximum depth.
+     * <p>
+     * The topmost stack frame (this method) and the one above that (the
+     * caller) are excluded from the array.  Frames with java.lang.reflect
+     * classes are skipped over.
+     * <p>
+     * The classes in the array are the defining classes of the methods.
+     * <p>
+     * This is similar to Harmony's VMStack.getClasses, except that this
+     * implementation doesn't have a concept of "privileged" frames.
+     *
+     * @param maxDepth
+     *      maximum number of classes to return, or -1 for all
+     * @return an array with classes for the most-recent methods on the stack
+     */
+    native public static Class<?>[] getClasses(int maxDepth);
+
+    /**
+     * Retrieves the stack trace from the specified thread.
+     *
+     * @param t
+     *      thread of interest
+     * @return an array of stack trace elements, or null if the thread
+     *      doesn't have a stack trace (e.g. because it exited)
+     */
+    native public static StackTraceElement[] getThreadStackTrace(Thread t);
+
+    /**
+     * Retrieves a partial stack trace from the specified thread into
+     * the provided array.
+     *
+     * @param t
+     *      thread of interest
+     * @param stackTraceElements
+     *      preallocated array for use when only the top of stack is
+     *      desired. Unused elements will be filled with null values.
+     * @return the number of elements filled
+     */
+    native public static int fillStackTraceElements(Thread t,
+        StackTraceElement[] stackTraceElements);
+}
diff --git a/dalvik/src/main/java/dalvik/system/Zygote.java b/dalvik/src/main/java/dalvik/system/Zygote.java
new file mode 100644
index 0000000..9e96204
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/Zygote.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+
+import java.io.File;
+
+/**
+ * Provides access to the Dalvik "zygote" feature, which allows a VM instance to
+ * be partially initialized and then fork()'d from the partially initialized
+ * state.
+ *
+ * @hide
+ */
+public class Zygote {
+    /*
+     * Bit values for "debugFlags" argument.  The definitions are duplicated
+     * in the native code.
+     */
+    /** enable debugging over JDWP */
+    public static final int DEBUG_ENABLE_DEBUGGER   = 1;
+    /** enable JNI checks */
+    public static final int DEBUG_ENABLE_CHECKJNI   = 1 << 1;
+    /** enable Java programming language "assert" statements */
+    public static final int DEBUG_ENABLE_ASSERT     = 1 << 2;
+    /** disable the JIT compiler */
+    public static final int DEBUG_ENABLE_SAFEMODE   = 1 << 3;
+    /** Enable logging of third-party JNI activity. */
+    public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4;
+
+    /** No external storage should be mounted. */
+    public static final int MOUNT_EXTERNAL_NONE = 0;
+    /** Single-user external storage should be mounted. */
+    public static final int MOUNT_EXTERNAL_SINGLEUSER = 1;
+    /** Multi-user external storage should be mounted. */
+    public static final int MOUNT_EXTERNAL_MULTIUSER = 2;
+    /** All multi-user external storage should be mounted. */
+    public static final int MOUNT_EXTERNAL_MULTIUSER_ALL = 3;
+
+    /**
+     * When set by the system server, all subsequent apps will be launched in
+     * VM safe mode.
+     */
+    public static boolean systemInSafeMode = false;
+
+    private Zygote() {}
+
+    private static void preFork() {
+        Daemons.stop();
+        waitUntilAllThreadsStopped();
+    }
+
+    /**
+     * We must not fork until we're single-threaded again. Wait until /proc shows we're
+     * down to just one thread.
+     */
+    private static void waitUntilAllThreadsStopped() {
+        File tasks = new File("/proc/self/task");
+        while (tasks.list().length > 1) {
+            try {
+                // Experimentally, booting and playing about with a stingray, I never saw us
+                // go round this loop more than once with a 10ms sleep.
+                Thread.sleep(10);
+            } catch (InterruptedException ignored) {
+            }
+        }
+    }
+
+    private static void postFork() {
+        Daemons.start();
+    }
+
+    /**
+     * Forks a new Zygote instance, but does not leave the zygote mode.
+     * The current VM must have been started with the -Xzygote flag. The
+     * new child is expected to eventually call forkAndSpecialize()
+     *
+     * @return 0 if this is the child, pid of the child
+     * if this is the parent, or -1 on error
+     */
+    public static int fork() {
+        preFork();
+        int pid = nativeFork();
+        postFork();
+        return pid;
+    }
+
+    native public static int nativeFork();
+
+    /**
+     * Forks a new VM instance.  The current VM must have been started
+     * with the -Xzygote flag. <b>NOTE: new instance keeps all
+     * root capabilities. The new process is expected to call capset()</b>.
+     *
+     * @param uid the UNIX uid that the new process should setuid() to after
+     * fork()ing and and before spawning any threads.
+     * @param gid the UNIX gid that the new process should setgid() to after
+     * fork()ing and and before spawning any threads.
+     * @param gids null-ok; a list of UNIX gids that the new process should
+     * setgroups() to after fork and before spawning any threads.
+     * @param debugFlags bit flags that enable debugging features.
+     * @param rlimits null-ok an array of rlimit tuples, with the second
+     * dimension having a length of 3 and representing
+     * (resource, rlim_cur, rlim_max). These are set via the posix
+     * setrlimit(2) call.
+     * @param seInfo null-ok a string specifying SEAndroid information for
+     * the new process.
+     * @param niceName null-ok a string specifying the process name.
+     *
+     * @return 0 if this is the child, pid of the child
+     * if this is the parent, or -1 on error.
+     */
+    public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
+            int[][] rlimits, int mountExternal, String seInfo, String niceName) {
+        preFork();
+        int pid = nativeForkAndSpecialize(
+                uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName);
+        postFork();
+        return pid;
+    }
+
+    native public static int nativeForkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
+            int[][] rlimits, int mountExternal, String seInfo, String niceName);
+
+    /**
+     * Special method to start the system server process. In addition to the
+     * common actions performed in forkAndSpecialize, the pid of the child
+     * process is recorded such that the death of the child process will cause
+     * zygote to exit.
+     *
+     * @param uid the UNIX uid that the new process should setuid() to after
+     * fork()ing and and before spawning any threads.
+     * @param gid the UNIX gid that the new process should setgid() to after
+     * fork()ing and and before spawning any threads.
+     * @param gids null-ok; a list of UNIX gids that the new process should
+     * setgroups() to after fork and before spawning any threads.
+     * @param debugFlags bit flags that enable debugging features.
+     * @param rlimits null-ok an array of rlimit tuples, with the second
+     * dimension having a length of 3 and representing
+     * (resource, rlim_cur, rlim_max). These are set via the posix
+     * setrlimit(2) call.
+     * @param permittedCapabilities argument for setcap()
+     * @param effectiveCapabilities argument for setcap()
+     *
+     * @return 0 if this is the child, pid of the child
+     * if this is the parent, or -1 on error.
+     */
+    public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
+            int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
+        preFork();
+        int pid = nativeForkSystemServer(
+                uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
+        postFork();
+        return pid;
+    }
+
+    native public static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
+            int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
+
+    /**
+     * Executes "/system/bin/sh -c &lt;command&gt;" using the exec() system call.
+     * This method throws a runtime exception if exec() failed, otherwise, this
+     * method never returns.
+     *
+     * @param command The shell command to execute.
+     */
+    public static void execShell(String command) {
+        String[] args = { "/system/bin/sh", "-c", command };
+        try {
+            Libcore.os.execv(args[0], args);
+        } catch (ErrnoException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Appends quotes shell arguments to the specified string builder.
+     * The arguments are quoted using single-quotes, escaped if necessary,
+     * prefixed with a space, and appended to the command.
+     *
+     * @param command A string builder for the shell command being constructed.
+     * @param args An array of argument strings to be quoted and appended to the command.
+     * @see #execShell(String)
+     */
+    public static void appendQuotedShellArgs(StringBuilder command, String[] args) {
+        for (String arg : args) {
+            command.append(" '").append(arg.replace("'", "'\\''")).append("'");
+        }
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java b/dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java
new file mode 100644
index 0000000..dbddcc6
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system.profiler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * AsciiHprofWriter produces hprof compatible text output for use with
+ * third party tools such as PerfAnal.
+ */
+public final class AsciiHprofWriter {
+
+    private final HprofData data;
+    private final PrintWriter out;
+
+    /**
+     * Writes the provided data to the specified stream.
+     */
+    public static void write(HprofData data, OutputStream outputStream) throws IOException {
+        new AsciiHprofWriter(data, outputStream).write();
+    }
+
+    private AsciiHprofWriter(HprofData data, OutputStream outputStream) {
+        this.data = data;
+        this.out = new PrintWriter(outputStream);
+    }
+
+    private void write() throws IOException {
+        for (HprofData.ThreadEvent e : data.getThreadHistory()) {
+            out.println(e);
+        }
+
+        List<HprofData.Sample> samples
+                = new ArrayList<HprofData.Sample>(data.getSamples());
+        Collections.sort(samples, SAMPLE_COMPARATOR);
+        int total = 0;
+        for (HprofData.Sample sample : samples) {
+            HprofData.StackTrace stackTrace = sample.stackTrace;
+            int count = sample.count;
+            total += count;
+            out.printf("TRACE %d: (thread=%d)\n",
+                       stackTrace.stackTraceId,
+                       stackTrace.threadId);
+            for (StackTraceElement e : stackTrace.stackFrames) {
+                out.printf("\t%s\n", e);
+            }
+        }
+        Date now = new Date(data.getStartMillis());
+        // "CPU SAMPLES BEGIN (total = 826) Wed Jul 21 12:03:46 2010"
+        out.printf("CPU SAMPLES BEGIN (total = %d) %ta %tb %td %tT %tY\n",
+                   total, now, now, now, now, now);
+        out.printf("rank   self  accum   count trace method\n");
+        int rank = 0;
+        double accum = 0;
+        for (HprofData.Sample sample : samples) {
+            rank++;
+            HprofData.StackTrace stackTrace = sample.stackTrace;
+            int count = sample.count;
+            double self = (double)count/(double)total;
+            accum += self;
+
+            // "   1 65.62% 65.62%     542 300302 java.lang.Long.parseLong"
+            out.printf("% 4d% 6.2f%%% 6.2f%% % 7d % 5d %s.%s\n",
+                       rank, self*100, accum*100, count, stackTrace.stackTraceId,
+                       stackTrace.stackFrames[0].getClassName(),
+                       stackTrace.stackFrames[0].getMethodName());
+        }
+        out.printf("CPU SAMPLES END\n");
+        out.flush();
+    }
+
+    private static final Comparator<HprofData.Sample> SAMPLE_COMPARATOR
+            = new Comparator<HprofData.Sample>() {
+        public int compare(HprofData.Sample s1, HprofData.Sample s2) {
+            return s2.count - s1.count;
+        }
+    };
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java
new file mode 100644
index 0000000..9720446
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system.profiler;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Hprof binary format related constants shared between the
+ * BinaryHprofReader and BinaryHprofWriter.
+ */
+public final class BinaryHprof {
+    /**
+     * Currently code only supports 4 byte id size.
+     */
+    public static final int ID_SIZE = 4;
+
+    /**
+     * Prefix of valid magic values from the start of a binary hprof file.
+     */
+    static String MAGIC = "JAVA PROFILE ";
+
+    /**
+     * Returns the file's magic value as a String if found, otherwise null.
+     */
+    public static final String readMagic(DataInputStream in) {
+        try {
+            byte[] bytes = new byte[512];
+            for (int i = 0; i < bytes.length; i++) {
+                byte b = in.readByte();
+                if (b == '\0') {
+                    String string = new String(bytes, 0, i, "UTF-8");
+                    if (string.startsWith(MAGIC)) {
+                        return string;
+                    }
+                    return null;
+                }
+                bytes[i] = b;
+            }
+            return null;
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    public static enum Tag {
+
+        STRING_IN_UTF8(0x01, -ID_SIZE),
+        LOAD_CLASS(0x02, 4 + ID_SIZE + 4 + ID_SIZE),
+        UNLOAD_CLASS(0x03, 4),
+        STACK_FRAME(0x04, ID_SIZE + ID_SIZE + ID_SIZE + ID_SIZE + 4 + 4),
+        STACK_TRACE(0x05, -(4 + 4 + 4)),
+        ALLOC_SITES(0x06, -(2 + 4 + 4 + 4 + 8 + 8 + 4)),
+        HEAP_SUMMARY(0x07, 4 + 4 + 8 + 8),
+        START_THREAD(0x0a, 4 + ID_SIZE + 4 + ID_SIZE + ID_SIZE + ID_SIZE),
+        END_THREAD(0x0b, 4),
+        HEAP_DUMP(0x0c, -0),
+        HEAP_DUMP_SEGMENT(0x1c, -0),
+        HEAP_DUMP_END(0x2c, 0),
+        CPU_SAMPLES(0x0d, -(4 + 4)),
+        CONTROL_SETTINGS(0x0e, 4 + 2);
+
+        public final byte tag;
+
+        /**
+         * Minimum size in bytes.
+         */
+        public final int minimumSize;
+
+        /**
+         * Maximum size in bytes. 0 mean no specific limit.
+         */
+        public final int maximumSize;
+
+        private Tag(int tag, int size) {
+            this.tag = (byte) tag;
+            if (size > 0) {
+                // fixed size, max and min the same
+                this.minimumSize = size;
+                this.maximumSize = size;
+            } else {
+                // only minimum bound
+                this.minimumSize = -size;
+                this.maximumSize = 0;
+            }
+        }
+
+        private static final Map<Byte, Tag> BYTE_TO_TAG
+                = new HashMap<Byte, Tag>();
+
+        static {
+            for (Tag v : Tag.values()) {
+                BYTE_TO_TAG.put(v.tag, v);
+            }
+        }
+
+        public static Tag get(byte tag) {
+            return BYTE_TO_TAG.get(tag);
+        }
+
+        /**
+         * Returns null if the actual size meets expectations, or a
+         * String error message if not.
+         */
+        public String checkSize(int actual) {
+            if (actual < minimumSize) {
+                return "expected a minimial record size of " + minimumSize + " for " + this
+                        + " but received " + actual;
+            }
+            if (maximumSize == 0) {
+                return null;
+            }
+            if (actual > maximumSize) {
+                return "expected a maximum record size of " + maximumSize + " for " + this
+                        + " but received " + actual;
+            }
+            return null;
+        }
+    }
+
+    public static enum ControlSettings {
+        ALLOC_TRACES(0x01),
+        CPU_SAMPLING(0x02);
+
+        public final int bitmask;
+
+        private ControlSettings(int bitmask) {
+            this.bitmask = bitmask;
+        }
+    }
+
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java
new file mode 100644
index 0000000..75a17f3
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system.profiler;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <pre>   {@code
+ * BinaryHprofReader reader = new BinaryHprofReader(new BufferedInputStream(inputStream));
+ * reader.setStrict(false); // for RI compatability
+ * reader.read();
+ * inputStream.close();
+ * reader.getVersion();
+ * reader.getHprofData();
+ * }</pre>
+ */
+public final class BinaryHprofReader {
+
+    private static final boolean TRACE = false;
+
+    private final DataInputStream in;
+
+    /**
+     * By default we try to strictly validate rules followed by
+     * our HprofWriter. For example, every end thread is preceded
+     * by a matching start thread.
+     */
+    private boolean strict = true;
+
+    /**
+     * version string from header after read has been performed,
+     * otherwise null. nullness used to detect if callers try to
+     * access data before read is called.
+     */
+    private String version;
+
+    private final Map<HprofData.StackTrace, int[]> stackTraces
+            = new HashMap<HprofData.StackTrace, int[]>();
+
+    private final HprofData hprofData = new HprofData(stackTraces);
+
+    private final Map<Integer, String> idToString = new HashMap<Integer, String>();
+    private final Map<Integer, String> idToClassName = new HashMap<Integer, String>();
+    private final Map<Integer, StackTraceElement> idToStackFrame
+            = new HashMap<Integer, StackTraceElement>();
+    private final Map<Integer, HprofData.StackTrace> idToStackTrace
+            = new HashMap<Integer, HprofData.StackTrace>();
+
+    /**
+     * Creates a BinaryHprofReader around the specified {@code
+     * inputStream}
+     */
+    public BinaryHprofReader(InputStream inputStream) throws IOException {
+        this.in = new DataInputStream(inputStream);
+    }
+
+    public boolean getStrict () {
+        return strict;
+    }
+
+    public void setStrict (boolean strict) {
+        if (version != null) {
+            throw new IllegalStateException("cannot set strict after read()");
+        }
+        this.strict = strict;
+    }
+
+    /**
+     * throws IllegalStateException if read() has not been called.
+     */
+    private void checkRead() {
+        if (version == null) {
+            throw new IllegalStateException("data access before read()");
+        }
+    }
+
+    public String getVersion() {
+        checkRead();
+        return version;
+    }
+
+    public HprofData getHprofData() {
+        checkRead();
+        return hprofData;
+    }
+
+    /**
+     * Read the hprof header and records from the input
+     */
+    public void read() throws IOException {
+        parseHeader();
+        parseRecords();
+    }
+
+    private void parseHeader() throws IOException {
+        if (TRACE) {
+            System.out.println("hprofTag=HEADER");
+        }
+        parseVersion();
+        parseIdSize();
+        parseTime();
+    }
+
+    private void parseVersion() throws IOException {
+        String version = BinaryHprof.readMagic(in);
+        if (version == null) {
+            throw new MalformedHprofException("Could not find HPROF version");
+        }
+        if (TRACE) {
+            System.out.println("\tversion=" + version);
+        }
+        this.version = version;
+    }
+
+    private void parseIdSize() throws IOException {
+        int idSize = in.readInt();
+        if (TRACE) {
+            System.out.println("\tidSize=" + idSize);
+        }
+        if (idSize != BinaryHprof.ID_SIZE) {
+            throw new MalformedHprofException("Unsupported identifier size: " + idSize);
+        }
+    }
+
+    private void parseTime() throws IOException {
+        long time = in.readLong();
+        if (TRACE) {
+            System.out.println("\ttime=" + Long.toHexString(time) + " " + new Date(time));
+        }
+        hprofData.setStartMillis(time);
+    }
+
+    private void parseRecords() throws IOException {
+        while (parseRecord()) {
+            ;
+        }
+    }
+
+    /**
+     * Read and process the next record. Returns true if a
+     * record was handled, false on EOF.
+     */
+    private boolean parseRecord() throws IOException {
+        int tagOrEOF = in.read();
+        if (tagOrEOF == -1) {
+            return false;
+        }
+        byte tag = (byte) tagOrEOF;
+        int timeDeltaInMicroseconds = in.readInt();
+        int recordLength = in.readInt();
+        BinaryHprof.Tag hprofTag = BinaryHprof.Tag.get(tag);
+        if (TRACE) {
+            System.out.println("hprofTag=" + hprofTag);
+        }
+        if (hprofTag == null) {
+            skipRecord(hprofTag, recordLength);
+            return true;
+        }
+        String error = hprofTag.checkSize(recordLength);
+        if (error != null) {
+            throw new MalformedHprofException(error);
+        }
+        switch (hprofTag) {
+            case CONTROL_SETTINGS:
+                parseControlSettings();
+                return true;
+
+            case STRING_IN_UTF8:
+                parseStringInUtf8(recordLength);
+                return true;
+
+            case START_THREAD:
+                parseStartThread();
+                return true;
+            case END_THREAD:
+                parseEndThread();
+                return true;
+
+            case LOAD_CLASS:
+                parseLoadClass();
+                return true;
+            case STACK_FRAME:
+                parseStackFrame();
+                return true;
+            case STACK_TRACE:
+                parseStackTrace(recordLength);
+                return true;
+
+            case CPU_SAMPLES:
+                parseCpuSamples(recordLength);
+                return true;
+
+            case UNLOAD_CLASS:
+            case ALLOC_SITES:
+            case HEAP_SUMMARY:
+            case HEAP_DUMP:
+            case HEAP_DUMP_SEGMENT:
+            case HEAP_DUMP_END:
+            default:
+                skipRecord(hprofTag, recordLength);
+                return true;
+        }
+    }
+
+    private void skipRecord(BinaryHprof.Tag hprofTag, long recordLength) throws IOException {
+        if (TRACE) {
+            System.out.println("\tskipping recordLength=" + recordLength);
+        }
+        long skipped = in.skip(recordLength);
+        if (skipped != recordLength) {
+            throw new EOFException("Expected to skip " + recordLength
+                                   + " bytes but only skipped " + skipped + " bytes");
+        }
+    }
+
+    private void parseControlSettings() throws IOException {
+        int flags = in.readInt();
+        short depth = in.readShort();
+        if (TRACE) {
+            System.out.println("\tflags=" + Integer.toHexString(flags));
+            System.out.println("\tdepth=" + depth);
+        }
+        hprofData.setFlags(flags);
+        hprofData.setDepth(depth);
+    }
+
+    private void parseStringInUtf8(int recordLength) throws IOException {
+        int stringId = in.readInt();
+        byte[] bytes = new byte[recordLength - BinaryHprof.ID_SIZE];
+        readFully(in, bytes);
+        String string = new String(bytes, "UTF-8");
+        if (TRACE) {
+            System.out.println("\tstring=" + string);
+        }
+        String old = idToString.put(stringId, string);
+        if (old != null) {
+            throw new MalformedHprofException("Duplicate string id: " + stringId);
+        }
+    }
+
+    private static void readFully(InputStream in, byte[] dst) throws IOException {
+        int offset = 0;
+        int byteCount = dst.length;
+        while (byteCount > 0) {
+            int bytesRead = in.read(dst, offset, byteCount);
+            if (bytesRead < 0) {
+                throw new EOFException();
+            }
+            offset += bytesRead;
+            byteCount -= bytesRead;
+        }
+    }
+
+    private void parseLoadClass() throws IOException {
+        int classId = in.readInt();
+        int classObjectId = readId();
+        // serial number apparently not a stack trace id. (int vs ID)
+        // we don't use this field.
+        int stackTraceSerialNumber = in.readInt();
+        String className = readString();
+        if (TRACE) {
+            System.out.println("\tclassId=" + classId);
+            System.out.println("\tclassObjectId=" + classObjectId);
+            System.out.println("\tstackTraceSerialNumber=" + stackTraceSerialNumber);
+            System.out.println("\tclassName=" + className);
+        }
+        String old = idToClassName.put(classId, className);
+        if (old != null) {
+            throw new MalformedHprofException("Duplicate class id: " + classId);
+        }
+    }
+
+    private int readId() throws IOException {
+        return in.readInt();
+    }
+
+    private String readString() throws IOException {
+        int id = readId();
+        if (id == 0) {
+            return null;
+        }
+        String string = idToString.get(id);
+        if (string == null) {
+            throw new MalformedHprofException("Unknown string id " + id);
+        }
+        return string;
+    }
+
+    private String readClass() throws IOException {
+        int id = readId();
+        String string = idToClassName.get(id);
+        if (string == null) {
+            throw new MalformedHprofException("Unknown class id " + id);
+        }
+        return string;
+    }
+
+    private void parseStartThread() throws IOException {
+        int threadId = in.readInt();
+        int objectId = readId();
+        // stack trace where thread was created.
+        // serial number apparently not a stack trace id. (int vs ID)
+        // we don't use this field.
+        int stackTraceSerialNumber = in.readInt();
+        String threadName = readString();
+        String groupName = readString();
+        String parentGroupName = readString();
+        if (TRACE) {
+            System.out.println("\tthreadId=" + threadId);
+            System.out.println("\tobjectId=" + objectId);
+            System.out.println("\tstackTraceSerialNumber=" + stackTraceSerialNumber);
+            System.out.println("\tthreadName=" + threadName);
+            System.out.println("\tgroupName=" + groupName);
+            System.out.println("\tparentGroupName=" + parentGroupName);
+        }
+        HprofData.ThreadEvent event
+                = HprofData.ThreadEvent.start(objectId, threadId,
+                                              threadName, groupName, parentGroupName);
+        hprofData.addThreadEvent(event);
+    }
+
+    private void parseEndThread() throws IOException {
+        int threadId = in.readInt();
+        if (TRACE) {
+            System.out.println("\tthreadId=" + threadId);
+        }
+        HprofData.ThreadEvent event = HprofData.ThreadEvent.end(threadId);
+        hprofData.addThreadEvent(event);
+    }
+
+    private void parseStackFrame() throws IOException {
+        int stackFrameId = readId();
+        String methodName = readString();
+        String methodSignature = readString();
+        String file = readString();
+        String className = readClass();
+        int line = in.readInt();
+        if (TRACE) {
+            System.out.println("\tstackFrameId=" + stackFrameId);
+            System.out.println("\tclassName=" + className);
+            System.out.println("\tmethodName=" + methodName);
+            System.out.println("\tmethodSignature=" + methodSignature);
+            System.out.println("\tfile=" + file);
+            System.out.println("\tline=" + line);
+        }
+        StackTraceElement stackFrame = new StackTraceElement(className, methodName, file, line);
+        StackTraceElement old = idToStackFrame.put(stackFrameId, stackFrame);
+        if (old != null) {
+            throw new MalformedHprofException("Duplicate stack frame id: " + stackFrameId);
+        }
+    }
+
+    private void parseStackTrace(int recordLength) throws IOException {
+        int stackTraceId = in.readInt();
+        int threadId = in.readInt();
+        int frames = in.readInt();
+        if (TRACE) {
+            System.out.println("\tstackTraceId=" + stackTraceId);
+            System.out.println("\tthreadId=" + threadId);
+            System.out.println("\tframes=" + frames);
+        }
+        int expectedLength = 4 + 4 + 4 + (frames * BinaryHprof.ID_SIZE);
+        if (recordLength != expectedLength) {
+            throw new MalformedHprofException("Expected stack trace record of size "
+                                              + expectedLength
+                                              + " based on number of frames but header "
+                                              + "specified a length of  " + recordLength);
+        }
+        StackTraceElement[] stackFrames = new StackTraceElement[frames];
+        for (int i = 0; i < frames; i++) {
+            int stackFrameId = readId();
+            StackTraceElement stackFrame = idToStackFrame.get(stackFrameId);
+            if (TRACE) {
+                System.out.println("\tstackFrameId=" + stackFrameId);
+                System.out.println("\tstackFrame=" + stackFrame);
+            }
+            if (stackFrame == null) {
+                throw new MalformedHprofException("Unknown stack frame id " + stackFrameId);
+            }
+            stackFrames[i] = stackFrame;
+        }
+
+        HprofData.StackTrace stackTrace
+                = new HprofData.StackTrace(stackTraceId, threadId, stackFrames);
+        if (strict) {
+            hprofData.addStackTrace(stackTrace, new int[1]);
+        } else {
+            // The RI can have duplicate stacks, presumably they
+            // have a minor race if two samples with the same
+            // stack are taken around the same time. if we have a
+            // duplicate, just skip adding it to hprofData, but
+            // register it locally in idToStackFrame. if it seen
+            // in CPU_SAMPLES, we will find a StackTrace is equal
+            // to the first, so they will share a countCell.
+            int[] countCell = stackTraces.get(stackTrace);
+            if (countCell == null) {
+                hprofData.addStackTrace(stackTrace, new int[1]);
+            }
+        }
+
+        HprofData.StackTrace old = idToStackTrace.put(stackTraceId, stackTrace);
+        if (old != null) {
+            throw new MalformedHprofException("Duplicate stack trace id: " + stackTraceId);
+        }
+
+    }
+
+    private void parseCpuSamples(int recordLength) throws IOException {
+        int totalSamples = in.readInt();
+        int samplesCount = in.readInt();
+        if (TRACE) {
+            System.out.println("\ttotalSamples=" + totalSamples);
+            System.out.println("\tsamplesCount=" + samplesCount);
+        }
+        int expectedLength = 4 + 4 + (samplesCount * (4 + 4));
+        if (recordLength != expectedLength) {
+            throw new MalformedHprofException("Expected CPU samples record of size "
+                                              + expectedLength
+                                              + " based on number of samples but header "
+                                              + "specified a length of  " + recordLength);
+        }
+        int total = 0;
+        for (int i = 0; i < samplesCount; i++) {
+            int count = in.readInt();
+            int stackTraceId = in.readInt();
+            if (TRACE) {
+                System.out.println("\tcount=" + count);
+                System.out.println("\tstackTraceId=" + stackTraceId);
+            }
+            HprofData.StackTrace stackTrace = idToStackTrace.get(stackTraceId);
+            if (stackTrace == null) {
+                throw new MalformedHprofException("Unknown stack trace id " + stackTraceId);
+            }
+            if (count == 0) {
+                throw new MalformedHprofException("Zero sample count for stack trace "
+                                                  + stackTrace);
+            }
+            int[] countCell = stackTraces.get(stackTrace);
+            if (strict) {
+                if (countCell[0] != 0) {
+                    throw new MalformedHprofException("Setting sample count of stack trace "
+                                                      + stackTrace + " to " + count
+                                                      + " found it was already initialized to "
+                                                      + countCell[0]);
+                }
+            } else {
+                // Coalesce counts from duplicate stack traces.
+                // For more on this, see comments in parseStackTrace.
+                count += countCell[0];
+            }
+            countCell[0] = count;
+            total += count;
+        }
+        if (strict && totalSamples != total) {
+            throw new MalformedHprofException("Expected a total of " + totalSamples
+                                              + " samples but saw " + total);
+        }
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
new file mode 100644
index 0000000..5c29838
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system.profiler;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * BinaryHprofWriter produces hprof compatible binary output for use
+ * with third party tools. Such files can be converted to text with
+ * with {@link HprofBinaryToAscii} or read back in with {@link BinaryHprofReader}.
+ */
+public final class BinaryHprofWriter {
+
+    private int nextStringId = 1; // id 0 => null
+    private int nextClassId = 1;
+    private int nextStackFrameId = 1;
+    private final Map<String, Integer> stringToId = new HashMap<String, Integer>();
+    private final Map<String, Integer> classNameToId = new HashMap<String, Integer>();
+    private final Map<StackTraceElement, Integer> stackFrameToId
+            = new HashMap<StackTraceElement, Integer>();
+
+    private final HprofData data;
+    private final DataOutputStream out;
+
+    /**
+     * Writes the provided data to the specified stream.
+     */
+    public static void write(HprofData data, OutputStream outputStream) throws IOException {
+        new BinaryHprofWriter(data, outputStream).write();
+    }
+
+    private BinaryHprofWriter(HprofData data, OutputStream outputStream) {
+        this.data = data;
+        this.out = new DataOutputStream(outputStream);
+    }
+
+    private void write() throws IOException {
+        try {
+            writeHeader(data.getStartMillis());
+
+            writeControlSettings(data.getFlags(), data.getDepth());
+
+            for (HprofData.ThreadEvent event : data.getThreadHistory()) {
+                writeThreadEvent(event);
+            }
+
+            Set<HprofData.Sample> samples = data.getSamples();
+            int total = 0;
+            for (HprofData.Sample sample : samples) {
+                total += sample.count;
+                writeStackTrace(sample.stackTrace);
+            }
+            writeCpuSamples(total, samples);
+
+        } finally {
+            out.flush();
+        }
+    }
+
+    private void writeHeader(long dumpTimeInMilliseconds) throws IOException {
+        out.writeBytes(BinaryHprof.MAGIC + "1.0.2");
+        out.writeByte(0); // null terminated string
+        out.writeInt(BinaryHprof.ID_SIZE);
+        out.writeLong(dumpTimeInMilliseconds);
+    }
+
+    private void writeControlSettings(int flags, int depth) throws IOException {
+        if (depth > Short.MAX_VALUE) {
+            throw new IllegalArgumentException("depth too large for binary hprof: "
+                                               + depth + " > " + Short.MAX_VALUE);
+        }
+        writeRecordHeader(BinaryHprof.Tag.CONTROL_SETTINGS,
+                          0,
+                          BinaryHprof.Tag.CONTROL_SETTINGS.maximumSize);
+        out.writeInt(flags);
+        out.writeShort((short) depth);
+    }
+
+    private void writeThreadEvent(HprofData.ThreadEvent e) throws IOException {
+        switch (e.type) {
+            case START:
+                writeStartThread(e);
+                return;
+            case END:
+                writeStopThread(e);
+                return;
+        }
+        throw new IllegalStateException(e.type.toString());
+    }
+
+    private void writeStartThread(HprofData.ThreadEvent e) throws IOException {
+        int threadNameId = writeString(e.threadName);
+        int groupNameId = writeString(e.groupName);
+        int parentGroupNameId = writeString(e.parentGroupName);
+        writeRecordHeader(BinaryHprof.Tag.START_THREAD,
+                          0,
+                          BinaryHprof.Tag.START_THREAD.maximumSize);
+        out.writeInt(e.threadId);
+        writeId(e.objectId);
+        out.writeInt(0); // stack trace where thread was started unavailable
+        writeId(threadNameId);
+        writeId(groupNameId);
+        writeId(parentGroupNameId);
+    }
+
+    private void writeStopThread(HprofData.ThreadEvent e) throws IOException {
+        writeRecordHeader(BinaryHprof.Tag.END_THREAD,
+                          0,
+                          BinaryHprof.Tag.END_THREAD.maximumSize);
+        out.writeInt(e.threadId);
+    }
+
+    private void writeRecordHeader(BinaryHprof.Tag hprofTag,
+                                   int timeDeltaInMicroseconds,
+                                   int recordLength) throws IOException {
+        String error = hprofTag.checkSize(recordLength);
+        if (error != null) {
+            throw new AssertionError(error);
+        }
+        out.writeByte(hprofTag.tag);
+        out.writeInt(timeDeltaInMicroseconds);
+        out.writeInt(recordLength);
+    }
+
+    private void writeId(int id) throws IOException {
+        out.writeInt(id);
+    }
+
+    /**
+     * Ensures that a string has been writen to the out and
+     * returns its ID. The ID of a null string is zero, and
+     * doesn't actually result in any output. In a string has
+     * already been written previously, the earlier ID will be
+     * returned and no output will be written.
+     */
+    private int writeString(String string) throws IOException {
+        if (string == null) {
+            return 0;
+        }
+        Integer identifier = stringToId.get(string);
+        if (identifier != null) {
+            return identifier;
+        }
+
+        int id = nextStringId++;
+        stringToId.put(string, id);
+
+        byte[] bytes = string.getBytes("UTF-8");
+        writeRecordHeader(BinaryHprof.Tag.STRING_IN_UTF8,
+                          0,
+                          BinaryHprof.ID_SIZE + bytes.length);
+        out.writeInt(id);
+        out.write(bytes, 0, bytes.length);
+
+        return id;
+    }
+
+    private void writeCpuSamples(int totalSamples, Set<HprofData.Sample> samples)
+            throws IOException {
+        int samplesCount = samples.size();
+        if (samplesCount == 0) {
+            return;
+        }
+        writeRecordHeader(BinaryHprof.Tag.CPU_SAMPLES, 0, 4 + 4 + (samplesCount * (4 + 4)));
+        out.writeInt(totalSamples);
+        out.writeInt(samplesCount);
+        for (HprofData.Sample sample : samples) {
+            out.writeInt(sample.count);
+            out.writeInt(sample.stackTrace.stackTraceId);
+        }
+    }
+
+    private void writeStackTrace(HprofData.StackTrace stackTrace) throws IOException {
+        int frames = stackTrace.stackFrames.length;
+        int[] stackFrameIds = new int[frames];
+        for (int i = 0; i < frames; i++) {
+            stackFrameIds[i] = writeStackFrame(stackTrace.stackFrames[i]);
+        }
+        writeRecordHeader(BinaryHprof.Tag.STACK_TRACE,
+                          0,
+                          4 + 4 + 4 + (frames * BinaryHprof.ID_SIZE));
+        out.writeInt(stackTrace.stackTraceId);
+        out.writeInt(stackTrace.threadId);
+        out.writeInt(frames);
+        for (int stackFrameId : stackFrameIds) {
+            writeId(stackFrameId);
+        }
+    }
+
+    private int writeLoadClass(String className) throws IOException {
+        Integer identifier = classNameToId.get(className);
+        if (identifier != null) {
+            return identifier;
+        }
+        int id = nextClassId++;
+        classNameToId.put(className, id);
+
+        int classNameId = writeString(className);
+        writeRecordHeader(BinaryHprof.Tag.LOAD_CLASS,
+                          0,
+                          BinaryHprof.Tag.LOAD_CLASS.maximumSize);
+        out.writeInt(id);
+        writeId(0); // class object ID
+        out.writeInt(0); // stack trace where class was loaded is unavailable
+        writeId(classNameId);
+
+        return id;
+    }
+
+    private int writeStackFrame(StackTraceElement stackFrame) throws IOException {
+        Integer identifier = stackFrameToId.get(stackFrame);
+        if (identifier != null) {
+            return identifier;
+        }
+
+        int id = nextStackFrameId++;
+        stackFrameToId.put(stackFrame, id);
+
+        int classId = writeLoadClass(stackFrame.getClassName());
+        int methodNameId = writeString(stackFrame.getMethodName());
+        int sourceId = writeString(stackFrame.getFileName());
+        writeRecordHeader(BinaryHprof.Tag.STACK_FRAME,
+                          0,
+                          BinaryHprof.Tag.STACK_FRAME.maximumSize);
+        writeId(id);
+        writeId(methodNameId);
+        writeId(0); // method signature is unavailable from StackTraceElement
+        writeId(sourceId);
+        out.writeInt(classId);
+        out.writeInt(stackFrame.getLineNumber());
+
+        return id;
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java b/dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java
new file mode 100644
index 0000000..c20cc32
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system.profiler;
+
+import dalvik.system.VMStack;
+import java.util.Arrays;
+
+class DalvikThreadSampler implements ThreadSampler {
+
+    private int depth;
+
+    /**
+     * Reusable storage for sampling sized for the specified depth.
+     */
+    private StackTraceElement[][] mutableStackTraceElements;
+
+    @Override public void setDepth(int depth) {
+        this.depth = depth;
+        this.mutableStackTraceElements = new StackTraceElement[depth+1][];
+        for (int i = 1; i < mutableStackTraceElements.length; i++) {
+            this.mutableStackTraceElements[i] = new StackTraceElement[i];
+        }
+    }
+
+    @Override public StackTraceElement[] getStackTrace(Thread thread) {
+        int count = VMStack.fillStackTraceElements(thread, mutableStackTraceElements[depth]);
+        if (count == 0) {
+            return null;
+        }
+        if (count < depth) {
+            System.arraycopy(mutableStackTraceElements[depth], 0,
+                             mutableStackTraceElements[count], 0,
+                             count);
+        }
+        return mutableStackTraceElements[count];
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java b/dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java
new file mode 100644
index 0000000..46c443d
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system.profiler;
+
+import java.io.BufferedInputStream;
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Run on device with:
+ * adb shell dalvikvm 'dalvik.system.profiler.HprofBinaryToAscii'
+ *
+ * Run on host with:
+ * java -classpath out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar
+ */
+public final class HprofBinaryToAscii {
+
+    /**
+     * Main entry point for HprofBinaryToAscii command line tool
+     */
+    public static void main(String[] args) {
+        System.exit(convert(args) ? 0 : 1);
+    }
+
+    /**
+     * Reads single file from arguments and attempts to read it as
+     * either a binary hprof file or a version with a text header.
+     */
+    private static boolean convert(String[] args) {
+
+        if (args.length != 1) {
+            usage("binary hprof file argument expected");
+            return false;
+        }
+        File file = new File(args[0]);
+        if (!file.exists()) {
+            usage("file " + file + " does not exist");
+            return false;
+        }
+
+        if (startsWithMagic(file)) {
+            HprofData hprofData;
+            try {
+                hprofData = readHprof(file);
+            } catch (IOException e) {
+                System.out.println("Problem reading binary hprof data from "
+                                   + file + ": " + e.getMessage());
+                return false;
+            }
+            return write(hprofData);
+        }
+
+        HprofData hprofData;
+        try {
+            hprofData = readSnapshot(file);
+        } catch (IOException e) {
+            System.out.println("Problem reading snapshot containing binary hprof data from "
+                               + file + ": " + e.getMessage());
+            return false;
+        }
+        return write(hprofData);
+    }
+
+    /**
+     * Probe the start of file to see if it starts with a plausible
+     * binary hprof magic value. If so, it is returned. On any other
+     * case including unexpected errors, false is returned.
+     */
+    private static boolean startsWithMagic(File file) {
+        DataInputStream inputStream = null;
+        try {
+            inputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
+            return BinaryHprof.readMagic(inputStream) != null;
+        } catch (IOException e) {
+            return false;
+        } finally {
+            closeQuietly(inputStream);
+        }
+    }
+
+    /**
+     * Read and return an HprofData from a vanilla binary hprof file.
+     */
+    private static HprofData readHprof(File file) throws IOException {
+        InputStream inputStream = null;
+        try {
+            inputStream = new BufferedInputStream(new FileInputStream(file));
+            return read(inputStream);
+        } finally {
+            closeQuietly(inputStream);
+        }
+    }
+
+    /**
+     * Read a file looking for text header terminated by two newlines,
+     * then proceed to read binary hprof data.
+     */
+    private static HprofData readSnapshot(File file) throws IOException {
+        InputStream inputStream = null;
+        try {
+            inputStream = new BufferedInputStream(new FileInputStream(file));
+            int ch;
+            while ((ch = inputStream.read()) != -1) {
+                if (ch == '\n' && inputStream.read() == '\n') {
+                    return read(inputStream);
+                }
+            }
+            throw new EOFException("Could not find expected header");
+        } finally {
+            closeQuietly(inputStream);
+        }
+    }
+
+    /**
+     * Read binary hprof data from the provided input stream and
+     * return the HprofData object.
+     */
+    private static HprofData read(InputStream inputStream) throws IOException {
+        BinaryHprofReader reader = new BinaryHprofReader(inputStream);
+        reader.setStrict(false);
+        reader.read();
+        return reader.getHprofData();
+    }
+
+    /**
+     * From IoUtils.closeQuietly but replicated for open source
+     * version.
+     */
+    private static void closeQuietly(Closeable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException ignored) {
+            }
+        }
+    }
+
+    /**
+     * Write text verion of hprof data to standard output. Returns
+     * false on error.
+     */
+    private static boolean write(HprofData hprofData) {
+        try {
+            AsciiHprofWriter.write(hprofData, System.out);
+        } catch (IOException e) {
+            System.out.println("Problem writing ASCII hprof data: " + e.getMessage());
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Prints usage error but does not exit.
+     */
+    private static void usage(String error) {
+        System.out.print("ERROR: ");
+        System.out.println(error);
+        System.out.println();
+        System.out.println("usage: HprofBinaryToAscii <binary-hprof-file>");
+        System.out.println();
+        System.out.println("Reads a binary hprof file and print it in ASCII format");
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/HprofData.java b/dalvik/src/main/java/dalvik/system/profiler/HprofData.java
new file mode 100644
index 0000000..7b44bb9
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/HprofData.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system.profiler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents sampling profiler data. Can be converted to ASCII or
+ * binary hprof-style output using {@link AsciiHprofWriter} or
+ * {@link BinaryHprofWriter}.
+ * <p>
+ * The data includes:
+ * <ul>
+ * <li>the start time of the last sampling period
+ * <li>the history of thread start and end events
+ * <li>stack traces with frequency counts
+ * <ul>
+ */
+public final class HprofData {
+
+    public static enum ThreadEventType { START, END };
+
+    /**
+     * ThreadEvent represents thread creation and death events for
+     * reporting. It provides a record of the thread and thread group
+     * names for tying samples back to their source thread.
+     */
+    public static final class ThreadEvent {
+
+        public final ThreadEventType type;
+        public final int objectId;
+        public final int threadId;
+        public final String threadName;
+        public final String groupName;
+        public final String parentGroupName;
+
+        public static ThreadEvent start(int objectId, int threadId, String threadName,
+                                        String groupName, String parentGroupName) {
+            return new ThreadEvent(ThreadEventType.START, objectId, threadId,
+                                   threadName, groupName, parentGroupName);
+        }
+
+        public static ThreadEvent end(int threadId) {
+            return new ThreadEvent(ThreadEventType.END, threadId);
+        }
+
+        private ThreadEvent(ThreadEventType type, int objectId, int threadId,
+                            String threadName, String groupName, String parentGroupName) {
+            if (threadName == null) {
+                throw new NullPointerException("threadName == null");
+            }
+            this.type = ThreadEventType.START;
+            this.objectId = objectId;
+            this.threadId = threadId;
+            this.threadName = threadName;
+            this.groupName = groupName;
+            this.parentGroupName = parentGroupName;
+        }
+
+        private ThreadEvent(ThreadEventType type, int threadId) {
+            this.type = ThreadEventType.END;
+            this.objectId = -1;
+            this.threadId = threadId;
+            this.threadName = null;
+            this.groupName = null;
+            this.parentGroupName = null;
+        }
+
+        @Override public int hashCode() {
+            int result = 17;
+            result = 31 * result + objectId;
+            result = 31 * result + threadId;
+            result = 31 * result + hashCode(threadName);
+            result = 31 * result + hashCode(groupName);
+            result = 31 * result + hashCode(parentGroupName);
+            return result;
+        }
+
+        private static int hashCode(Object o) {
+            return (o == null) ? 0 : o.hashCode();
+        }
+
+        @Override public boolean equals(Object o) {
+            if (!(o instanceof ThreadEvent)) {
+                return false;
+            }
+            ThreadEvent event = (ThreadEvent) o;
+            return (this.type == event.type
+                    && this.objectId == event.objectId
+                    && this.threadId == event.threadId
+                    && equal(this.threadName, event.threadName)
+                    && equal(this.groupName, event.groupName)
+                    && equal(this.parentGroupName, event.parentGroupName));
+        }
+
+        private static boolean equal(Object a, Object b) {
+            return a == b || (a != null && a.equals(b));
+        }
+
+        @Override public String toString() {
+            switch (type) {
+                case START:
+                    return String.format(
+                            "THREAD START (obj=%d, id = %d, name=\"%s\", group=\"%s\")",
+                            objectId, threadId, threadName, groupName);
+                case END:
+                    return String.format("THREAD END (id = %d)", threadId);
+            }
+            throw new IllegalStateException(type.toString());
+        }
+    }
+
+    /**
+     * A unique stack trace for a specific thread.
+     */
+    public static final class StackTrace {
+
+        public final int stackTraceId;
+        int threadId;
+        StackTraceElement[] stackFrames;
+
+        StackTrace() {
+            this.stackTraceId = -1;
+        }
+
+        public StackTrace(int stackTraceId, int threadId, StackTraceElement[] stackFrames) {
+            if (stackFrames == null) {
+                throw new NullPointerException("stackFrames == null");
+            }
+            this.stackTraceId = stackTraceId;
+            this.threadId = threadId;
+            this.stackFrames = stackFrames;
+        }
+
+        public int getThreadId() {
+            return threadId;
+        }
+
+        public StackTraceElement[] getStackFrames() {
+            return stackFrames;
+        }
+
+        @Override public int hashCode() {
+            int result = 17;
+            result = 31 * result + threadId;
+            result = 31 * result + Arrays.hashCode(stackFrames);
+            return result;
+        }
+
+        @Override public boolean equals(Object o) {
+            if (!(o instanceof StackTrace)) {
+                return false;
+            }
+            StackTrace s = (StackTrace) o;
+            return threadId == s.threadId && Arrays.equals(stackFrames, s.stackFrames);
+        }
+
+        @Override public String toString() {
+            StringBuilder frames = new StringBuilder();
+            if (stackFrames.length > 0) {
+                frames.append('\n');
+                for (StackTraceElement stackFrame : stackFrames) {
+                    frames.append("\t at ");
+                    frames.append(stackFrame);
+                    frames.append('\n');
+                }
+            } else {
+                frames.append("<empty>");
+            }
+            return "StackTrace[stackTraceId=" + stackTraceId
+                    + ", threadId=" + threadId
+                    + ", frames=" + frames + "]";
+
+        }
+    }
+
+    /**
+     * A read only container combining a stack trace with its frequency.
+     */
+    public static final class Sample {
+
+        public final StackTrace stackTrace;
+        public final int count;
+
+        private Sample(StackTrace stackTrace, int count) {
+            if (stackTrace == null) {
+                throw new NullPointerException("stackTrace == null");
+            }
+            if (count < 0) {
+                throw new IllegalArgumentException("count < 0:" + count);
+            }
+            this.stackTrace = stackTrace;
+            this.count = count;
+        }
+
+        @Override public int hashCode() {
+            int result = 17;
+            result = 31 * result + stackTrace.hashCode();
+            result = 31 * result + count;
+            return result;
+        }
+
+        @Override public boolean equals(Object o) {
+            if (!(o instanceof Sample)) {
+                return false;
+            }
+            Sample s = (Sample) o;
+            return count == s.count && stackTrace.equals(s.stackTrace);
+        }
+
+        @Override public String toString() {
+            return "Sample[count=" + count + " " + stackTrace + "]";
+        }
+
+    }
+
+    /**
+     * Start of last sampling period.
+     */
+    private long startMillis;
+
+    /**
+     * CONTROL_SETTINGS flags
+     */
+    private int flags;
+
+    /**
+     * stack sampling depth
+     */
+    private int depth;
+
+    /**
+     * List of thread creation and death events.
+     */
+    private final List<ThreadEvent> threadHistory = new ArrayList<ThreadEvent>();
+
+    /**
+     * Map of thread id to a start ThreadEvent
+     */
+    private final Map<Integer, ThreadEvent> threadIdToThreadEvent
+            = new HashMap<Integer, ThreadEvent>();
+
+    /**
+     * Map of stack traces to a mutable sample count. The map is
+     * provided by the creator of the HprofData so only have
+     * mutable access to the int[] cells that contain the sample
+     * count. Only an unmodifiable iterator view is available to
+     * users of the HprofData.
+     */
+    private final Map<HprofData.StackTrace, int[]> stackTraces;
+
+    public HprofData(Map<StackTrace, int[]> stackTraces) {
+        if (stackTraces == null) {
+            throw new NullPointerException("stackTraces == null");
+        }
+        this.stackTraces = stackTraces;
+    }
+
+    /**
+     * The start time in milliseconds of the last profiling period.
+     */
+    public long getStartMillis() {
+        return startMillis;
+    }
+
+    /**
+     * Set the time for the start of the current sampling period.
+     */
+    public void setStartMillis(long startMillis) {
+        this.startMillis = startMillis;
+    }
+
+    /**
+     * Get the {@link BinaryHprof.ControlSettings} flags
+     */
+    public int getFlags() {
+        return flags;
+    }
+
+    /**
+     * Set the {@link BinaryHprof.ControlSettings} flags
+     */
+    public void setFlags(int flags) {
+        this.flags = flags;
+    }
+
+    /**
+     * Get the stack sampling depth
+     */
+    public int getDepth() {
+        return depth;
+    }
+
+    /**
+     * Set the stack sampling depth
+     */
+    public void setDepth(int depth) {
+        this.depth = depth;
+    }
+
+    /**
+     * Return an unmodifiable history of start and end thread events.
+     */
+    public List<ThreadEvent> getThreadHistory() {
+        return Collections.unmodifiableList(threadHistory);
+    }
+
+    /**
+     * Return a new set containing the current sample data.
+     */
+    public Set<Sample> getSamples() {
+        Set<Sample> samples = new HashSet<Sample>(stackTraces.size());
+        for (Entry<StackTrace, int[]> e : stackTraces.entrySet()) {
+            StackTrace stackTrace = e.getKey();
+            int countCell[] = e.getValue();
+            int count = countCell[0];
+            Sample sample = new Sample(stackTrace, count);
+            samples.add(sample);
+        }
+        return samples;
+    }
+
+    /**
+     * Record an event in the thread history.
+     */
+    public void addThreadEvent(ThreadEvent event) {
+        if (event == null) {
+            throw new NullPointerException("event == null");
+        }
+        ThreadEvent old = threadIdToThreadEvent.put(event.threadId, event);
+        switch (event.type) {
+            case START:
+                if (old != null) {
+                    throw new IllegalArgumentException("ThreadEvent already registered for id "
+                                                       + event.threadId);
+                }
+                break;
+            case END:
+                // Do not assert that the END_THREAD matches a
+                // START_THREAD unless in strict mode. While thhis
+                // hold true in the binary hprof BinaryHprofWriter
+                // produces, it is not true of hprof files created
+                // by the RI. However, if there is an event
+                // already registed for a thread id, it should be
+                // the matching start, not a duplicate end.
+                if (old != null && old.type == ThreadEventType.END) {
+                    throw new IllegalArgumentException("Duplicate ThreadEvent.end for id "
+                                                       + event.threadId);
+                }
+                break;
+        }
+        threadHistory.add(event);
+    }
+
+    /**
+     * Record an stack trace and an associated int[] cell of
+     * sample cound for the stack trace. The caller is allowed
+     * retain a pointer to the cell to update the count. The
+     * SamplingProfiler intentionally does not present a mutable
+     * view of the count.
+     */
+    public void addStackTrace(StackTrace stackTrace, int[] countCell) {
+        if (!threadIdToThreadEvent.containsKey(stackTrace.threadId)) {
+            throw new IllegalArgumentException("Unknown thread id " + stackTrace.threadId);
+        }
+        int[] old = stackTraces.put(stackTrace, countCell);
+        if (old != null) {
+            throw new IllegalArgumentException("StackTrace already registered for id "
+                                               + stackTrace.stackTraceId + ":\n" + stackTrace);
+        }
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/MalformedHprofException.java b/dalvik/src/main/java/dalvik/system/profiler/MalformedHprofException.java
new file mode 100644
index 0000000..b4097b2
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/MalformedHprofException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system.profiler;
+
+import java.io.IOException;
+
+public final class MalformedHprofException extends IOException {
+
+    private static final long serialVersionUID = 8558990237047894213L;
+
+    MalformedHprofException(String message) {
+        super(message);
+    }
+    MalformedHprofException(String message, Throwable cause) {
+        super(message, cause);
+    }
+    MalformedHprofException(Throwable cause) {
+        super(cause);
+    }
+}
+
diff --git a/dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java b/dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java
new file mode 100644
index 0000000..96e0b27
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system.profiler;
+
+import java.util.Arrays;
+
+/**
+ * ThreadSampler implementation that only uses Thread.getStackTrace()
+ * and therefore is portable.
+ */
+class PortableThreadSampler implements ThreadSampler {
+
+    private int depth;
+
+    @Override public void setDepth(int depth) {
+        this.depth = depth;
+    }
+
+    @Override public StackTraceElement[] getStackTrace(Thread thread) {
+        StackTraceElement[] stackFrames = thread.getStackTrace();
+        if (stackFrames.length == 0) {
+            return null;
+        }
+        if (stackFrames.length > depth) {
+            stackFrames = Arrays.copyOfRange(stackFrames, 0, depth);
+        }
+        return stackFrames;
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java b/dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java
new file mode 100644
index 0000000..744977c
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system.profiler;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * A sampling profiler. It currently is implemented without any
+ * virtual machine support, relying solely on {@code
+ * Thread.getStackTrace} to collect samples. As such, the overhead is
+ * higher than a native approach and it does not provide insight into
+ * where time is spent within native code, but it can still provide
+ * useful insight into where a program is spending time.
+ *
+ * <h3>Usage Example</h3>
+ *
+ * The following example shows how to use the {@code
+ * SamplingProfiler}. It samples the current thread's stack to a depth
+ * of 12 stack frame elements over two different measurement periods
+ * with samples taken every 100 milliseconds. In then prints the
+ * results in hprof format to the standard output.
+ *
+ * <pre> {@code
+ * ThreadSet threadSet = SamplingProfiler.newArrayThreadSet(Thread.currentThread());
+ * SamplingProfiler profiler = new SamplingProfiler(12, threadSet);
+ * profiler.start(100);
+ * // period of measurement
+ * profiler.stop();
+ * // period of non-measurement
+ * profiler.start(100);
+ * // another period of measurement
+ * profiler.stop();
+ * profiler.shutdown();
+ * AsciiHprofWriter.write(profiler.getHprofData(), System.out);
+ * }</pre>
+ */
+public final class SamplingProfiler {
+
+    /**
+     * Map of stack traces to a mutable sample count.
+     */
+    private final Map<HprofData.StackTrace, int[]> stackTraces
+            = new HashMap<HprofData.StackTrace, int[]>();
+
+    /**
+     * Data collected by the sampling profiler
+     */
+    private final HprofData hprofData = new HprofData(stackTraces);
+
+    /**
+     * Timer that is used for the lifetime of the profiler
+     */
+    private final Timer timer = new Timer("SamplingProfiler", true);
+
+    /**
+     * A sampler is created every time profiling starts and cleared
+     * every time profiling stops because once a {@code TimerTask} is
+     * canceled it cannot be reused.
+     */
+    private Sampler sampler;
+
+    /**
+     * The maximum number of {@code StackTraceElements} to retain in
+     * each stack.
+     */
+    private final int depth;
+
+    /**
+     * The {@code ThreadSet} that identifies which threads to sample.
+     */
+    private final ThreadSet threadSet;
+
+    /*
+     *  Real hprof output examples don't start the thread and trace
+     *  identifiers at one but seem to start at these arbitrary
+     *  constants. It certainly seems useful to have relatively unique
+     *  identifiers when manual searching hprof output.
+     */
+    private int nextThreadId = 200001;
+    private int nextStackTraceId = 300001;
+    private int nextObjectId = 1;
+
+    /**
+     * The threads currently known to the profiler for detecting
+     * thread start and end events.
+     */
+    private Thread[] currentThreads = new Thread[0];
+
+    /**
+     * Map of currently active threads to their identifiers. When
+     * threads disappear they are removed and only referenced by their
+     * identifiers to prevent retaining garbage threads.
+     */
+    private final Map<Thread, Integer> threadIds = new HashMap<Thread, Integer>();
+
+    /**
+     * Mutable {@code StackTrace} that is used for probing the {@link
+     * #stackTraces stackTraces} map without allocating a {@code
+     * StackTrace}. If {@link #addStackTrace addStackTrace} needs to
+     * be thread safe, have a single mutable instance would need to be
+     * reconsidered.
+     */
+    private final HprofData.StackTrace mutableStackTrace = new HprofData.StackTrace();
+
+    /**
+     * The {@code ThreadSampler} is used to produce a {@code
+     * StackTraceElement} array for a given thread. The array is
+     * expected to be {@link #depth depth} or less in length.
+     */
+    private final ThreadSampler threadSampler;
+
+    /**
+     * Create a sampling profiler that collects stacks with the
+     * specified depth from the threads specified by the specified
+     * thread collector.
+     *
+     * @param depth The maximum stack depth to retain for each sample
+     * similar to the hprof option of the same name. Any stack deeper
+     * than this will be truncated to this depth. A good starting
+     * value is 4 although it is not uncommon to need to raise this to
+     * get enough context to understand program behavior. While
+     * programs with extensive recursion may require a high value for
+     * depth, simply passing in a value for Integer.MAX_VALUE is not
+     * advised because of the significant memory need to retain such
+     * stacks and runtime overhead to compare stacks.
+     *
+     * @param threadSet The thread set specifies which threads to
+     * sample. In a general purpose program, all threads typically
+     * should be sample with a ThreadSet such as provided by {@link
+     * #newThreadGroupThreadSet newThreadGroupThreadSet}. For a
+     * benchmark a fixed set such as provided by {@link
+     * #newArrayThreadSet newArrayThreadSet} can reduce the overhead
+     * of profiling.
+     */
+    public SamplingProfiler(int depth, ThreadSet threadSet) {
+        this.depth = depth;
+        this.threadSet = threadSet;
+        this.threadSampler = findDefaultThreadSampler();
+        threadSampler.setDepth(depth);
+        hprofData.setFlags(BinaryHprof.ControlSettings.CPU_SAMPLING.bitmask);
+        hprofData.setDepth(depth);
+    }
+
+    private static ThreadSampler findDefaultThreadSampler() {
+        if ("Dalvik Core Library".equals(System.getProperty("java.specification.name"))) {
+            String className = "dalvik.system.profiler.DalvikThreadSampler";
+            try {
+                return (ThreadSampler) Class.forName(className).newInstance();
+            } catch (Exception e) {
+                System.out.println("Problem creating " + className + ": " + e);
+            }
+        }
+        return new PortableThreadSampler();
+    }
+
+    /**
+     * A ThreadSet specifies the set of threads to sample.
+     */
+    public static interface ThreadSet {
+        /**
+         * Returns an array containing the threads to be sampled. The
+         * array may be longer than the number of threads to be
+         * sampled, in which case the extra elements must be null.
+         */
+        public Thread[] threads();
+    }
+
+    /**
+     * Returns a ThreadSet for a fixed set of threads that will not
+     * vary at runtime. This has less overhead than a dynamically
+     * calculated set, such as {@link #newThreadGroupThreadSet}, which has
+     * to enumerate the threads each time profiler wants to collect
+     * samples.
+     */
+    public static ThreadSet newArrayThreadSet(Thread... threads) {
+        return new ArrayThreadSet(threads);
+    }
+
+    /**
+     * An ArrayThreadSet samples a fixed set of threads that does not
+     * vary over the life of the profiler.
+     */
+    private static class ArrayThreadSet implements ThreadSet {
+        private final Thread[] threads;
+        public ArrayThreadSet(Thread... threads) {
+            if (threads == null) {
+                throw new NullPointerException("threads == null");
+            }
+            this.threads = threads;
+        }
+        public Thread[] threads() {
+            return threads;
+        }
+    }
+
+    /**
+     * Returns a ThreadSet that is dynamically computed based on the
+     * threads found in the specified ThreadGroup and that
+     * ThreadGroup's children.
+     */
+    public static ThreadSet newThreadGroupThreadSet(ThreadGroup threadGroup) {
+        return new ThreadGroupThreadSet(threadGroup);
+    }
+
+    /**
+     * An ThreadGroupThreadSet sample the threads from the specified
+     * ThreadGroup and the ThreadGroup's children
+     */
+    private static class ThreadGroupThreadSet implements ThreadSet {
+        private final ThreadGroup threadGroup;
+        private Thread[] threads;
+        private int lastThread;
+
+        public ThreadGroupThreadSet(ThreadGroup threadGroup) {
+            if (threadGroup == null) {
+                throw new NullPointerException("threadGroup == null");
+            }
+            this.threadGroup = threadGroup;
+            resize();
+        }
+
+        private void resize() {
+            int count = threadGroup.activeCount();
+            // we can only tell if we had enough room for all active
+            // threads if we actually are larger than the the number of
+            // active threads. making it larger also leaves us room to
+            // tolerate additional threads without resizing.
+            threads = new Thread[count*2];
+            lastThread = 0;
+        }
+
+        public Thread[] threads() {
+            int threadCount;
+            while (true) {
+                threadCount = threadGroup.enumerate(threads);
+                if (threadCount == threads.length) {
+                    resize();
+                } else {
+                    break;
+                }
+            }
+            if (threadCount < lastThread) {
+                // avoid retaining pointers to threads that have ended
+                Arrays.fill(threads, threadCount, lastThread, null);
+            }
+            lastThread = threadCount;
+            return threads;
+        }
+    }
+
+    /**
+     * Starts profiler sampling at the specified rate.
+     *
+     * @param interval The number of milliseconds between samples
+     */
+    public void start(int interval) {
+        if (interval < 1) {
+            throw new IllegalArgumentException("interval < 1");
+        }
+        if (sampler != null) {
+            throw new IllegalStateException("profiling already started");
+        }
+        sampler = new Sampler();
+        hprofData.setStartMillis(System.currentTimeMillis());
+        timer.scheduleAtFixedRate(sampler, 0, interval);
+    }
+
+    /**
+     * Stops profiler sampling. It can be restarted with {@link
+     * #start(int)} to continue sampling.
+     */
+    public void stop() {
+        if (sampler == null) {
+            return;
+        }
+        synchronized(sampler) {
+            sampler.stop = true;
+            while (!sampler.stopped) {
+                try {
+                    sampler.wait();
+                } catch (InterruptedException ignored) {
+                }
+            }
+        }
+        sampler = null;
+    }
+
+    /**
+     * Shuts down profiling after which it can not be restarted. It is
+     * important to shut down profiling when done to free resources
+     * used by the profiler. Shutting down the profiler also stops the
+     * profiling if that has not already been done.
+     */
+    public void shutdown() {
+        stop();
+        timer.cancel();
+    }
+
+    /**
+     * Returns the hprof data accumulated by the profiler since it was
+     * created. The profiler needs to be stopped, but not necessarily
+     * shut down, in order to access the data. If the profiler is
+     * restarted, there is no thread safe way to access the data.
+     */
+    public HprofData getHprofData() {
+        if (sampler != null) {
+            throw new IllegalStateException("cannot access hprof data while sampling");
+        }
+        return hprofData;
+    }
+
+    /**
+     * The Sampler does the real work of the profiler.
+     *
+     * At every sample time, it asks the thread set for the set
+     * of threads to sample. It maintains a history of thread creation
+     * and death events based on changes observed to the threads
+     * returned by the {@code ThreadSet}.
+     *
+     * For each thread to be sampled, a stack is collected and used to
+     * update the set of collected samples. Stacks are truncated to a
+     * maximum depth. There is no way to tell if a stack has been truncated.
+     */
+    private class Sampler extends TimerTask {
+
+        private boolean stop;
+        private boolean stopped;
+
+        private Thread timerThread;
+
+        public void run() {
+            synchronized(this) {
+                if (stop) {
+                    cancel();
+                    stopped = true;
+                    notifyAll();
+                    return;
+                }
+            }
+
+            if (timerThread == null) {
+                timerThread = Thread.currentThread();
+            }
+
+            // process thread creation and death first so that we
+            // assign thread ids to any new threads before allocating
+            // new stacks for them
+            Thread[] newThreads = threadSet.threads();
+            if (!Arrays.equals(currentThreads, newThreads)) {
+                updateThreadHistory(currentThreads, newThreads);
+                currentThreads = newThreads.clone();
+            }
+
+            for (Thread thread : currentThreads) {
+                if (thread == null) {
+                    break;
+                }
+                if (thread == timerThread) {
+                    continue;
+                }
+
+                StackTraceElement[] stackFrames = threadSampler.getStackTrace(thread);
+                if (stackFrames == null) {
+                    continue;
+                }
+                recordStackTrace(thread, stackFrames);
+            }
+        }
+
+        /**
+         * Record a new stack trace. The thread should have been
+         * previously registered with addStartThread.
+         */
+        private void recordStackTrace(Thread thread, StackTraceElement[] stackFrames) {
+            Integer threadId = threadIds.get(thread);
+            if (threadId == null) {
+                throw new IllegalArgumentException("Unknown thread " + thread);
+            }
+            mutableStackTrace.threadId = threadId;
+            mutableStackTrace.stackFrames = stackFrames;
+
+            int[] countCell = stackTraces.get(mutableStackTrace);
+            if (countCell == null) {
+                countCell = new int[1];
+                // cloned because the ThreadSampler may reuse the array
+                StackTraceElement[] stackFramesCopy = stackFrames.clone();
+                HprofData.StackTrace stackTrace
+                        = new HprofData.StackTrace(nextStackTraceId++, threadId, stackFramesCopy);
+                hprofData.addStackTrace(stackTrace, countCell);
+            }
+            countCell[0]++;
+        }
+
+        private void updateThreadHistory(Thread[] oldThreads, Thread[] newThreads) {
+            // thread start/stop shouldn't happen too often and
+            // these aren't too big, so hopefully this approach
+            // won't be too slow...
+            Set<Thread> n = new HashSet<Thread>(Arrays.asList(newThreads));
+            Set<Thread> o = new HashSet<Thread>(Arrays.asList(oldThreads));
+
+            // added = new-old
+            Set<Thread> added = new HashSet<Thread>(n);
+            added.removeAll(o);
+
+            // removed = old-new
+            Set<Thread> removed = new HashSet<Thread>(o);
+            removed.removeAll(n);
+
+            for (Thread thread : added) {
+                if (thread == null) {
+                    continue;
+                }
+                if (thread == timerThread) {
+                    continue;
+                }
+                addStartThread(thread);
+            }
+            for (Thread thread : removed) {
+                if (thread == null) {
+                    continue;
+                }
+                if (thread == timerThread) {
+                    continue;
+                }
+                addEndThread(thread);
+            }
+        }
+
+        /**
+         * Record that a newly noticed thread.
+         */
+        private void addStartThread(Thread thread) {
+            if (thread == null) {
+                throw new NullPointerException("thread == null");
+            }
+            int threadId = nextThreadId++;
+            Integer old = threadIds.put(thread, threadId);
+            if (old != null) {
+                throw new IllegalArgumentException("Thread already registered as " + old);
+            }
+
+            String threadName = thread.getName();
+            // group will become null when thread is terminated
+            ThreadGroup group = thread.getThreadGroup();
+            String groupName = group == null ? null : group.getName();
+            ThreadGroup parentGroup = group == null ? null : group.getParent();
+            String parentGroupName = parentGroup == null ? null : parentGroup.getName();
+
+            HprofData.ThreadEvent event
+                    = HprofData.ThreadEvent.start(nextObjectId++, threadId,
+                                                  threadName, groupName, parentGroupName);
+            hprofData.addThreadEvent(event);
+        }
+
+        /**
+         * Record that a thread has disappeared.
+         */
+        private void addEndThread(Thread thread) {
+            if (thread == null) {
+                throw new NullPointerException("thread == null");
+            }
+            Integer threadId = threadIds.remove(thread);
+            if (threadId == null) {
+                throw new IllegalArgumentException("Unknown thread " + thread);
+            }
+            HprofData.ThreadEvent event = HprofData.ThreadEvent.end(threadId);
+            hprofData.addThreadEvent(event);
+        }
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java b/dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java
new file mode 100644
index 0000000..104b16d
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system.profiler;
+
+import java.io.IOException;
+
+/**
+ * The {@code ThreadSampler} interfaces allows a profiler to choose
+ * between portable and VM specific implementations of thread
+ * sampling.
+ */
+public interface ThreadSampler {
+
+    /**
+     * Used to specify the maximum stack depth to collect.
+     */
+    public void setDepth(int depth);
+
+    /**
+     * Return a stack trace for the current thread limited by the
+     * maximum depth specified by {@link #setDepth setDepth}. May
+     * return null if no sample is availble for the thread, which may
+     * happen in cases such as thread termination. The resulting array
+     * should be copied before the next call to {@code getStackTrace}
+     * if the caller wants to use the results, since the {@code
+     * ThreadSampler} may reuse the array. Note that the elements
+     * themselves are immutable and do not need to be copied.
+     */
+    public StackTraceElement[] getStackTrace(Thread thread);
+}
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java b/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java
new file mode 100644
index 0000000..7b46d2e
--- /dev/null
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 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 im