merge from eclair
diff --git a/buildspec.mk.default b/buildspec.mk.default
index 659b9f6..6303efc 100644
--- a/buildspec.mk.default
+++ b/buildspec.mk.default
@@ -21,6 +21,30 @@
 # "buildspec.mk" should never be checked in to source control.
 ######################################################################
 
+# Choose a product to build for.  Look in the products directory for ones
+# that work.
+ifndef TARGET_PRODUCT
+#TARGET_PRODUCT:=generic
+endif
+
+# Choose a variant to build.  If you don't pick one, the default is eng.
+# User is what we ship.  Userdebug is that, with a few flags turned on
+# for debugging.  Eng has lots of extra tools for development.
+ifndef TARGET_BUILD_VARIANT
+#TARGET_BUILD_VARIANT:=user
+#TARGET_BUILD_VARIANT:=userdebug
+#TARGET_BUILD_VARIANT:=eng
+endif
+
+# Choose additional targets to always install, even when building
+# minimal targets like "make droid".  This takes simple target names
+# like "Browser" or "MyApp", the names used by LOCAL_MODULE or
+# LOCAL_PACKAGE_NAME.  Modules listed here will always be installed in
+# /system, even if they'd usually go in /data.
+ifndef CUSTOM_MODULES
+#CUSTOM_MODULES:=
+endif
+
 # Uncomment this if you want the simulator, otherwise, build for arm
 ifndef TARGET_SIMULATOR
 #TARGET_SIMULATOR:=true
@@ -53,21 +77,6 @@
 #HOST_CUSTOM_DEBUG_CFLAGS:=
 #TARGET_CUSTOM_DEBUG_CFLAGS:=
 
-# Choose a product to build for.  Look in the products directory for ones
-# that work.
-ifndef TARGET_PRODUCT
-#TARGET_PRODUCT:=generic
-endif
-
-# Choose additional targets to always install, even when building
-# minimal targets like "make droid".  This takes simple target names
-# like "Browser" or "MyApp", the names used by LOCAL_MODULE or
-# LOCAL_PACKAGE_NAME.  Modules listed here will always be installed in
-# /system, even if they'd usually go in /data.
-ifndef CUSTOM_MODULES
-#CUSTOM_MODULES:=
-endif
-
 # Choose additional locales, like "en_US" or "it_IT", to add to any
 # built product.  Any locales that appear in CUSTOM_LOCALES but not in
 # the locale list for the selected product will be added to the end
@@ -91,10 +100,16 @@
 #NO_FALLBACK_FONT:=true
 endif
 
-# To enabled instrumentation in webcore based apps like gmail and
+# To enable instrumentation in webcore based apps like gmail and
 # the browser, define WEBCORE_INSTRUMENTATION:=true
+ifndef WEBCORE_INSTRUMENTATION
 #WEBCORE_INSTRUMENTATION:=true
-#endif
+endif
+
+# To enable SVG in webcore define ENABLE_SVG:=true
+ifndef ENABLE_SVG
+#ENABLE_SVG:=true
+endif
 
 # when the build system changes such that this file must be updated, this
 # variable will be changed.  After you have modified this file with the new
diff --git a/cleanspec.mk b/cleanspec.mk
index c244c46..d1401fb 100644
--- a/cleanspec.mk
+++ b/cleanspec.mk
@@ -18,7 +18,7 @@
 # WHEN DOING SO, DELETE ANY "add-clean-step" ENTRIES THAT HAVE PILED UP.
 # **********************************************************************
 #
-INTERNAL_CLEAN_BUILD_VERSION := 2
+INTERNAL_CLEAN_BUILD_VERSION := 3
 #
 # ***********************************************************************
 # Do not touch INTERNAL_CLEAN_BUILD_VERSION if you've added a clean step!
@@ -31,7 +31,7 @@
 #
 # E.g.:
 #     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
-#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/external/zlib/)
+#     $(call add-clean-step, rm -rf $(PRODUCT_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.
@@ -54,38 +54,10 @@
 #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
 #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
 #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)
 
-$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/etc/NOTICE.html)
-# Remove generated java files after CL 126153
-$(call add-clean-step, find $(OUT_DIR) -type f -name "*.java" -print0 | xargs -0 rm -f)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sapphire/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates/led)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/mountd)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/mountd.conf)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Browser_intermediates)
-$(call add-clean-step, rm -f vendor/google/apps/Talk/res/drawable/%*)
-$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/SHARED_LIBRARIES/libandroid_runtime_intermediates/android_os_NetStat.o)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libjni_andpyime_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/share)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libwebcore_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PinyinIME_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.inputmethod.pinyin.lib_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PinyinIMEGoogleService_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.inputmethod.pinyin.lib_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PinyinIMEGoogleService_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj)
-$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/tcpdump)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/location)
-
-$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/SHARED_LIBRARIES/lib?camera_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/STATIC_LIBRARIES/lib?camera_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libwebcore_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/Silence.ogg)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/notifications/Silence.ogg)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/core/Makefile b/core/Makefile
index 2c4d133..e704748 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -118,6 +118,7 @@
 			PRODUCT_BRAND="$(PRODUCT_BRAND)" \
 			PRODUCT_DEFAULT_LANGUAGE="$(call default-locale-language,$(PRODUCT_LOCALES))" \
 			PRODUCT_DEFAULT_REGION="$(call default-locale-region,$(PRODUCT_LOCALES))" \
+			PRODUCT_DEFAULT_WIFI_CHANNELS="$(PRODUCT_DEFAULT_WIFI_CHANNELS)" \
 			PRODUCT_MODEL="$(PRODUCT_MODEL)" \
 			PRODUCT_MANUFACTURER="$(PRODUCT_MANUFACTURER)" \
 			PRIVATE_BUILD_DESC="$(PRIVATE_BUILD_DESC)" \
@@ -299,7 +300,7 @@
 $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
 	$(call pretty,"Target boot image: $@")
 	$(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) --output $@
-	$(hide) $(call assert-max-file-size,$@,$(BOARD_BOOTIMAGE_MAX_SIZE),raw)
+	$(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE),raw)
 endif # TARGET_BOOTIMAGE_USE_EXT2
 
 else	# TARGET_NO_KERNEL
@@ -501,7 +502,7 @@
 # Recovery image
 
 # If neither TARGET_NO_KERNEL nor TARGET_NO_RECOVERY are true
-ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))
+ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY) $(BUILD_TINY_ANDROID)))
 
 INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
 
@@ -577,7 +578,7 @@
 	$(MKBOOTFS) $(TARGET_RECOVERY_ROOT_OUT) | $(MINIGZIP) > $(recovery_ramdisk)
 	$(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) --output $@
 	@echo ----- Made recovery image -------- $@
-	$(hide) $(call assert-max-file-size,$@,$(BOARD_RECOVERYIMAGE_MAX_SIZE),raw)
+	$(hide) $(call assert-max-image-size,$@,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE),raw)
 
 else
 INSTALLED_RECOVERYIMAGE_TARGET :=
@@ -662,7 +663,7 @@
 $(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP)
 	@echo "Install system fs image: $@"
 	$(copy-file-to-target)
-	$(hide) $(call assert-max-file-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_MAX_SIZE),yaffs)
+	$(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE),yaffs)
 
 systemimage: $(INSTALLED_SYSTEMIMAGE)
 
@@ -671,7 +672,7 @@
 	            | $(INTERNAL_MKUSERFS)
 	@echo "make $@: ignoring dependencies"
 	$(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE))
-	$(hide) $(call assert-max-file-size,$(INSTALLED_SYSTEMIMAGE),$(BOARD_SYSTEMIMAGE_MAX_SIZE),yaffs)
+	$(hide) $(call assert-max-image-size,$(INSTALLED_SYSTEMIMAGE),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE),yaffs)
 
 #######
 ## system tarball
@@ -708,7 +709,7 @@
     $(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)")
     @mkdir -p $(TARGET_OUT_DATA)
     $(call build-userimage-ext2-target,$(TARGET_OUT_DATA),$(INSTALLED_USERDATAIMAGE_TARGET),userdata,)
-    $(hide) $(call assert-max-file-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_MAX_SIZE),yaffs)
+    $(hide) $(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE),yaffs)
 endef
 
 else # TARGET_USERIMAGES_USE_EXT2 != true
@@ -718,7 +719,7 @@
     $(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)")
     @mkdir -p $(TARGET_OUT_DATA)
     $(hide) $(MKYAFFS2) -f $(TARGET_OUT_DATA) $(INSTALLED_USERDATAIMAGE_TARGET)
-    $(hide) $(call assert-max-file-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_MAX_SIZE),yaffs)
+    $(hide) $(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE),yaffs)
 endef
 endif # TARGET_USERIMAGES_USE_EXT2
 
@@ -813,6 +814,13 @@
 
 $(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_API_VERSION := $(RECOVERY_API_VERSION)
 
+ifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),)
+# default to common dir for device vendor
+$(BUILT_TARGET_FILES_PACKAGE): tool_extensions := $(TARGET_DEVICE_DIR)/../common
+else
+$(BUILT_TARGET_FILES_PACKAGE): tool_extensions := $(TARGET_RELEASETOOLS_EXTENSIONS)
+endif
+
 # Depending on the various images guarantees that the underlying
 # directories are up-to-date.
 $(BUILT_TARGET_FILES_PACKAGE): \
@@ -842,6 +850,9 @@
 ifdef BOARD_KERNEL_CMDLINE
 	$(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/RECOVERY/cmdline
 endif
+ifdef BOARD_KERNEL_BASE
+	$(hide) echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/RECOVERY/base
+endif
 	@# Components of the boot image
 	$(hide) mkdir -p $(zip_root)/BOOT
 	$(hide) $(call package_files-copy-root, \
@@ -856,11 +867,12 @@
 ifdef BOARD_KERNEL_CMDLINE
 	$(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline
 endif
-ifdef INSTALLED_RADIOIMAGE_TARGET
-	@# The radio image
-	$(hide) mkdir -p $(zip_root)/RADIO
-	$(hide) $(ACP) $(INSTALLED_RADIOIMAGE_TARGET) $(zip_root)/RADIO/image
+ifdef BOARD_KERNEL_BASE
+	$(hide) echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/BOOT/base
 endif
+	$(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),\
+	            mkdir -p $(zip_root)/RADIO; \
+	            $(ACP) $(t) $(zip_root)/RADIO/$(notdir $(t));)
 	@# Contents of the system image
 	$(hide) $(call package_files-copy-root, \
 		$(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM)
@@ -878,10 +890,11 @@
 	$(hide)	echo "$(PRODUCT_OTA_PUBLIC_KEYS)" > $(zip_root)/META/otakeys.txt
 	$(hide) echo "$(PRIVATE_RECOVERY_API_VERSION)" > $(zip_root)/META/recovery-api-version.txt
 	$(hide) echo "blocksize $(BOARD_FLASH_BLOCK_SIZE)" > $(zip_root)/META/imagesizes.txt
-	$(hide) echo "boot $(BOARD_BOOTIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt
-	$(hide) echo "recovery $(BOARD_RECOVERYIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt
-	$(hide) echo "system $(BOARD_SYSTEMIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt
-	$(hide) echo "userdata $(BOARD_USERDATAIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt
+	$(hide) echo "boot $(call image-size-from-data-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE))" >> $(zip_root)/META/imagesizes.txt
+	$(hide) echo "recovery $(call image-size-from-data-size,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE))" >> $(zip_root)/META/imagesizes.txt
+	$(hide) echo "system $(call image-size-from-data-size,$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))" >> $(zip_root)/META/imagesizes.txt
+	$(hide) echo "userdata $(call image-size-from-data-size,$(BOARD_USERDATAIMAGE_PARTITION_SIZE))" >> $(zip_root)/META/imagesizes.txt
+	$(hide) echo "$(tool_extensions)" > $(zip_root)/META/tool-extensions.txt
 	@# Zip everything up, preserving symlinks
 	$(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
 
@@ -904,9 +917,17 @@
 
 $(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
 
+ifeq ($(TARGET_OTA_SCRIPT_MODE),)
+# default to "auto"
+$(INTERNAL_OTA_PACKAGE_TARGET): scriptmode := auto
+else
+$(INTERNAL_OTA_PACKAGE_TARGET): scriptmode := $(TARGET_OTA_SCRIPT_MODE)
+endif
+
 $(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) otatools
 	@echo "Package OTA: $@"
 	$(hide) ./build/tools/releasetools/ota_from_target_files \
+	   -m $(scriptmode) \
 	   -p $(HOST_OUT) \
            -k $(KEY_CERT_PAIR) \
            $(BUILT_TARGET_FILES_PACKAGE) $@
@@ -1030,9 +1051,17 @@
 
 INTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 
+ifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),)
+# default to common dir for device vendor
+$(INTERNAL_UPDATE_PACKAGE_TARGET): extensions := $(TARGET_DEVICE_DIR)/../common
+else
+$(INTERNAL_UPDATE_PACKAGE_TARGET): extensions := $(TARGET_RELEASETOOLS_EXTENSIONS)
+endif
+
 $(INTERNAL_UPDATE_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) otatools
 	@echo "Package: $@"
 	$(hide) ./build/tools/releasetools/img_from_target_files \
+	   -s $(extensions) \
 	   -p $(HOST_OUT) \
 	   $(BUILT_TARGET_FILES_PACKAGE) $@
 
diff --git a/core/base_rules.mk b/core/base_rules.mk
index a6bf504..64b74e7 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -272,8 +272,10 @@
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_SOURCES := $(all_java_sources)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_OBJECTS := $(patsubst %.java,%.class,$(LOCAL_SRC_FILES))
 ifeq ($(my_prefix),TARGET_)
+ifeq ($(LOCAL_SDK_VERSION),)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_BOOTCLASSPATH := -bootclasspath $(call java-lib-files,core)
 endif
+endif
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EXTRA_JAR_ARGS := $(extra_jar_args)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASSET_DIR := $(LOCAL_ASSET_DIR)
@@ -350,7 +352,6 @@
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_POST_PROCESS_COMMAND:= $(LOCAL_POST_PROCESS_COMMAND)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_AAPT_FLAGS:= $(LOCAL_AAPT_FLAGS)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_JAVA_LIBRARIES:= $(LOCAL_JAVA_LIBRARIES)
-#TODO: add this: $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_ADDITIONAL_DEPENDENCIES:= $(LOCAL_ADDITIONAL_DEPENDENCIES)
 
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_ALL_JAVA_LIBRARIES:= $(full_java_libs)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_IS_HOST_MODULE := $(LOCAL_IS_HOST_MODULE)
diff --git a/core/binary.mk b/core/binary.mk
index ddcdc6f..4413d47 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -87,7 +87,7 @@
 ifneq ($(strip $(yacc_cpps)),)
 $(yacc_cpps): $(intermediates)/%$(LOCAL_CPP_EXTENSION): \
 		$(TOPDIR)$(LOCAL_PATH)/%.y \
-		$(lex_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+		$(lex_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(call transform-y-to-cpp,$(PRIVATE_CPP_EXTENSION))
 $(yacc_headers): $(intermediates)/%.h: $(intermediates)/%$(LOCAL_CPP_EXTENSION)
 
@@ -115,7 +115,7 @@
 $(lex_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)
 $(lex_objects): $(intermediates)/%.o: \
 		$(intermediates)/%$(LOCAL_CPP_EXTENSION) \
-		$(PRIVATE_ADDITIONAL_DEPENDENCIES) \
+		$(LOCAL_ADDITIONAL_DEPENDENCIES) \
 		$(yacc_headers)
 	$(transform-$(PRIVATE_HOST)cpp-to-o)
 endif
@@ -142,7 +142,7 @@
 ifneq ($(strip $(cpp_objects)),)
 $(cpp_objects): $(intermediates)/%.o: \
 		$(TOPDIR)$(LOCAL_PATH)/%$(LOCAL_CPP_EXTENSION) \
-		$(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+		$(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)cpp-to-o)
 -include $(cpp_objects:%.o=%.P)
 endif
@@ -159,7 +159,7 @@
 # TODO: support compiling certain generated files as arm.
 $(gen_cpp_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)
 $(gen_cpp_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)
-$(gen_cpp_objects): $(intermediates)/%.o: $(intermediates)/%$(LOCAL_CPP_EXTENSION) $(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(gen_cpp_objects): $(intermediates)/%.o: $(intermediates)/%$(LOCAL_CPP_EXTENSION) $(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)cpp-to-o)
 -include $(gen_cpp_objects:%.o=%.P)
 endif
@@ -172,7 +172,7 @@
 gen_S_objects := $(gen_S_sources:%.S=%.o)
 
 ifneq ($(strip $(gen_S_sources)),)
-$(gen_S_objects): $(intermediates)/%.o: $(intermediates)/%.S $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(gen_S_objects): $(intermediates)/%.o: $(intermediates)/%.S $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)s-to-o)
 -include $(gen_S_objects:%.o=%.P)
 endif
@@ -181,7 +181,7 @@
 gen_s_objects := $(gen_s_sources:%.s=%.o)
 
 ifneq ($(strip $(gen_s_objects)),)
-$(gen_s_objects): $(intermediates)/%.o: $(intermediates)/%.s $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(gen_s_objects): $(intermediates)/%.o: $(intermediates)/%.s $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)s-to-o-no-deps)
 -include $(gen_s_objects:%.o=%.P)
 endif
@@ -206,12 +206,29 @@
 c_objects        := $(c_arm_objects) $(c_normal_objects)
 
 ifneq ($(strip $(c_objects)),)
-$(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c $(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c $(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)c-to-o)
 -include $(c_objects:%.o=%.P)
 endif
 
 ###########################################################
+## C: Compile generated .c files to .o.
+###########################################################
+
+gen_c_sources := $(filter %.c,$(LOCAL_GENERATED_SOURCES))
+gen_c_objects := $(gen_c_sources:%.c=%.o)
+
+ifneq ($(strip $(gen_c_objects)),)
+# Compile all generated files as thumb.
+# TODO: support compiling certain generated files as arm.
+$(gen_c_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)
+$(gen_c_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)
+$(gen_c_objects): $(intermediates)/%.o: $(intermediates)/%.c $(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(transform-$(PRIVATE_HOST)c-to-o)
+-include $(gen_c_objects:%.o=%.P)
+endif
+
+###########################################################
 ## ObjC: Compile .m files to .o
 ###########################################################
 
@@ -232,7 +249,7 @@
 asm_objects_S := $(addprefix $(intermediates)/,$(asm_sources_S:.S=.o))
 
 ifneq ($(strip $(asm_objects_S)),)
-$(asm_objects_S): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.S $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(asm_objects_S): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.S $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)s-to-o)
 -include $(asm_objects_S:%.o=%.P)
 endif
@@ -241,7 +258,7 @@
 asm_objects_s := $(addprefix $(intermediates)/,$(asm_sources_s:.s=.o))
 
 ifneq ($(strip $(asm_objects_s)),)
-$(asm_objects_s): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.s $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(asm_objects_s): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.s $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)s-to-o-no-deps)
 -include $(asm_objects_s:%.o=%.P)
 endif
@@ -261,6 +278,7 @@
 	$(gen_cpp_objects) \
 	$(gen_asm_objects) \
 	$(c_objects) \
+	$(gen_c_objects) \
 	$(yacc_objects) \
 	$(lex_objects) \
 	$(addprefix $(TOPDIR)$(LOCAL_PATH)/,$(LOCAL_PREBUILT_OBJ_FILES))
diff --git a/core/build_id.mk b/core/build_id.mk
index 060c9b5..40bb35d 100644
--- a/core/build_id.mk
+++ b/core/build_id.mk
@@ -23,7 +23,7 @@
 # (like "TC1-RC5").  It must be a single word, and is
 # capitalized by convention.
 #
-BUILD_ID := Donut
+BUILD_ID := MASTER
 
 # DISPLAY_BUILD_NUMBER should only be set for development branches,
 # If set, the BUILD_NUMBER (cl) is appended to the BUILD_ID for
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 28044c4..a7eba3f 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -87,6 +87,8 @@
 LOCAL_SDK_VERSION:=
 LOCAL_NO_EMMA_INSTRUMENT:=
 LOCAL_NO_EMMA_COMPILE:=
+LOCAL_PROGUARD_ENABLED:= # '',optonly,full,custom
+LOCAL_PROGUARD_FLAGS:=
 
 # Trim MAKEFILE_LIST so that $(call my-dir) doesn't need to
 # iterate over thousands of entries every time.
diff --git a/core/combo/arch/arm/armv4t.mk b/core/combo/arch/arm/armv4t.mk
new file mode 100644
index 0000000..abc8fa2
--- /dev/null
+++ b/core/combo/arch/arm/armv4t.mk
@@ -0,0 +1,23 @@
+# Configuration for Linux on ARM.
+# Generating binaries for the ARMv4T architecture and higher
+#
+# Supporting armv4 (without thumb) does not make much sense since
+# it's mostly an obsoleted instruction set architecture (only available
+# in StrongArm and arm8). Supporting armv4 will require a lot of conditional
+# code in assembler source since the bx (branch and exchange) instruction is
+# not supported.
+#
+$(warning ARMv4t support is currently a work in progress. It does not work right now!)
+ARCH_ARM_HAVE_THUMB_SUPPORT := false
+ARCH_ARM_HAVE_THUMB_INTERWORKING := false
+ARCH_ARM_HAVE_64BIT_DATA := false
+ARCH_ARM_HAVE_HALFWORD_MULTIPLY := false
+ARCH_ARM_HAVE_CLZ := false
+ARCH_ARM_HAVE_FFS := false
+
+DEFAULT_TARGET_CPU := arm920t
+
+# Note: Hard coding the 'tune' value here is probably not ideal,
+# and a better solution should be found in the future.
+#
+arch_variant_cflags := -march=armv4t -mtune=arm920t -D__ARM_ARCH_4T__
diff --git a/core/combo/arch/arm/armv5te-vfp.mk b/core/combo/arch/arm/armv5te-vfp.mk
new file mode 100644
index 0000000..75299ac
--- /dev/null
+++ b/core/combo/arch/arm/armv5te-vfp.mk
@@ -0,0 +1,7 @@
+# At the moment, use the same settings than the one
+# for armv5te, since TARGET_ARCH_VARIANT := armv5te-vfp
+# will only be used to select an optimized VFP-capable assembly
+# interpreter loop for Dalvik.
+#
+include $(BUILD_COMBOS)/arch/arm/armv5te.mk
+
diff --git a/core/combo/arch/arm/armv5te.mk b/core/combo/arch/arm/armv5te.mk
new file mode 100644
index 0000000..29aada6
--- /dev/null
+++ b/core/combo/arch/arm/armv5te.mk
@@ -0,0 +1,21 @@
+# Configuration for Linux on ARM.
+# Generating binaries for the ARMv5TE architecture and higher
+#
+ARCH_ARM_HAVE_THUMB_SUPPORT     := true
+ARCH_ARM_HAVE_FAST_INTERWORKING := true
+ARCH_ARM_HAVE_64BIT_DATA        := true
+ARCH_ARM_HAVE_HALFWORD_MULTIPLY := true
+ARCH_ARM_HAVE_CLZ               := true
+ARCH_ARM_HAVE_FFS               := true
+
+# Note: Hard coding the 'tune' value here is probably not ideal,
+# and a better solution should be found in the future.
+#
+arch_variant_cflags := \
+    -march=armv5te \
+    -mtune=xscale  \
+    -D__ARM_ARCH_5__ \
+    -D__ARM_ARCH_5T__ \
+    -D__ARM_ARCH_5E__ \
+    -D__ARM_ARCH_5TE__
+
diff --git a/core/combo/arch/arm/armv7-a.mk b/core/combo/arch/arm/armv7-a.mk
new file mode 100644
index 0000000..7a3ecc4
--- /dev/null
+++ b/core/combo/arch/arm/armv7-a.mk
@@ -0,0 +1,22 @@
+# Configuration for Linux on ARM.
+# Generating binaries for the ARMv7-a architecture and higher
+#
+ARCH_ARM_HAVE_THUMB_SUPPORT     := true
+ARCH_ARM_HAVE_FAST_INTERWORKING := true
+ARCH_ARM_HAVE_64BIT_DATA        := true
+ARCH_ARM_HAVE_HALFWORD_MULTIPLY := true
+ARCH_ARM_HAVE_CLZ               := true
+ARCH_ARM_HAVE_FFS               := true
+ARCH_ARM_HAVE_VFP               := true
+ARCH_ARM_HAVE_NEON              := true
+
+# Note: Hard coding the 'tune' value here is probably not ideal,
+# and a better solution should be found in the future.
+#
+arch_variant_cflags := \
+    -march=armv7-a \
+    -mfloat-abi=softfp \
+    -mfpu=neon
+
+arch_variant_ldflags := \
+	-Wl,--fix-cortex-a8
\ No newline at end of file
diff --git a/core/combo/linux-arm.mk b/core/combo/linux-arm.mk
index 11a8ac7..6011351 100644
--- a/core/combo/linux-arm.mk
+++ b/core/combo/linux-arm.mk
@@ -1,68 +1,44 @@
 # Configuration for Linux on ARM.
 # Included by combo/select.make
 
-# You can set TARGET_ARCH_VERSION to use an arch version other
-# than ARMv5TE
-ifeq ($(strip $(TARGET_ARCH_VERSION)),)
-TARGET_ARCH_VERSION := armv5te
-endif
-
-# This set of if blocks sets makefile variables similar to preprocesser
+# You can set TARGET_ARCH_VARIANT to use an arch version other
+# than ARMv5TE. Each value should correspond to a file named
+# $(BUILD_COMBOS)/arch/<name>.mk which must contain
+# makefile variable definitions similar to the preprocessor
 # defines in system/core/include/arch/<combo>/AndroidConfig.h. Their
-# purpose is to allow module Android.mk files to selctively compile
-# different versions of code based upon the funtionality and 
+# purpose is to allow module Android.mk files to selectively compile
+# different versions of code based upon the funtionality and
 # instructions available in a given architecture version.
 #
-# The blocks also define specific arch_version_cflags, which 
+# The blocks also define specific arch_variant_cflags, which
 # include defines, and compiler settings for the given architecture
 # version.
 #
-# Note: Hard coding the 'tune' value here is probably not ideal,
-# and a better solution should be found in the future.
-#
-# With two or three different versions this if block approach is
-# fine. If/when this becomes large, please change this to include
-# architecture versions specific Makefiles which define these
-# variables.
-#
-# Supporting armv4 (without thumb) does not make much sense since
-# it's mostly an obsoleted instruction set architecture (only available
-# in StrongArm and arm8). Supporting armv4 will require a lot of conditional
-# code in assembler source since the bx (branch and exchange) instruction is
-# not supported.
-#
-ifeq ($(TARGET_ARCH_VERSION),armv5te)
-ARCH_ARM_HAVE_THUMB_SUPPORT := true
-ARCH_ARM_HAVE_FAST_INTERWORKING := true
-ARCH_ARM_HAVE_64BIT_DATA := true
-ARCH_ARM_HAVE_HALFWORD_MULTIPLY := true
-ARCH_ARM_HAVE_CLZ := true
-ARCH_ARM_HAVE_FFS := true
-
-arch_version_cflags := -march=armv5te -mtune=xscale  -D__ARM_ARCH_5__ \
-	-D__ARM_ARCH_5T__ -D__ARM_ARCH_5TE__
-else
-ifeq ($(TARGET_ARCH_VERSION),armv4t)
-$(warning ARMv4t support is currently a work in progress. It does not work right now!)
-ARCH_ARM_HAVE_THUMB_SUPPORT := false
-ARCH_ARM_HAVE_THUMB_INTERWORKING := false
-ARCH_ARM_HAVE_64BIT_DATA := false
-ARCH_ARM_HAVE_HALFWORD_MULTIPLY := false
-ARCH_ARM_HAVE_CLZ := false
-ARCH_ARM_HAVE_FFS := false
-
-DEFAULT_TARGET_CPU := arm920t
-
-arch_version_cflags := -march=armv4t -mtune=arm920t -D__ARM_ARCH_4T__
-else
-$(error Unknown ARM architecture version: $(TARGET_ARCH_VERSION))
+ifeq ($(strip $(TARGET_ARCH_VARIANT)),)
+TARGET_ARCH_VARIANT := armv5te
 endif
+
+# TARGET_ARCH_VARIANT used to be called TARGET_ARCH_VERSION
+# to avoid any weirdness, issue an error message if the latter
+# is defined.
+#
+ifneq ($(strip $(TARGET_ARCH_VERSION)),)
+$(info Definition for TARGET_ARCH_VERSION encountered !)
+$(info This variable has been renamed TARGET_ARCH_VARIANT, please update your build files !!)
+$(error Aborting the build.)
 endif
 
+TARGET_ARCH_SPECIFIC_MAKEFILE := $(BUILD_COMBOS)/arch/$(TARGET_ARCH)/$(TARGET_ARCH_VARIANT).mk
+ifeq ($(strip $(wildcard $(TARGET_ARCH_SPECIFIC_MAKEFILE))),)
+$(error Unknown ARM architecture version: $(TARGET_ARCH_VARIANT))
+endif
+
+include $(TARGET_ARCH_SPECIFIC_MAKEFILE)
+
 # You can set TARGET_TOOLS_PREFIX to get gcc from somewhere else
 ifeq ($(strip $($(combo_target)TOOLS_PREFIX)),)
 $(combo_target)TOOLS_PREFIX := \
-	prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.2.1/bin/arm-eabi-
+	prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.4.0/bin/arm-eabi-
 endif
 
 $(combo_target)CC := $($(combo_target)TOOLS_PREFIX)gcc$(HOST_EXECUTABLE_SUFFIX)
@@ -79,7 +55,7 @@
                         -funswitch-loops     \
                         -finline-limit=300
 
-# Modules can choose to compile some source as thumb. As 
+# Modules can choose to compile some source as thumb. As
 # non-thumb enabled targets are supported, this is treated
 # as a 'hint'. If thumb is not enabled, these files are just
 # compiled as ARM.
@@ -103,7 +79,7 @@
 # with -mlong-calls.  When built at -O0, those libraries are
 # too big for a thumb "BL <label>" to go from one end to the other.
 ifeq ($(FORCE_ARM_DEBUGGING),true)
-  TARGET_arm_CFLAGS += -fno-omit-frame-pointer
+  TARGET_arm_CFLAGS += -fno-omit-frame-pointer -fno-strict-aliasing
   TARGET_thumb_CFLAGS += -marm -fno-omit-frame-pointer
 endif
 
@@ -116,10 +92,13 @@
 			-funwind-tables \
 			-fstack-protector \
 			-fno-short-enums \
-			$(arch_version_cflags) \
+			$(arch_variant_cflags) \
 			-include $(android_config_h) \
 			-I $(arch_include_dir)
 
+$(combo_target)GLOBAL_LDFLAGS += \
+			$(arch_variant_ldflags)
+
 # We only need thumb interworking in cases where thumb support
 # is available in the architecture, and just to be sure, (and
 # since sometimes thumb-interwork appears to be default), we
@@ -150,7 +129,7 @@
 
 ## on some hosts, the target cross-compiler is not available so do not run this command
 ifneq ($(wildcard $($(combo_target)CC)),)
-# We compile with the global cflags to ensure that 
+# We compile with the global cflags to ensure that
 # any flags which affect libgcc are correctly taken
 # into account.
 $(combo_target)LIBGCC := $(shell $($(combo_target)CC) $($(combo_target)GLOBAL_CFLAGS) -print-libgcc-file-name)
@@ -201,6 +180,7 @@
 	$(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
 	-o $@ \
 	$(PRIVATE_LDFLAGS) \
+	$(TARGET_GLOBAL_LDFLAGS) \
 	$(TARGET_LIBGCC)
 endef
 
@@ -217,6 +197,7 @@
 	$(PRIVATE_ALL_OBJECTS) \
 	$(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
 	$(PRIVATE_LDFLAGS) \
+	$(TARGET_GLOBAL_LDFLAGS) \
 	$(TARGET_LIBGCC) \
 	$(TARGET_CRTEND_O)
 endef
@@ -228,6 +209,7 @@
 	$(TARGET_GLOBAL_LD_DIRS) \
 	$(TARGET_CRTBEGIN_STATIC_O) \
 	$(PRIVATE_LDFLAGS) \
+	$(TARGET_GLOBAL_LDFLAGS) \
 	$(PRIVATE_ALL_OBJECTS) \
 	$(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
 	$(TARGET_LIBGCC) \
diff --git a/core/combo/select.mk b/core/combo/select.mk
index 273b660..793b0a9 100644
--- a/core/combo/select.mk
+++ b/core/combo/select.mk
@@ -39,6 +39,7 @@
 # These flags might (will) be overridden by the target makefiles
 $(combo_target)GLOBAL_CFLAGS := -fno-exceptions -Wno-multichar
 $(combo_target)RELEASE_CFLAGS := -O2 -g -fno-strict-aliasing
+$(combo_target)GLOBAL_LDFLAGS :=
 $(combo_target)GLOBAL_ARFLAGS := crs
 
 $(combo_target)EXECUTABLE_SUFFIX := 
diff --git a/core/config.mk b/core/config.mk
index b705de5..37ca404 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -75,11 +75,11 @@
 # ###############################################################
 
 # These can be changed to modify both host and device modules.
-COMMON_GLOBAL_CFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused
+COMMON_GLOBAL_CFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -Winit-self -Wpointer-arith
 COMMON_RELEASE_CFLAGS:= -DNDEBUG -UDEBUG
 
-COMMON_GLOBAL_CPPFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -Wnon-virtual-dtor
-COMMON_RELEASE_CPPFLAGS:= -DNDEBUG -UDEBUG
+COMMON_GLOBAL_CPPFLAGS:= $(COMMON_GLOBAL_CFLAGS) -Wsign-promo
+COMMON_RELEASE_CPPFLAGS:= $(COMMON_RELEASE_CFLAGS)
 
 # Set the extensions used for various packages
 COMMON_PACKAGE_SUFFIX := .zip
@@ -87,7 +87,13 @@
 COMMON_ANDROID_PACKAGE_SUFFIX := .apk
 
 # list of flags to turn specific warnings in to errors
-TARGET_ERROR_FLAGS := -Werror=return-type
+TARGET_ERROR_FLAGS := -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point
+
+# TODO: do symbol compression
+TARGET_COMPRESS_MODULE_SYMBOLS := false
+
+# Default is to prelink modules.
+TARGET_PRELINK_MODULE := true
 
 # ###############################################################
 # Include sub-configuration files
@@ -105,6 +111,32 @@
 # are specific to the user's build configuration.
 include $(BUILD_SYSTEM)/envsetup.mk
 
+# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
+# or under vendor/*/$(TARGET_DEVICE).  Search in both places, but
+# make sure only one exists.
+# Real boards should always be associated with an OEM vendor.
+board_config_mk := \
+	$(strip $(wildcard \
+		$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
+		vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \
+	))
+ifeq ($(board_config_mk),)
+  $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))
+endif
+ifneq ($(words $(board_config_mk)),1)
+  $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
+endif
+include $(board_config_mk)
+TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))
+board_config_mk :=
+
+# Clean up/verify variables defined by the board config file.
+TARGET_BOOTLOADER_BOARD_NAME := $(strip $(TARGET_BOOTLOADER_BOARD_NAME))
+TARGET_CPU_ABI := $(strip $(TARGET_CPU_ABI))
+ifeq ($(TARGET_CPU_ABI),)
+  $(error No TARGET_CPU_ABI defined by board config: $(board_config_mk))
+endif
+
 # $(1): os/arch
 define select-android-config-h
 system/core/include/arch/$(1)/AndroidConfig.h
@@ -167,6 +199,7 @@
 TUNE2FS := tune2fs
 E2FSCK := e2fsck
 JARJAR := java -jar $(HOST_OUT_JAVA_LIBRARIES)/jarjar.jar
+PROGUARD := external/proguard/bin/proguard.sh
 
 # dx is java behind a shell script; no .exe necessary.
 DX := $(HOST_OUT_EXECUTABLES)/dx
@@ -259,10 +292,6 @@
 TARGET_GLOBAL_CFLAGS += $(TARGET_RELEASE_CFLAGS)
 TARGET_GLOBAL_CPPFLAGS += $(TARGET_RELEASE_CPPFLAGS)
 
-# TODO: do symbol compression
-TARGET_COMPRESS_MODULE_SYMBOLS := false
-TARGET_PRELINK_MODULE := true
-
 PREBUILT_IS_PRESENT := $(if $(wildcard prebuilt/Android.mk),true)
 
 
diff --git a/core/definitions.mk b/core/definitions.mk
index 9b3a03d..3221525 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -109,7 +109,14 @@
 
 # Figure out where we are.
 define my-dir
-$(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST),$(MAKEFILE_LIST))))
+$(strip \
+  $(eval md_file_ := $$(lastword $$(MAKEFILE_LIST))) \
+  $(if $(filter $(CLEAR_VARS),$(md_file_)), \
+    $(error LOCAL_PATH must be set before including $$(CLEAR_VARS)) \
+   , \
+    $(patsubst %/,%,$(dir $(md_file_))) \
+   ) \
+ )
 endef
 
 ###########################################################
@@ -128,7 +135,8 @@
 # $(1): directory to search under
 # Ignores $(1)/Android.mk
 define first-makefiles-under
-$(shell build/tools/findleaves.sh --mindepth=2 $(1) Android.mk)
+$(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git \
+        --mindepth=2 $(1) Android.mk)
 endef
 
 ###########################################################
@@ -1166,6 +1174,7 @@
     $(addprefix -S , $(PRIVATE_RESOURCE_DIR)) \
     $(addprefix -A , $(PRIVATE_ASSET_DIR)) \
     $(addprefix -I , $(PRIVATE_AAPT_INCLUDES)) \
+    $(addprefix -G , $(PRIVATE_PROGUARD_OPTIONS_FILE)) \
     $(addprefix --min-sdk-version , $(DEFAULT_APP_TARGET_SDK)) \
     $(addprefix --target-sdk-version , $(DEFAULT_APP_TARGET_SDK)) \
     $(addprefix --version-code , $(PLATFORM_SDK_VERSION)) \
@@ -1264,11 +1273,12 @@
 
 #TODO: use a smaller -Xmx value for most libraries;
 #      only core.jar and framework.jar need a heap this big.
+# Avoid the memory arguments on Windows, dx fails to load for some reason with them.
 define transform-classes.jar-to-dex
 @echo "target Dex: $(PRIVATE_MODULE)"
 @mkdir -p $(dir $@)
-$(hide) $(DX) -JXms16M \
-    -JXmx1536M \
+$(hide) $(DX) \
+    $(if $(findstring windows,$(HOST_OS)),,-JXms16M -JXmx1536M) \
     --dex --output=$@ \
     $(if $(NO_OPTIMIZE_DX), \
         --no-optimize) \
@@ -1324,11 +1334,9 @@
 $(hide) rm -rf $(dir $@)lib
 endef
 
-#TODO: use aapt instead of zip, once it supports junking the path
-#      (so adding "xxx/yyy/classes.dex" appears as "classes.dex")
 #TODO: update the manifest to point to the dex file
 define add-dex-to-package
-$(hide) zip -qj $@ $(PRIVATE_DEX_FILE)
+$(hide) $(AAPT) add -k $@ $(PRIVATE_DEX_FILE)
 endef
 
 define add-java-resources-to-package
@@ -1587,7 +1595,7 @@
   echo "$$printname total size is $$total"; \
   img_blocksize=$(call image-size-from-data-size,$(BOARD_FLASH_BLOCK_SIZE)); \
   if [ "$(3)" == "yaffs" ]; then \
-    reservedblocks=5; \
+    reservedblocks=8; \
   else \
     reservedblocks=0; \
   fi; \
@@ -1607,6 +1615,37 @@
  )
 endef
 
+# Like assert-max-file-size, but the second argument is a partition
+# size, which we'll convert to a max image size before checking it
+# against the files.
+#
+# $(1): The file(s) to check (often $@)
+# $(2): The partition size.
+define assert-max-image-size
+$(if $(2), \
+  $(call assert-max-file-size,$(1),$(call image-size-from-data-size,$(2))), \
+  true)
+endef
+
+
+###########################################################
+## Define device-specific radio files
+###########################################################
+
+# Copy a radio image file to the output location, and add it to
+# INSTALLED_RADIOIMAGE_TARGET.
+# $(1): filename
+define add-radio-file
+  $(eval $(call add-radio-file-internal,$(1)))
+endef
+define add-radio-file-internal
+INSTALLED_RADIOIMAGE_TARGET += $$(PRODUCT_OUT)/$(1)
+ALL_PREBUILT += $$(PRODUCT_OUT)/$(1)
+$$(PRODUCT_OUT)/$(1) : $$(LOCAL_PATH)/$(1) | $$(ACP)
+	$$(transform-prebuilt-to-target)
+endef
+
+
 ###########################################################
 ## Other includes
 ###########################################################
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 31901e9..6d14753 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -173,7 +173,7 @@
   # under product/) are actually host-dependent.
   # But, the debug type is controlled by TARGET_BUILD_TYPE and not
   # HOST_BUILD_TYPE.
-  TARGET_PRODUCT_OUT_ROOT := $(HOST_OUT_$(TARGET_BUILD_TYPE))/product
+  TARGET_PRODUCT_OUT_ROOT := $(HOST_OUT_$(TARGET_BUILD_TYPE))/pr
 else
   TARGET_PRODUCT_OUT_ROOT := $(TARGET_OUT_ROOT)/product
 endif
@@ -270,7 +270,7 @@
 	ABP:=$(ABP):$(TARGET_OUT_EXECUTABLES)
 else
 	# this should be copied to HOST_OUT_EXECUTABLES instead
-	ABP:=$(ABP):$(PWD)/prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.2.1/bin
+	ABP:=$(ABP):$(PWD)/prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.4.0/bin
 endif
 ANDROID_BUILD_PATHS := $(ABP)
 ANDROID_PREBUILTS := prebuilt/$(HOST_PREBUILT_TAG)
@@ -335,5 +335,3 @@
 $(info   BUILD_ID=$(BUILD_ID))
 $(info ============================================)
 endif
-
-
diff --git a/core/java.mk b/core/java.mk
index 658b173..19b4d63 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -68,7 +68,8 @@
 # the emma tool
 full_classes_emma_jar := $(emma_intermediates_dir)/lib/$(full_classes_compiled_jar_leaf)
 full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar
-full_classes_jarjar_jar := $(full_classes_jar)
+full_classes_jarjar_jar := $(intermediates.COMMON)/classes-jarjar.jar
+full_classes_proguard_jar := $(full_classes_jar)
 built_dex := $(intermediates.COMMON)/classes.dex
 
 LOCAL_INTERMEDIATE_TARGETS += \
@@ -174,15 +175,14 @@
 $(PRIVATE_EMMA_COVERAGE_FILE): $(full_classes_emma_jar)
 else
 $(full_classes_emma_jar): $(full_classes_compiled_jar) | $(ACP)
-	@echo Copying $<
+	@echo Copying: $<
 	$(copy-file-to-target)
 endif
 
-# Run jarjar if necessary, otherwise just copy the file.  This is the last
-# part of this step, so the output of this command is full_classes_jar.
+# Run jarjar if necessary, otherwise just copy the file.
 ifneq ($(strip $(LOCAL_JARJAR_RULES)),)
 $(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES)
-$(full_classes_jarjar_jar): $(full_classes_emma_jar) | jarjar
+$(full_classes_jarjar_jar): $(full_classes_emma_jar) | $(JARJAR)
 	@echo JarJar: $@
 	$(hide) $(JARJAR) process $(PRIVATE_JARJAR_RULES) $< $@
 else
@@ -191,6 +191,40 @@
 	$(hide) $(ACP) $< $@
 endif
 
+# Run proguard if necessary, otherwise just copy the file.  This is the last
+# part of this step, so the output of this command is full_classes_jar.
+ifneq ($(strip $(LOCAL_PROGUARD_ENABLED)),)
+proguard_dictionary := $(intermediates.COMMON)/proguard_dictionary
+proguard_flags := $(addprefix -libraryjars ,$(full_java_libs)) \
+                  -include $(BUILD_SYSTEM)/proguard.flags \
+                  -forceprocessing \
+                  -printmapping $(proguard_dictionary)
+ifeq ($(strip $(LOCAL_PROGUARD_ENABLED)),full)
+    # full
+else
+ifeq ($(strip $(LOCAL_PROGUARD_ENABLED)),optonly)
+    # optonly
+    proguard_flags += -dontobfuscate
+else
+ifeq ($(strip $(LOCAL_PROGUARD_ENABLED)),custom)
+    # custom
+else
+    $(warning while processing: $(LOCAL_MODULE))
+    $(error invalid value for LOCAL_PROGUARD_ENABLED: $(LOCAL_PROGUARD_ENABLED))
+endif
+endif
+endif
+
+$(full_classes_proguard_jar): PRIVATE_PROGUARD_FLAGS := $(proguard_flags) $(LOCAL_PROGUARD_FLAGS)
+$(full_classes_proguard_jar): $(full_classes_emma_jar) | $(PROGUARD)
+	@echo Proguard: $@
+	$(hide) $(PROGUARD) -injars $< -outjars $@ $(PRIVATE_PROGUARD_FLAGS)
+else
+$(full_classes_proguard_jar): $(full_classes_emma_jar) | $(ACP)
+	@echo Copying: $@
+	$(hide) $(ACP) $< $@
+endif
+
 # Override PRIVATE_INTERMEDIATES_DIR so that install-dex-debug
 # will work even when intermediates != intermediates.COMMON.
 $(built_dex): PRIVATE_INTERMEDIATES_DIR := $(intermediates.COMMON)
diff --git a/core/main.mk b/core/main.mk
index 97e49f4..9986f94 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -54,6 +54,12 @@
 # be generated correctly
 include $(BUILD_SYSTEM)/cleanbuild.mk
 
+VERSION_CHECK_SEQUENCE_NUMBER := 1
+-include $(OUT_DIR)/versions_checked.mk
+ifneq ($(VERSION_CHECK_SEQUENCE_NUMBER),$(VERSIONS_CHECKED))
+
+$(info Checking build tools versions...)
+
 ifneq ($(HOST_OS),windows)
 ifneq ($(HOST_OS)-$(HOST_ARCH),darwin-ppc)
 # check for a case sensitive file system
@@ -123,6 +129,10 @@
 
 endif # windows
 
+$(shell echo 'VERSIONS_CHECKED := $(VERSION_CHECK_SEQUENCE_NUMBER)' \
+        > $(OUT_DIR)/versions_checked.mk)
+endif
+
 # These are the modifier targets that don't do anything themselves, but
 # change the behavior of the build.
 # (must be defined before including definitions.make)
@@ -220,7 +230,8 @@
 tags_to_install := user debug eng
   # Don't require the setup wizard on eng builds
   ADDITIONAL_BUILD_PROPERTIES := $(filter-out ro.setupwizard.mode=%,\
-          $(call collapse-pairs, $(ADDITIONAL_BUILD_PROPERTIES)))
+          $(call collapse-pairs, $(ADDITIONAL_BUILD_PROPERTIES))) \
+          ro.setupwizard.mode=OPTIONAL
 endif
 
 ## tests ##
@@ -241,8 +252,16 @@
 ADDITIONAL_BUILD_PROPERTIES += xmpp.auto-presence=true
 ADDITIONAL_BUILD_PROPERTIES += ro.config.nocheckin=yes
 else # !sdk
-# Enable sync for non-sdk builds only (sdk builds lack SubscribedFeedsProvider).
-ADDITIONAL_BUILD_PROPERTIES += ro.config.sync=yes
+endif
+
+## precise GC ##
+
+ifneq ($(filter dalvik.gc.type-precise,$(PRODUCT_TAGS)),)
+  # Enabling type-precise GC results in larger optimized DEX files.  The
+  # additional storage requirements for ".odex" files can cause /system
+  # to overflow on some devices, so this is configured separately for
+  # each product.
+  ADDITIONAL_BUILD_PROPERTIES += dalvik.vm.dexopt-flags=m=y
 endif
 
 # Install an apns-conf.xml file if one's not already being installed.
@@ -256,7 +275,7 @@
 # If we're on an eng or tests build, but not on the sdk, and we have
 # a better one, use that instead.
 ifneq ($(filter eng tests,$(TARGET_BUILD_VARIANT)),)
-  ifdef is_sdk_build
+  ifndef is_sdk_build
     apns_to_use := $(wildcard vendor/google/etc/apns-conf.xml)
     ifneq ($(strip $(apns_to_use)),)
       PRODUCT_COPY_FILES := \
@@ -267,6 +286,7 @@
 endif
 
 ADDITIONAL_BUILD_PROPERTIES += net.bt.name=Android
+ADDITIONAL_BUILD_PROPERTIES += ro.config.sync=yes
 
 # enable vm tracing in files for now to help track
 # the cause of ANRs in the content process
@@ -317,8 +337,6 @@
 # Bring in all modules that need to be built.
 ifneq ($(dont_bother),true)
 
-subdir_makefiles :=
-
 ifeq ($(HOST_OS),windows)
 SDK_ONLY := true
 endif
@@ -338,6 +356,7 @@
 	dalvik/tools/hprof-conv \
 	development/emulator/mksdcard \
 	development/tools/line_endings \
+	development/tools/sdklauncher \
 	development/host \
 	external/expat \
 	external/libpng \
@@ -371,6 +390,7 @@
 	development/tools/sdkstats \
 	development/tools/sdkmanager \
 	development/tools/mkstubs \
+	development/tools/layoutopt \
 	frameworks/base \
 	frameworks/base/tools/layoutlib \
 	external/googleclient \
@@ -390,8 +410,6 @@
 # TINY_ANDROID is a super-minimal build configuration, handy for board 
 # bringup and very low level debugging
 
-INTERNAL_DEFAULT_DOCS_TARGETS := 
-
 subdirs := \
 	bionic \
 	system/core \
@@ -410,7 +428,6 @@
 #
 # Typical build; include any Android.mk files we can find.
 #
-INTERNAL_DEFAULT_DOCS_TARGETS := offline-sdk-docs
 subdirs := $(TOP)
 
 FULL_BUILD := true
@@ -419,41 +436,6 @@
 
 endif	# !SDK_ONLY
 
-# Can't use first-makefiles-under here because
-# --mindepth=2 makes the prunes not work.
-subdir_makefiles += \
-	$(shell build/tools/findleaves.sh --prune="*\.git*" --prune="*\.repo*" --prune="./out" $(subdirs) Android.mk)
-
-# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
-# or under vendor/*/$(TARGET_DEVICE).  Search in both places, but
-# make sure only one exists.
-# Real boards should always be associated with an OEM vendor.
-board_config_mk := \
-	$(strip $(wildcard \
-		$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
-		vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \
-	))
-ifeq ($(board_config_mk),)
-  $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))
-endif
-ifneq ($(words $(board_config_mk)),1)
-  $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
-endif
-include $(board_config_mk)
-TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))
-board_config_mk :=
-
-# Clean up/verify variables defined by the board config file.
-TARGET_BOOTLOADER_BOARD_NAME := $(strip $(TARGET_BOOTLOADER_BOARD_NAME))
-TARGET_CPU_ABI := $(strip $(TARGET_CPU_ABI))
-ifeq ($(TARGET_CPU_ABI),)
-  $(error No TARGET_CPU_ABI defined by board config: $(board_config_mk))
-endif
-
-#
-# Include all of the makefiles in the system
-#
-
 ifneq ($(ONE_SHOT_MAKEFILE),)
 # We've probably been invoked by the "mm" shell function
 # with a subdirectory's makefile.
@@ -465,14 +447,25 @@
 # would have been with a normal make.
 CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS),))
 FULL_BUILD :=
-INTERNAL_DEFAULT_DOCS_TARGETS :=
 # Stub out the notice targets, which probably aren't defined
 # when using ONE_SHOT_MAKEFILE.
 NOTICE-HOST-%: ;
 NOTICE-TARGET-%: ;
-else
+
+else # ONE_SHOT_MAKEFILE
+
+#
+# Include all of the makefiles in the system
+#
+
+# Can't use first-makefiles-under here because
+# --mindepth=2 makes the prunes not work.
+subdir_makefiles := \
+	$(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git $(subdirs) Android.mk)
+
 include $(subdir_makefiles)
-endif
+endif # ONE_SHOT_MAKEFILE
+
 # -------------------------------------------------------------------
 # All module makefiles have been included at this point.
 # -------------------------------------------------------------------
@@ -673,7 +666,6 @@
 	$(INSTALLED_BOOTIMAGE_TARGET) \
 	$(INSTALLED_RECOVERYIMAGE_TARGET) \
 	$(INSTALLED_USERDATAIMAGE_TARGET) \
-	$(INTERNAL_DEFAULT_DOCS_TARGETS) \
 	$(INSTALLED_FILES_FILE)
 
 # The actual files built by the droidcore target changes depending
diff --git a/core/notice_files.mk b/core/notice_files.mk
index 24295c7..fd76d51 100644
--- a/core/notice_files.mk
+++ b/core/notice_files.mk
@@ -20,12 +20,19 @@
     # We can't use xxx_OUT_STATIC_LIBRARIES because it points into
     # device-obj or host-obj.
     module_installed_filename := \
-    	$(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_SHARED_LIBRARIES))/$(notdir $(LOCAL_BUILT_MODULE))
+        $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_SHARED_LIBRARIES))/$(notdir $(LOCAL_BUILT_MODULE))
   else
     ifeq ($(LOCAL_MODULE_CLASS),JAVA_LIBRARIES)
       # Stick the static java libraries with the regular java libraries.
+      module_leaf := $(notdir $(LOCAL_BUILT_MODULE))
+      # javalib.jar is the default name for the build module (and isn't meaningful)
+      # If that's what we have, substitute the module name instead.  These files
+      # aren't included on the device, so this name is synthetic anyway.
+      ifeq ($(module_leaf),javalib.jar)
+        module_leaf := $(LOCAL_MODULE).jar
+      endif
       module_installed_filename := \
-	  $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_JAVA_LIBRARIES))/$(notdir $(LOCAL_BUILT_MODULE))
+          $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_JAVA_LIBRARIES))/$(module_leaf)
     else
       $(error Cannot determine where to install NOTICE file for $(LOCAL_MODULE))
     endif # JAVA_LIBRARIES
diff --git a/core/package.mk b/core/package.mk
index 6b09bda..828f4c7 100644
--- a/core/package.mk
+++ b/core/package.mk
@@ -111,6 +111,11 @@
 
 LOCAL_BUILT_MODULE_STEM := package.apk
 
+proguard_options_file := $(package_expected_intermediates_COMMON)/proguard_options
+ifneq ($(strip $(LOCAL_PROGUARD_ENABLED)),custom)
+    LOCAL_PROGUARD_FLAGS := -include $(proguard_options_file) $(LOCAL_PROGUARD_FLAGS)
+endif
+
 # The dex files go in the package, so we don't
 # want to install them separately for this module.
 old_DONT_INSTALL_DEX_FILES := $(DONT_INSTALL_DEX_FILES)
@@ -143,6 +148,7 @@
 
 $(R_file_stamp): PRIVATE_RESOURCE_PUBLICS_OUTPUT := \
 			$(intermediates.COMMON)/public_resources.xml
+$(R_file_stamp): PRIVATE_PROGUARD_OPTIONS_FILE := $(proguard_options_file)
 $(R_file_stamp): $(all_res_assets) $(full_android_manifest) $(AAPT) | $(ACP)
 	@echo "target R.java/Manifest.java: $(PRIVATE_MODULE) ($@)"
 	@rm -f $@
@@ -164,6 +170,8 @@
 		$(ACP) -fpt $$GENERATED_R_FILE $@ || exit 32; \
 	done; \
 
+$(proguard_options_file): $(R_file_stamp)
+
 ifdef LOCAL_EXPORT_PACKAGE_RESOURCES
 # Put this module's resources into a PRODUCT-agnositc package that
 # other packages can use to build their own PRODUCT-agnostic R.java (etc.)
diff --git a/core/pathmap.mk b/core/pathmap.mk
index e281b9d..1ae663d 100644
--- a/core/pathmap.mk
+++ b/core/pathmap.mk
@@ -28,8 +28,8 @@
 #
 pathmap_INCL := \
     bluedroid:system/bluetooth/bluedroid/include \
-    bluez-libs:external/bluez/libs/include \
-    bluez-utils:external/bluez/utils \
+    bluez:external/bluetooth/bluez \
+    glib:external/bluetooth/glib \
     bootloader:bootable/bootloader/legacy/include \
     corecg:external/skia/include/core \
     dbus:external/dbus \
@@ -76,7 +76,6 @@
 	$(addsuffix /java, \
 	    core \
 	    graphics \
-	    im \
 	    location \
 	    media \
 	    opengl \
diff --git a/core/prebuilt.mk b/core/prebuilt.mk
index 4cbbb99..b2bb07c 100644
--- a/core/prebuilt.mk
+++ b/core/prebuilt.mk
@@ -26,6 +26,8 @@
   prebuilt_module_is_a_library :=
 endif
 
+PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES))
+
 # Ensure that prebuilt .apks have been aligned.
 ifneq ($(filter APPS,$(LOCAL_MODULE_CLASS)),)
 $(LOCAL_BUILT_MODULE) : $(LOCAL_PATH)/$(LOCAL_SRC_FILES) | $(ZIPALIGN)
diff --git a/core/prelink-linux-arm.map b/core/prelink-linux-arm.map
index eb42ad3..64149bc 100644
--- a/core/prelink-linux-arm.map
+++ b/core/prelink-linux-arm.map
@@ -27,7 +27,7 @@
 liba2dp.so              0xAEE00000
 audio.so                0xAED00000
 input.so                0xAEC00000
-libhcid.so              0xAEB00000
+libbluetoothd.so        0xAEB00000
 libbluedroid.so         0xAEA00000
 libbluetooth.so         0xAE900000
 libdbus.so              0xAE800000
@@ -50,8 +50,10 @@
 
 # graphics
 libpixelflinger.so      0xACF00000
+# libcorecg is for backward-compatibility with donut
 libcorecg.so            0xACE00000
 libsurfaceflinger.so    0xACD00000
+libGLES_android.so      0xACC80000
 libagl.so               0xACC00000
 
 libGLESv1_CM.so         0xACB00000
@@ -64,14 +66,21 @@
 
 libexif.so              0xAC500000
 libui.so                0xAC400000
-libsgl.so               0xAC000000
+# libsgl is for backward-compatibility with donut
+libsgl.so               0xAC200000
+libskia.so              0xAC000000
+librs_jni.so            0xABF00000
+libRS.so                0xAB900000
+
 
 # audio
-libFLAC.so              0xAB900000
-libspeech.so            0xAB800000
+libFLAC.so              0xAB8A0000
+libaudiopolicy.so       0xAB880000
+libaudiopolicygeneric.so 0xAB800000
+libsoundpool.so         0xAB780000
 libaudio.so             0xAB700000
-libsonivox.so           0xAB600000
-libsoundpool.so         0xAB500000
+libspeech.so            0xAB600000
+libsonivox.so           0xAB500000
 libvorbisidec.so        0xAB400000
 libmedia_jni.so         0xAB300000
 libmediaplayerservice.so 0xAB280000
@@ -83,6 +92,7 @@
 libsqlite.so            0xAAC00000
 libexpat.so             0xAAB00000
 libwebcore.so           0xAA000000
+libbinder.so            0xA9D80000
 libutils.so             0xA9D00000
 libcameraservice.so     0xA9C80000
 libhardware.so          0xA9C70000
@@ -92,13 +102,29 @@
 libime.so               0xA9800000
 libgps.so               0xA9700000
 libcamera.so            0xA9680000
-libqcamera.so           0xA9400000
+liboemcamera.so         0xA9400000
 
-# pv opencore libraries
-libopencore_author.so   0xA7B00000
-libopencore_player.so   0xA7800000
-libopencore_common.so   0xA7300000
-libopencore_2way.so     0xA7000000
+# pv libraries
+libpvasf.so                    0xA7C26000
+libpvasfreg.so                 0xA7C00000
+libomx_sharedlibrary.so        0xA7BA0000
+libopencore_download.so        0xA7B40000
+libopencore_downloadreg.so     0xA7B00000
+libopencore_net_support.so     0xA7A00000
+libopencore_rtsp.so            0xA7900000
+libopencore_rtspreg.so         0xA7890000
+libopencore_author.so          0xA7800000
+libomx_aacdec_sharedlibrary.so 0xA7700000
+libomx_amrdec_sharedlibrary.so 0xA76A0000
+libomx_amrenc_sharedlibrary.so 0xA7680000
+libomx_avcdec_sharedlibrary.so 0xA7660000
+libomx_avcenc_sharedlibrary.so 0xA7610000
+libomx_m4vdec_sharedlibrary.so 0xA75C0000
+libomx_m4venc_sharedlibrary.so 0xA7590000
+libomx_mp3dec_sharedlibrary.so 0xA7450000
+libopencore_mp4local.so        0xA7400000
+libopencore_mp4localreg.so     0xA7300000
+libopencore_player.so          0xA7000000
 
 # opencore hardware support
 libmm-adspsvc.so              0xA6FFD000
@@ -137,3 +163,4 @@
 libtrace_test.so        0x9A300000
 libsrec_jni.so          0x9A200000
 libcerttool_jni.so      0x9A100000
+
diff --git a/core/product.mk b/core/product.mk
index adc81c3..a9a24d2 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -68,7 +68,8 @@
     PRODUCT_SDK_ADDON_NAME \
     PRODUCT_SDK_ADDON_COPY_FILES \
     PRODUCT_SDK_ADDON_COPY_MODULES \
-    PRODUCT_SDK_ADDON_DOC_MODULE
+    PRODUCT_SDK_ADDON_DOC_MODULE \
+    PRODUCT_DEFAULT_WIFI_CHANNELS
 
 define dump-product
 $(info ==== $(1) ====)\
diff --git a/core/product_config.mk b/core/product_config.mk
index 7cfa5f4..0a693f4 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -193,6 +193,12 @@
   extra_locales :=
 endif
 
+# Default to medium-density assets.
+# (Can be overridden in the device config, e.g.: PRODUCT_LOCALES += hdpi)
+PRODUCT_LOCALES := $(strip \
+	$(PRODUCT_LOCALES) \
+	$(if $(filter %dpi,$(PRODUCT_LOCALES)),,mdpi))
+
 # Assemble the list of options.
 PRODUCT_AAPT_CONFIG := $(PRODUCT_LOCALES)
 
@@ -214,6 +220,9 @@
   PRODUCT_MANUFACTURER := unknown
 endif
 
+PRODUCT_DEFAULT_WIFI_CHANNELS := \
+	$(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEFAULT_WIFI_CHANNELS))
+
 # Which policy should this product use
 PRODUCT_POLICY := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_POLICY))
 
diff --git a/core/proguard.flags b/core/proguard.flags
new file mode 100644
index 0000000..afd1548
--- /dev/null
+++ b/core/proguard.flags
@@ -0,0 +1,7 @@
+# see http://sourceforge.net/tracker/?func=detail&aid=2787465&group_id=54750&atid=474707
+-optimizations !code/simplification/arithmetic
+-allowaccessmodification
+
+# Some classes in the libraries extend package private classes to chare common functionality
+# that isn't explicitly part of the API
+-dontskipnonpubliclibraryclasses
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index 96ce0c1..cfd72e7 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -68,7 +68,9 @@
 	CtsAppTestCases \
 	CtsContentTestCases \
 	CtsDatabaseTestCases \
-        CtsGestureTestCases \
+	CtsDpiTestCases \
+	CtsDpiTestCases2 \
+    CtsGestureTestCases \
 	CtsGraphicsTestCases \
 	CtsHardwareTestCases \
 	CtsLocationTestCases \
@@ -77,6 +79,7 @@
 	CtsPermissionTestCases \
 	CtsPermission2TestCases \
 	CtsProviderTestCases \
+	CtsSpeechTestCases \
 	CtsTelephonyTestCases \
 	CtsTextTestCases \
 	CtsUtilTestCases \
diff --git a/core/tasks/product-graph.mk b/core/tasks/product-graph.mk
index ead1406..6442252 100644
--- a/core/tasks/product-graph.mk
+++ b/core/tasks/product-graph.mk
@@ -15,21 +15,34 @@
 #
 
 products_pdf := $(OUT_DIR)/products.pdf
+products_graph := $(products_pdf:%.pdf=%.dot)
 
-# This rule doens't include any nodes that don't inherit from
+$(products_graph):
+	@echo Product graph DOT: $@
+	$(hide) ( \
+		echo 'digraph {'; \
+		echo 'graph [ ratio=.5 ];'; \
+		$(foreach p,$(ALL_PRODUCTS), \
+			$(foreach d,$(PRODUCTS.$(strip $(p)).INHERITS_FROM), \
+			echo \"$(d)\" -\> \"$(p)\";)) \
+		$(foreach prod, \
+			$(sort $(foreach p,$(ALL_PRODUCTS), \
+				$(foreach d,$(PRODUCTS.$(strip $(p)).INHERITS_FROM), \
+					$(d))) \
+				$(foreach p,$(ALL_PRODUCTS),$(p))), \
+			echo \"$(prod)\" [ label=\"$(dir $(prod))\\n$(notdir $(prod))\"];) \
+		echo '}' \
+	) > $@
+
+# This rule doesn't include any nodes that don't inherit from
 # anything or don't have anything inherit from them, to make the
 # graph more readable.  To add that, add this line to the rule
 # below:
 #		$(foreach p,$(ALL_PRODUCTS), echo \"$(p)\";) \
 
-$(products_pdf):
-	$(hide) ( \
-		echo 'digraph {'; \
-		$(foreach p,$(ALL_PRODUCTS), \
-			$(foreach d,$(PRODUCTS.$(strip $(p)).INHERITS_FROM), \
-			echo \"$(d)\" -\> \"$(p)\";)) \
-		echo '}' \
-	) | dot -Tpdf -Nshape=box -o $@
+$(products_pdf): $(products_graph)
+	@echo Product graph PDF: $@
+	dot -Tpdf -Nshape=box -o $@ $<
 
 product-graph: $(products_pdf)
 
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index 0e6cf5c..7115eac 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -41,7 +41,7 @@
   # which is the version that we reveal to the end user.
   # Update this value when the platform version changes (rather
   # than overriding it somewhere else).  Can be an arbitrary string.
-  PLATFORM_VERSION := 1.6
+  PLATFORM_VERSION := AOSP
 endif
 
 ifeq "" "$(PLATFORM_SDK_VERSION)"
@@ -53,13 +53,13 @@
   # intermediate builds).  During development, this number remains at the
   # SDK version the branch is based on and PLATFORM_VERSION_CODENAME holds
   # the code-name of the new development work.
-  PLATFORM_SDK_VERSION := 4
+  PLATFORM_SDK_VERSION := 5
 endif
 
 ifeq "" "$(PLATFORM_VERSION_CODENAME)"
-  # If the build is not a final release build, then this is the current
-  # development code-name.  If this is a final release build, it is simply "REL".
-  PLATFORM_VERSION_CODENAME := REL
+  # This is the current development code-name, if the build is not a final
+  # release build.  If this is a final release build, it is simply "REL".
+  PLATFORM_VERSION_CODENAME := AOSP
 endif
 
 ifeq "" "$(DEFAULT_APP_TARGET_SDK)"
diff --git a/envsetup.sh b/envsetup.sh
index 6325c3d..a32d0b2 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -102,7 +102,7 @@
     # and in with the new
     CODE_REVIEWS=
     prebuiltdir=$(getprebuilt)
-    export ANDROID_EABI_TOOLCHAIN=$prebuiltdir/toolchain/arm-eabi-4.2.1/bin
+    export ANDROID_EABI_TOOLCHAIN=$prebuiltdir/toolchain/arm-eabi-4.4.0/bin
     export ANDROID_TOOLCHAIN=$ANDROID_EABI_TOOLCHAIN
     export ANDROID_QTOOLS=$T/development/emulator/qtools
     export ANDROID_BUILD_PATHS=:$(get_build_var ANDROID_BUILD_PATHS):$ANDROID_QTOOLS:$ANDROID_TOOLCHAIN:$ANDROID_EABI_TOOLCHAIN$CODE_REVIEWS
@@ -596,6 +596,8 @@
         # Find the closest Android.mk file.
         T=$(gettop)
         local M=$(findmakefile)
+        # Remove the path to top as the makefilepath needs to be relative
+        local M=`echo $M|sed 's:'$T'/::'`
         if [ ! "$T" ]; then
             echo "Couldn't locate the top of the tree.  Try setting TOP."
         elif [ ! "$M" ]; then
@@ -634,6 +636,7 @@
                     ARGS="$ARGS showcommands"
                 else
                     echo "No Android.mk in $DIR."
+                    return 1
                 fi
             fi
         done
@@ -653,6 +656,26 @@
     fi
 }
 
+function cproj()
+{
+    TOPFILE=build/core/envsetup.mk
+    # We redirect cd to /dev/null in case it's aliased to
+    # a command that prints something as a side-effect
+    # (like pushd)
+    local HERE=$PWD
+    T=
+    while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
+        T=$PWD
+        if [ -f "$T/Android.mk" ]; then
+            cd $T
+            return
+        fi
+        cd .. > /dev/null
+    done
+    cd $HERE > /dev/null
+    echo "can't find Android.mk"
+}
+
 function pid()
 {
    local EXE="$1"
@@ -944,14 +967,7 @@
         echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
         return
     fi
-    (cd "$T" && development/testrunner/runtest.py $@)
-}
-
-# TODO: Remove this some time after 1 June 2009
-function runtest_py()
-{
-    echo "runtest_py is obsolete; use runtest instead" >&2
-    return 1
+    ("$T"/development/testrunner/runtest.py $@)
 }
 
 function godir () {
diff --git a/target/board/Android.mk b/target/board/Android.mk
index 64e3a74..fc2f651 100644
--- a/target/board/Android.mk
+++ b/target/board/Android.mk
@@ -20,11 +20,8 @@
   INSTALLED_KERNEL_TARGET :=
 endif
 
-ifneq ($(strip $(TARGET_NO_RADIOIMAGE)),true)
-  INSTALLED_RADIOIMAGE_TARGET := $(PRODUCT_OUT)/radio.img
-else
-  INSTALLED_RADIOIMAGE_TARGET :=
-endif
+# Use the add-radio-file function to add values to this variable.
+INSTALLED_RADIOIMAGE_TARGET :=
 
 ifeq (,$(wildcard $(TARGET_DEVICE_DIR)/AndroidBoard.mk))
   ifeq (,$(wildcard $(TARGET_DEVICE_DIR)/Android.mk))
diff --git a/target/board/generic/BoardConfig.mk b/target/board/generic/BoardConfig.mk
index 6ec2de3..2b72d01 100644
--- a/target/board/generic/BoardConfig.mk
+++ b/target/board/generic/BoardConfig.mk
@@ -1,12 +1,11 @@
 # config.mk
-# 
+#
 # Product-specific compile-time definitions.
 #
 
 # The generic product target doesn't have any hardware-specific pieces.
 TARGET_NO_BOOTLOADER := true
 TARGET_NO_KERNEL := true
-TARGET_NO_RADIOIMAGE := true
 TARGET_CPU_ABI := armeabi
 HAVE_HTC_AUDIO_DRIVER := true
 BOARD_USES_GENERIC_AUDIO := true
diff --git a/target/product/core.mk b/target/product/core.mk
index 347b127..a93ba7c 100644
--- a/target/product/core.mk
+++ b/target/product/core.mk
@@ -3,7 +3,8 @@
 PRODUCT_DEVICE :=
 PRODUCT_POLICY := android.policy_phone
 PRODUCT_PROPERTY_OVERRIDES := \
-    ro.config.notification_sound=F1_New_SMS.ogg
+    ro.config.notification_sound=OnTheHunt.ogg \
+    ro.config.alarm_alert=Alarm_Classic.ogg
 
 PRODUCT_PACKAGES := \
     framework-res \
@@ -25,4 +26,3 @@
     UserDictionaryProvider \
     PackageInstaller \
     Bugreport
-
diff --git a/target/product/generic.mk b/target/product/generic.mk
index b9bc070..b4b1b09 100644
--- a/target/product/generic.mk
+++ b/target/product/generic.mk
@@ -3,11 +3,17 @@
 # you should derive from generic_with_google.mk
 
 PRODUCT_PACKAGES := \
+    AccountAndSyncSettings \
     AlarmClock \
     AlarmProvider \
+    Bluetooth \
+    Calculator \
     Calendar \
     Camera \
+    CertInstaller \
     DrmProvider \
+    Email \
+    Gallery \
     LatinIME \
     Mms \
     Music \
@@ -15,7 +21,6 @@
     Sync \
     Updater \
     CalendarProvider \
-    SubscribedFeedsProvider \
     SyncProvider
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/core.mk)
diff --git a/target/product/generic_with_google.mk b/target/product/generic_with_google.mk
index dddbbb7..ba30f15 100644
--- a/target/product/generic_with_google.mk
+++ b/target/product/generic_with_google.mk
@@ -4,13 +4,16 @@
 # from generic.mk
 
 PRODUCT_PACKAGES := \
-    GoogleContactsProvider \
+    ContactsProvider \
+    GoogleContactsSyncAdapter \
     GoogleSubscribedFeedsProvider \
     com.google.android.gtalkservice \
+    com.google.android.datamessaging \
     com.google.android.maps
 
 PRODUCT_COPY_FILES := \
     vendor/google/frameworks/maps/com.google.android.maps.xml:system/etc/permissions/com.google.android.maps.xml \
+    vendor/google/frameworks/datamessaging/com.google.android.datamessaging.xml:system/etc/permissions/com.google.android.datamessaging.xml \
     vendor/google/apps/GTalkService/com.google.android.gtalkservice.xml:system/etc/permissions/com.google.android.gtalkservice.xml
 
 
diff --git a/target/product/min_dev.mk b/target/product/min_dev.mk
index 7d0fbe6..92ba973 100644
--- a/target/product/min_dev.mk
+++ b/target/product/min_dev.mk
@@ -1,7 +1,8 @@
 
 PRODUCT_POLICY := android.policy_phone
 PRODUCT_PROPERTY_OVERRIDES := \
-    ro.config.notification_sound=F1_New_SMS.ogg
+    ro.config.notification_sound=OnTheHunt.ogg \
+    ro.config.alarm_alert=Alarm_Classic.ogg
 PRODUCT_BRAND := generic
 PRODUCT_NAME := min_dev
 PRODUCT_DEVICE := generic
diff --git a/target/product/sdk.mk b/target/product/sdk.mk
index d847377..ba105f8 100644
--- a/target/product/sdk.mk
+++ b/target/product/sdk.mk
@@ -1,6 +1,7 @@
 PRODUCT_PROPERTY_OVERRIDES :=
 
 PRODUCT_PACKAGES := \
+	AccountAndSyncSettings \
 	AlarmClock \
 	Camera \
 	Calculator \
@@ -23,12 +24,15 @@
 	libWnnEngDic \
 	libWnnJpnDic \
 	libwnndict \
+	CertInstaller \
+	LiveWallpapersPicker \
 	ApiDemos \
 	GestureBuilder \
 	SoftKeyboard
 
 PRODUCT_COPY_FILES := \
-	development/data/etc/vold.conf:system/etc/vold.conf
+	development/data/etc/vold.conf:system/etc/vold.conf \
+	frameworks/base/data/etc/android.hardware.camera.autofocus.xml:system/etc/permissions/android.hardware.camera.autofocus.xml
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/core.mk)
 
@@ -37,6 +41,9 @@
 PRODUCT_NAME := sdk
 PRODUCT_DEVICE := generic
 PRODUCT_LOCALES := \
+	ldpi \
+	hdpi \
+	mdpi \
 	en_US \
 	en_GB \
 	en_CA \
@@ -64,3 +71,10 @@
 	ru_RU \
 	ko_KR
 
+# include available languages for TTS in the system image
+include external/svox/pico/lang/PicoLangDeDeInSystem.mk
+include external/svox/pico/lang/PicoLangEnGBInSystem.mk
+include external/svox/pico/lang/PicoLangEnUsInSystem.mk
+include external/svox/pico/lang/PicoLangEsEsInSystem.mk
+include external/svox/pico/lang/PicoLangFrFrInSystem.mk
+include external/svox/pico/lang/PicoLangItItInSystem.mk
diff --git a/tools/adbs b/tools/adbs
new file mode 100755
index 0000000..8b1fac6
--- /dev/null
+++ b/tools/adbs
@@ -0,0 +1,222 @@
+#!/usr/bin/env python
+
+# 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.
+
+import os
+import re
+import string
+import sys
+
+###############################################################################
+# match "#00  pc 0003f52e  /system/lib/libdvm.so" for example
+###############################################################################
+trace_line = re.compile("(.*)(\#[0-9]+)  (..) ([0-9a-f]{8})  ([^\r\n \t]*)")
+
+# returns a list containing the function name and the file/lineno
+def CallAddr2Line(lib, addr):
+  global symbols_dir
+  global addr2line_cmd
+  global cppfilt_cmd
+
+  if lib != "":
+    cmd = addr2line_cmd + \
+        " -f -e " + symbols_dir + lib + " 0x" + addr
+    stream = os.popen(cmd)
+    lines = stream.readlines()
+    list = map(string.strip, lines)
+  else:
+    list = []
+  if list != []:
+    # Name like "move_forward_type<JavaVMOption>" causes troubles
+    mangled_name = re.sub('<', '\<', list[0]);
+    mangled_name = re.sub('>', '\>', mangled_name);
+    cmd = cppfilt_cmd + " " + mangled_name
+    stream = os.popen(cmd)
+    list[0] = stream.readline()
+    stream.close()
+    list = map(string.strip, list)
+  else:
+    list = [ "(unknown)", "(unknown)" ]
+  return list
+
+
+###############################################################################
+# similar to CallAddr2Line, but using objdump to find out the name of the
+# containing function of the specified address
+###############################################################################
+def CallObjdump(lib, addr):
+  global objdump_cmd
+  global symbols_dir
+
+  unknown = "(unknown)"
+  uname = os.uname()[0]
+  if uname == "Darwin":
+    proc = os.uname()[-1]
+    if proc == "i386":
+      uname = "darwin-x86"
+    else:
+      uname = "darwin-ppc"
+  elif uname == "Linux":
+    uname = "linux-x86"
+  if lib != "":
+    next_addr = string.atoi(addr, 16) + 1
+    cmd = objdump_cmd \
+        + " -C -d --start-address=0x" + addr + " --stop-address=" \
+        + str(next_addr) \
+        + " " + symbols_dir + lib
+    stream = os.popen(cmd)
+    lines = stream.readlines()
+    map(string.strip, lines)
+    stream.close()
+  else:
+    return unknown
+
+  # output looks like
+  #
+  # file format elf32-littlearm
+  #
+  # Disassembly of section .text:
+  #
+  # 0000833c <func+0x4>:
+  #        833c:       701a            strb    r2, [r3, #0]
+  #
+  # we want to extract the "func" part
+  num_lines = len(lines)
+  if num_lines < 2:
+    return unknown
+  func_name = lines[num_lines-2]
+  func_regexp = re.compile("(^.*\<)(.*)(\+.*\>:$)")
+  components = func_regexp.match(func_name)
+  if components is None:
+    return unknown
+  return components.group(2)
+
+###############################################################################
+# determine the symbols directory in the local build
+###############################################################################
+def FindSymbolsDir():
+  global symbols_dir
+
+  try:
+    path = os.environ['ANDROID_PRODUCT_OUT'] + "/symbols"
+  except:
+    cmd = "CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core " \
+      + "SRC_TARGET_DIR=build/target make -f build/core/envsetup.mk " \
+      + "dumpvar-abs-TARGET_OUT_UNSTRIPPED"
+    stream = os.popen(cmd)
+    str = stream.read()
+    stream.close()
+    path = str.strip()
+
+  if (not os.path.exists(path)):
+    print path + " not found!"
+    sys.exit(1)
+
+  symbols_dir = path
+
+###############################################################################
+# determine the path of binutils
+###############################################################################
+def SetupToolsPath():
+  global addr2line_cmd
+  global objdump_cmd
+  global cppfilt_cmd
+  global symbols_dir
+
+  uname = os.uname()[0]
+  if uname == "Darwin":
+    proc = os.uname()[-1]
+    if proc == "i386":
+      uname = "darwin-x86"
+    else:
+      uname = "darwin-ppc"
+  elif uname == "Linux":
+    uname = "linux-x86"
+  prefix = "./prebuilt/" + uname + "/toolchain/arm-eabi-4.4.0/bin/"
+  addr2line_cmd = prefix + "arm-eabi-addr2line"
+
+  if (not os.path.exists(addr2line_cmd)):
+    try:
+      prefix = os.environ['ANDROID_BUILD_TOP'] + "/prebuilt/" + uname + \
+               "/toolchain/arm-eabi-4.4.0/bin/"
+    except:
+      prefix = "";
+
+    addr2line_cmd = prefix + "arm-eabi-addr2line"
+    if (not os.path.exists(addr2line_cmd)):
+      print addr2line_cmd + " not found!"
+      sys.exit(1)
+
+  objdump_cmd = prefix + "arm-eabi-objdump"
+  cppfilt_cmd = prefix + "arm-eabi-c++filt"
+
+###############################################################################
+# look up the function and file/line number for a raw stack trace line
+# groups[0]: log tag
+# groups[1]: stack level
+# groups[2]: "pc"
+# groups[3]: code address
+# groups[4]: library name
+###############################################################################
+def SymbolTranslation(groups):
+  lib_name = groups[4]
+  code_addr = groups[3]
+  caller = CallObjdump(lib_name, code_addr)
+  func_line_pair = CallAddr2Line(lib_name, code_addr)
+
+  # If a callee is inlined to the caller, objdump will see the caller's
+  # address but addr2line will report the callee's address. So the printed
+  # format is desgined to be "caller<-callee  file:line"
+  if (func_line_pair[0] != caller):
+    print groups[0] + groups[1] + " " + caller + "<-" + \
+          '  '.join(func_line_pair[:]) + " "
+  else:
+    print groups[0] + groups[1] + " " + '  '.join(func_line_pair[:]) + " "
+
+###############################################################################
+
+if __name__ == '__main__':
+  # pass the options to adb
+  adb_cmd  = "adb " + ' '.join(sys.argv[1:])
+
+  # setup addr2line_cmd and objdump_cmd
+  SetupToolsPath()
+
+  # setup the symbols directory
+  FindSymbolsDir()
+
+  # invoke the adb command and filter its output
+  stream = os.popen(adb_cmd)
+  while (True):
+    line = stream.readline()
+
+    # EOF reached
+    if (line == ''):
+      break
+
+    # remove the trailing \n
+    line = line.strip()
+
+    # see if this is a stack trace line
+    match = trace_line.match(line)
+    if (match):
+      groups = match.groups()
+      # translate raw address into symbols
+      SymbolTranslation(groups)
+    else:
+      print line
+
+  # adb itself aborts
+  stream.close()
diff --git a/tools/apicheck/src/com/android/apicheck/ApiCheck.java b/tools/apicheck/src/com/android/apicheck/ApiCheck.java
index 20a98ce..c8272dd 100644
--- a/tools/apicheck/src/com/android/apicheck/ApiCheck.java
+++ b/tools/apicheck/src/com/android/apicheck/ApiCheck.java
@@ -127,7 +127,7 @@
     }
 
     private static class MakeHandler extends DefaultHandler {
-            
+
             private ApiInfo mApi;
             private PackageInfo mCurrentPackage;
             private ClassInfo mCurrentClass;
@@ -139,8 +139,9 @@
                 super();
                 mApi = new ApiInfo();
             }
-            
-            public void startElement(String uri, String localName, String qName, 
+
+            @Override
+            public void startElement(String uri, String localName, String qName,
                                      Attributes attributes) {
                 if (qName.equals("package")) {
                     mCurrentPackage = new PackageInfo(attributes.getValue("name"),
@@ -150,25 +151,25 @@
                     // push the old outer scope for later recovery, then set
                     // up the new current class object
                     mClassScope.push(mCurrentClass);
-                    mCurrentClass = new ClassInfo(attributes.getValue("name"), 
+                    mCurrentClass = new ClassInfo(attributes.getValue("name"),
                                                   mCurrentPackage,
                                                   attributes.getValue("extends") ,
-                                                  qName.equals("interface"), 
+                                                  qName.equals("interface"),
                                                   Boolean.valueOf(
                                                       attributes.getValue("abstract")),
                                                   Boolean.valueOf(
                                                       attributes.getValue("static")),
                                                   Boolean.valueOf(
                                                       attributes.getValue("final")),
-                                                  attributes.getValue("deprecated"), 
+                                                  attributes.getValue("deprecated"),
                                                   attributes.getValue("visibility"),
                                                   SourcePositionInfo.fromXml(attributes.getValue("source")),
                                                   mCurrentClass);
                 } else if (qName.equals("method")) {
-                    mCurrentMethod = new MethodInfo(attributes.getValue("name"), 
+                    mCurrentMethod = new MethodInfo(attributes.getValue("name"),
                                                     attributes.getValue("return") ,
                                                     Boolean.valueOf(
-                                                        attributes.getValue("abstract")), 
+                                                        attributes.getValue("abstract")),
                                                     Boolean.valueOf(
                                                         attributes.getValue("native")),
                                                     Boolean.valueOf(
@@ -178,11 +179,11 @@
                                                     Boolean.valueOf(
                                                         attributes.getValue("final")),
                                                     attributes.getValue("deprecated"),
-                                                    attributes.getValue("visibility"), 
+                                                    attributes.getValue("visibility"),
                                                     SourcePositionInfo.fromXml(attributes.getValue("source")),
                                                     mCurrentClass);
                 } else if (qName.equals("constructor")) {
-                    mCurrentMethod = new ConstructorInfo(attributes.getValue("name"), 
+                    mCurrentMethod = new ConstructorInfo(attributes.getValue("name"),
                                                          attributes.getValue("type") ,
                                                          Boolean.valueOf(
                                                              attributes.getValue("static")),
@@ -193,7 +194,7 @@
                                                          SourcePositionInfo.fromXml(attributes.getValue("source")),
                                                          mCurrentClass);
                 } else if (qName.equals("field")) {
-                    FieldInfo fInfo = new FieldInfo(attributes.getValue("name"), 
+                    FieldInfo fInfo = new FieldInfo(attributes.getValue("name"),
                                                     attributes.getValue("type") ,
                                                     Boolean.valueOf(
                                                         attributes.getValue("transient")),
@@ -218,6 +219,8 @@
                     mCurrentClass.addInterface(attributes.getValue("name"));
                 }
             }
+
+            @Override
             public void endElement(String uri, String localName, String qName) {
                 if (qName.equals("method")) {
                     mCurrentClass.addMethod((MethodInfo) mCurrentMethod);
diff --git a/tools/apicheck/src/com/android/apicheck/Errors.java b/tools/apicheck/src/com/android/apicheck/Errors.java
index d7013e3..b0b620e 100644
--- a/tools/apicheck/src/com/android/apicheck/Errors.java
+++ b/tools/apicheck/src/com/android/apicheck/Errors.java
@@ -41,6 +41,7 @@
             return this.msg.compareTo(that.msg);
         }
 
+        @Override
         public String toString() {
             return this.pos.toString() + this.msg;
         }
@@ -115,7 +116,7 @@
     public static Error CHANGED_CLASS = new Error(23, WARNING);
     public static Error CHANGED_DEPRECATED = new Error(24, WARNING);
     public static Error CHANGED_SYNCHRONIZED = new Error(25, ERROR);
-    
+
     public static Error[] ERRORS = {
         PARSE_ERROR,
         ADDED_PACKAGE,
diff --git a/tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java b/tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java
index 477c1d3..276771b 100644
--- a/tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java
+++ b/tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java
@@ -80,6 +80,7 @@
         return new SourcePositionInfo(that.file, line, 0);
     }
 
+    @Override
     public String toString()
     {
         if (this.file == null) {
diff --git a/tools/applypatch/Android.mk b/tools/applypatch/Android.mk
index 5796cef..d20d6c8 100644
--- a/tools/applypatch/Android.mk
+++ b/tools/applypatch/Android.mk
@@ -17,7 +17,7 @@
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := applypatch.c bsdiff.c freecache.c imgpatch.c utils.c
+LOCAL_SRC_FILES := applypatch.c bspatch.c freecache.c imgpatch.c utils.c
 LOCAL_MODULE := libapplypatch
 LOCAL_MODULE_TAGS := eng
 LOCAL_C_INCLUDES += external/bzip2 external/zlib bootable/recovery
@@ -47,12 +47,12 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := imgdiff.c utils.c
+LOCAL_SRC_FILES := imgdiff.c utils.c bsdiff.c
 LOCAL_MODULE := imgdiff
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_TAGS := eng
-LOCAL_C_INCLUDES += external/zlib
-LOCAL_STATIC_LIBRARIES += libz
+LOCAL_C_INCLUDES += external/zlib external/bzip2
+LOCAL_STATIC_LIBRARIES += libz libbz
 
 include $(BUILD_HOST_EXECUTABLE)
 
diff --git a/tools/applypatch/bsdiff.c b/tools/applypatch/bsdiff.c
index d5cd617..b6d342b 100644
--- a/tools/applypatch/bsdiff.c
+++ b/tools/applypatch/bsdiff.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,239 +14,397 @@
  * limitations under the License.
  */
 
-// This file is a nearly line-for-line copy of bspatch.c from the
-// bsdiff-4.3 distribution; the primary differences being how the
-// input and output data are read and the error handling.  Running
-// applypatch with the -l option will display the bsdiff license
-// notice.
+/*
+ * Most of this code comes from bsdiff.c from the bsdiff-4.3
+ * distribution, which is:
+ */
 
-#include <stdio.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
 
 #include <bzlib.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
-#include "mincrypt/sha.h"
-#include "applypatch.h"
+#define MIN(x,y) (((x)<(y)) ? (x) : (y))
 
-void ShowBSDiffLicense() {
-  puts("The bsdiff library used herein is:\n"
-       "\n"
-       "Copyright 2003-2005 Colin Percival\n"
-       "All rights reserved\n"
-       "\n"
-       "Redistribution and use in source and binary forms, with or without\n"
-       "modification, are permitted providing that the following conditions\n"
-       "are met:\n"
-       "1. Redistributions of source code must retain the above copyright\n"
-       "   notice, this list of conditions and the following disclaimer.\n"
-       "2. Redistributions in binary form must reproduce the above copyright\n"
-       "   notice, this list of conditions and the following disclaimer in the\n"
-       "   documentation and/or other materials provided with the distribution.\n"
-       "\n"
-       "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
-       "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
-       "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
-       "ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n"
-       "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
-       "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
-       "OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
-       "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n"
-       "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n"
-       "IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
-       "POSSIBILITY OF SUCH DAMAGE.\n"
-       "\n------------------\n\n"
-       "This program uses Julian R Seward's \"libbzip2\" library, available\n"
-       "from http://www.bzip.org/.\n"
-       );
-}
-
-static off_t offtin(u_char *buf)
+static void split(off_t *I,off_t *V,off_t start,off_t len,off_t h)
 {
-  off_t y;
+	off_t i,j,k,x,tmp,jj,kk;
 
-  y=buf[7]&0x7F;
-  y=y*256;y+=buf[6];
-  y=y*256;y+=buf[5];
-  y=y*256;y+=buf[4];
-  y=y*256;y+=buf[3];
-  y=y*256;y+=buf[2];
-  y=y*256;y+=buf[1];
-  y=y*256;y+=buf[0];
+	if(len<16) {
+		for(k=start;k<start+len;k+=j) {
+			j=1;x=V[I[k]+h];
+			for(i=1;k+i<start+len;i++) {
+				if(V[I[k+i]+h]<x) {
+					x=V[I[k+i]+h];
+					j=0;
+				};
+				if(V[I[k+i]+h]==x) {
+					tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp;
+					j++;
+				};
+			};
+			for(i=0;i<j;i++) V[I[k+i]]=k+j-1;
+			if(j==1) I[k]=-1;
+		};
+		return;
+	};
 
-  if(buf[7]&0x80) y=-y;
+	x=V[I[start+len/2]+h];
+	jj=0;kk=0;
+	for(i=start;i<start+len;i++) {
+		if(V[I[i]+h]<x) jj++;
+		if(V[I[i]+h]==x) kk++;
+	};
+	jj+=start;kk+=jj;
 
-  return y;
+	i=start;j=0;k=0;
+	while(i<jj) {
+		if(V[I[i]+h]<x) {
+			i++;
+		} else if(V[I[i]+h]==x) {
+			tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp;
+			j++;
+		} else {
+			tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp;
+			k++;
+		};
+	};
+
+	while(jj+j<kk) {
+		if(V[I[jj+j]+h]==x) {
+			j++;
+		} else {
+			tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp;
+			k++;
+		};
+	};
+
+	if(jj>start) split(I,V,start,jj-start,h);
+
+	for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1;
+	if(jj==kk-1) I[jj]=-1;
+
+	if(start+len>kk) split(I,V,kk,start+len-kk,h);
 }
 
+static void qsufsort(off_t *I,off_t *V,u_char *old,off_t oldsize)
+{
+	off_t buckets[256];
+	off_t i,h,len;
 
-int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
-                     const char* patch_filename, ssize_t patch_offset,
-                     SinkFn sink, void* token, SHA_CTX* ctx) {
+	for(i=0;i<256;i++) buckets[i]=0;
+	for(i=0;i<oldsize;i++) buckets[old[i]]++;
+	for(i=1;i<256;i++) buckets[i]+=buckets[i-1];
+	for(i=255;i>0;i--) buckets[i]=buckets[i-1];
+	buckets[0]=0;
 
-  unsigned char* new_data;
-  ssize_t new_size;
-  if (ApplyBSDiffPatchMem(old_data, old_size, patch_filename, patch_offset,
-                          &new_data, &new_size) != 0) {
-    return -1;
-  }
+	for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i;
+	I[0]=oldsize;
+	for(i=0;i<oldsize;i++) V[i]=buckets[old[i]];
+	V[oldsize]=0;
+	for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1;
+	I[0]=-1;
 
-  if (sink(new_data, new_size, token) < new_size) {
-    fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno));
-    return 1;
-  }
-  if (ctx) {
-    SHA_update(ctx, new_data, new_size);
-  }
-  free(new_data);
+	for(h=1;I[0]!=-(oldsize+1);h+=h) {
+		len=0;
+		for(i=0;i<oldsize+1;) {
+			if(I[i]<0) {
+				len-=I[i];
+				i-=I[i];
+			} else {
+				if(len) I[i-len]=-len;
+				len=V[I[i]]+1-i;
+				split(I,V,i,len,h);
+				i+=len;
+				len=0;
+			};
+		};
+		if(len) I[i-len]=-len;
+	};
 
-  return 0;
+	for(i=0;i<oldsize+1;i++) I[V[i]]=i;
 }
 
-int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
-                        const char* patch_filename, ssize_t patch_offset,
-                        unsigned char** new_data, ssize_t* new_size) {
+static off_t matchlen(u_char *old,off_t oldsize,u_char *new,off_t newsize)
+{
+	off_t i;
 
-  FILE* f;
-  if ((f = fopen(patch_filename, "rb")) == NULL) {
-    fprintf(stderr, "failed to open patch file\n");
-    return 1;
-  }
+	for(i=0;(i<oldsize)&&(i<newsize);i++)
+		if(old[i]!=new[i]) break;
 
-  // File format:
-  //   0       8       "BSDIFF40"
-  //   8       8       X
-  //   16      8       Y
-  //   24      8       sizeof(newfile)
-  //   32      X       bzip2(control block)
-  //   32+X    Y       bzip2(diff block)
-  //   32+X+Y  ???     bzip2(extra block)
-  // with control block a set of triples (x,y,z) meaning "add x bytes
-  // from oldfile to x bytes from the diff block; copy y bytes from the
-  // extra block; seek forwards in oldfile by z bytes".
+	return i;
+}
 
-  fseek(f, patch_offset, SEEK_SET);
+static off_t search(off_t *I,u_char *old,off_t oldsize,
+		u_char *new,off_t newsize,off_t st,off_t en,off_t *pos)
+{
+	off_t x,y;
 
-  unsigned char header[32];
-  if (fread(header, 1, 32, f) < 32) {
-    fprintf(stderr, "failed to read patch file header\n");
-    return 1;
-  }
+	if(en-st<2) {
+		x=matchlen(old+I[st],oldsize-I[st],new,newsize);
+		y=matchlen(old+I[en],oldsize-I[en],new,newsize);
 
-  if (memcmp(header, "BSDIFF40", 8) != 0) {
-    fprintf(stderr, "corrupt bsdiff patch file header (magic number)\n");
-    return 1;
-  }
+		if(x>y) {
+			*pos=I[st];
+			return x;
+		} else {
+			*pos=I[en];
+			return y;
+		}
+	};
 
-  ssize_t ctrl_len, data_len;
-  ctrl_len = offtin(header+8);
-  data_len = offtin(header+16);
-  *new_size = offtin(header+24);
+	x=st+(en-st)/2;
+	if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) {
+		return search(I,old,oldsize,new,newsize,x,en,pos);
+	} else {
+		return search(I,old,oldsize,new,newsize,st,x,pos);
+	};
+}
 
-  if (ctrl_len < 0 || data_len < 0 || *new_size < 0) {
-    fprintf(stderr, "corrupt patch file header (data lengths)\n");
-    return 1;
-  }
+static void offtout(off_t x,u_char *buf)
+{
+	off_t y;
 
-  fclose(f);
+	if(x<0) y=-x; else y=x;
 
-  int bzerr;
+		buf[0]=y%256;y-=buf[0];
+	y=y/256;buf[1]=y%256;y-=buf[1];
+	y=y/256;buf[2]=y%256;y-=buf[2];
+	y=y/256;buf[3]=y%256;y-=buf[3];
+	y=y/256;buf[4]=y%256;y-=buf[4];
+	y=y/256;buf[5]=y%256;y-=buf[5];
+	y=y/256;buf[6]=y%256;y-=buf[6];
+	y=y/256;buf[7]=y%256;
 
-#define OPEN_AT(f, bzf, offset)                                          \
-  FILE* f;                                                               \
-  BZFILE* bzf;                                                           \
-  if ((f = fopen(patch_filename, "rb")) == NULL) {                       \
-    fprintf(stderr, "failed to open patch file\n");                      \
-    return 1;                                                            \
-  }                                                                      \
-  if (fseeko(f, offset+patch_offset, SEEK_SET)) {                        \
-    fprintf(stderr, "failed to seek in patch file\n");                   \
-    return 1;                                                            \
-  }                                                                      \
-  if ((bzf = BZ2_bzReadOpen(&bzerr, f, 0, 0, NULL, 0)) == NULL) {        \
-    fprintf(stderr, "failed to bzReadOpen in patch file (%d)\n", bzerr); \
-    return 1;                                                            \
-  }
+	if(x<0) buf[7]|=0x80;
+}
 
-  OPEN_AT(cpf, cpfbz2, 32);
-  OPEN_AT(dpf, dpfbz2, 32+ctrl_len);
-  OPEN_AT(epf, epfbz2, 32+ctrl_len+data_len);
+// This is main() from bsdiff.c, with the following changes:
+//
+//    - old, oldsize, new, newsize are arguments; we don't load this
+//      data from files.  old and new are owned by the caller; we
+//      don't free them at the end.
+//
+//    - the "I" block of memory is owned by the caller, who passes a
+//      pointer to *I, which can be NULL.  This way if we call
+//      bsdiff() multiple times with the same 'old' data, we only do
+//      the qsufsort() step the first time.
+//
+int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize,
+           const char* patch_filename)
+{
+	int fd;
+	off_t *I;
+	off_t scan,pos,len;
+	off_t lastscan,lastpos,lastoffset;
+	off_t oldscore,scsc;
+	off_t s,Sf,lenf,Sb,lenb;
+	off_t overlap,Ss,lens;
+	off_t i;
+	off_t dblen,eblen;
+	u_char *db,*eb;
+	u_char buf[8];
+	u_char header[32];
+	FILE * pf;
+	BZFILE * pfbz2;
+	int bz2err;
 
-#undef OPEN_AT
+        if (*IP == NULL) {
+            off_t* V;
+            *IP = malloc((oldsize+1) * sizeof(off_t));
+            V = malloc((oldsize+1) * sizeof(off_t));
+            qsufsort(*IP, V, old, oldsize);
+            free(V);
+        }
+        I = *IP;
 
-  *new_data = malloc(*new_size);
-  if (*new_data == NULL) {
-    fprintf(stderr, "failed to allocate %d bytes of memory for output file\n",
-            (int)*new_size);
-    return 1;
-  }
+	if(((db=malloc(newsize+1))==NULL) ||
+		((eb=malloc(newsize+1))==NULL)) err(1,NULL);
+	dblen=0;
+	eblen=0;
 
-  off_t oldpos = 0, newpos = 0;
-  off_t ctrl[3];
-  off_t len_read;
-  int i;
-  unsigned char buf[8];
-  while (newpos < *new_size) {
-    // Read control data
-    for (i = 0; i < 3; ++i) {
-      len_read = BZ2_bzRead(&bzerr, cpfbz2, buf, 8);
-      if (len_read < 8 || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
-        fprintf(stderr, "corrupt patch (read control)\n");
-        return 1;
-      }
-      ctrl[i] = offtin(buf);
-    }
+	/* Create the patch file */
+	if ((pf = fopen(patch_filename, "w")) == NULL)
+              err(1, "%s", patch_filename);
 
-    // Sanity check
-    if (newpos + ctrl[0] > *new_size) {
-      fprintf(stderr, "corrupt patch (new file overrun)\n");
-      return 1;
-    }
+	/* Header is
+		0	8	 "BSDIFF40"
+		8	8	length of bzip2ed ctrl block
+		16	8	length of bzip2ed diff block
+		24	8	length of new file */
+	/* File is
+		0	32	Header
+		32	??	Bzip2ed ctrl block
+		??	??	Bzip2ed diff block
+		??	??	Bzip2ed extra block */
+	memcpy(header,"BSDIFF40",8);
+	offtout(0, header + 8);
+	offtout(0, header + 16);
+	offtout(newsize, header + 24);
+	if (fwrite(header, 32, 1, pf) != 1)
+		err(1, "fwrite(%s)", patch_filename);
 
-    // Read diff string
-    len_read = BZ2_bzRead(&bzerr, dpfbz2, *new_data + newpos, ctrl[0]);
-    if (len_read < ctrl[0] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
-      fprintf(stderr, "corrupt patch (read diff)\n");
-      return 1;
-    }
+	/* Compute the differences, writing ctrl as we go */
+	if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
+		errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
+	scan=0;len=0;
+	lastscan=0;lastpos=0;lastoffset=0;
+	while(scan<newsize) {
+		oldscore=0;
 
-    // Add old data to diff string
-    for (i = 0; i < ctrl[0]; ++i) {
-      if ((oldpos+i >= 0) && (oldpos+i < old_size)) {
-        (*new_data)[newpos+i] += old_data[oldpos+i];
-      }
-    }
+		for(scsc=scan+=len;scan<newsize;scan++) {
+			len=search(I,old,oldsize,new+scan,newsize-scan,
+					0,oldsize,&pos);
 
-    // Adjust pointers
-    newpos += ctrl[0];
-    oldpos += ctrl[0];
+			for(;scsc<scan+len;scsc++)
+			if((scsc+lastoffset<oldsize) &&
+				(old[scsc+lastoffset] == new[scsc]))
+				oldscore++;
 
-    // Sanity check
-    if (newpos + ctrl[1] > *new_size) {
-      fprintf(stderr, "corrupt patch (new file overrun)\n");
-      return 1;
-    }
+			if(((len==oldscore) && (len!=0)) ||
+				(len>oldscore+8)) break;
 
-    // Read extra string
-    len_read = BZ2_bzRead(&bzerr, epfbz2, *new_data + newpos, ctrl[1]);
-    if (len_read < ctrl[1] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
-      fprintf(stderr, "corrupt patch (read extra)\n");
-      return 1;
-    }
+			if((scan+lastoffset<oldsize) &&
+				(old[scan+lastoffset] == new[scan]))
+				oldscore--;
+		};
 
-    // Adjust pointers
-    newpos += ctrl[1];
-    oldpos += ctrl[2];
-  }
+		if((len!=oldscore) || (scan==newsize)) {
+			s=0;Sf=0;lenf=0;
+			for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {
+				if(old[lastpos+i]==new[lastscan+i]) s++;
+				i++;
+				if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
+			};
 
-  BZ2_bzReadClose(&bzerr, cpfbz2);
-  BZ2_bzReadClose(&bzerr, dpfbz2);
-  BZ2_bzReadClose(&bzerr, epfbz2);
-  fclose(cpf);
-  fclose(dpf);
-  fclose(epf);
+			lenb=0;
+			if(scan<newsize) {
+				s=0;Sb=0;
+				for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {
+					if(old[pos-i]==new[scan-i]) s++;
+					if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };
+				};
+			};
 
-  return 0;
+			if(lastscan+lenf>scan-lenb) {
+				overlap=(lastscan+lenf)-(scan-lenb);
+				s=0;Ss=0;lens=0;
+				for(i=0;i<overlap;i++) {
+					if(new[lastscan+lenf-overlap+i]==
+					   old[lastpos+lenf-overlap+i]) s++;
+					if(new[scan-lenb+i]==
+					   old[pos-lenb+i]) s--;
+					if(s>Ss) { Ss=s; lens=i+1; };
+				};
+
+				lenf+=lens-overlap;
+				lenb-=lens;
+			};
+
+			for(i=0;i<lenf;i++)
+				db[dblen+i]=new[lastscan+i]-old[lastpos+i];
+			for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
+				eb[eblen+i]=new[lastscan+lenf+i];
+
+			dblen+=lenf;
+			eblen+=(scan-lenb)-(lastscan+lenf);
+
+			offtout(lenf,buf);
+			BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
+			if (bz2err != BZ_OK)
+				errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+
+			offtout((scan-lenb)-(lastscan+lenf),buf);
+			BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
+			if (bz2err != BZ_OK)
+				errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+
+			offtout((pos-lenb)-(lastpos+lenf),buf);
+			BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
+			if (bz2err != BZ_OK)
+				errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+
+			lastscan=scan-lenb;
+			lastpos=pos-lenb;
+			lastoffset=pos-scan;
+		};
+	};
+	BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
+	if (bz2err != BZ_OK)
+		errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
+
+	/* Compute size of compressed ctrl data */
+	if ((len = ftello(pf)) == -1)
+		err(1, "ftello");
+	offtout(len-32, header + 8);
+
+	/* Write compressed diff data */
+	if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
+		errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
+	BZ2_bzWrite(&bz2err, pfbz2, db, dblen);
+	if (bz2err != BZ_OK)
+		errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+	BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
+	if (bz2err != BZ_OK)
+		errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
+
+	/* Compute size of compressed diff data */
+	if ((newsize = ftello(pf)) == -1)
+		err(1, "ftello");
+	offtout(newsize - len, header + 16);
+
+	/* Write compressed extra data */
+	if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
+		errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
+	BZ2_bzWrite(&bz2err, pfbz2, eb, eblen);
+	if (bz2err != BZ_OK)
+		errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+	BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
+	if (bz2err != BZ_OK)
+		errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
+
+	/* Seek to the beginning, write the header, and close the file */
+	if (fseeko(pf, 0, SEEK_SET))
+		err(1, "fseeko");
+	if (fwrite(header, 32, 1, pf) != 1)
+		err(1, "fwrite(%s)", patch_filename);
+	if (fclose(pf))
+		err(1, "fclose");
+
+	/* Free the memory we used */
+	free(db);
+	free(eb);
+
+	return 0;
 }
diff --git a/tools/applypatch/bspatch.c b/tools/applypatch/bspatch.c
new file mode 100644
index 0000000..d5cd617
--- /dev/null
+++ b/tools/applypatch/bspatch.c
@@ -0,0 +1,252 @@
+/*
+ * 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.
+ */
+
+// This file is a nearly line-for-line copy of bspatch.c from the
+// bsdiff-4.3 distribution; the primary differences being how the
+// input and output data are read and the error handling.  Running
+// applypatch with the -l option will display the bsdiff license
+// notice.
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <bzlib.h>
+
+#include "mincrypt/sha.h"
+#include "applypatch.h"
+
+void ShowBSDiffLicense() {
+  puts("The bsdiff library used herein is:\n"
+       "\n"
+       "Copyright 2003-2005 Colin Percival\n"
+       "All rights reserved\n"
+       "\n"
+       "Redistribution and use in source and binary forms, with or without\n"
+       "modification, are permitted providing that the following conditions\n"
+       "are met:\n"
+       "1. Redistributions of source code must retain the above copyright\n"
+       "   notice, this list of conditions and the following disclaimer.\n"
+       "2. Redistributions in binary form must reproduce the above copyright\n"
+       "   notice, this list of conditions and the following disclaimer in the\n"
+       "   documentation and/or other materials provided with the distribution.\n"
+       "\n"
+       "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
+       "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
+       "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
+       "ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n"
+       "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
+       "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
+       "OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
+       "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n"
+       "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n"
+       "IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
+       "POSSIBILITY OF SUCH DAMAGE.\n"
+       "\n------------------\n\n"
+       "This program uses Julian R Seward's \"libbzip2\" library, available\n"
+       "from http://www.bzip.org/.\n"
+       );
+}
+
+static off_t offtin(u_char *buf)
+{
+  off_t y;
+
+  y=buf[7]&0x7F;
+  y=y*256;y+=buf[6];
+  y=y*256;y+=buf[5];
+  y=y*256;y+=buf[4];
+  y=y*256;y+=buf[3];
+  y=y*256;y+=buf[2];
+  y=y*256;y+=buf[1];
+  y=y*256;y+=buf[0];
+
+  if(buf[7]&0x80) y=-y;
+
+  return y;
+}
+
+
+int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
+                     const char* patch_filename, ssize_t patch_offset,
+                     SinkFn sink, void* token, SHA_CTX* ctx) {
+
+  unsigned char* new_data;
+  ssize_t new_size;
+  if (ApplyBSDiffPatchMem(old_data, old_size, patch_filename, patch_offset,
+                          &new_data, &new_size) != 0) {
+    return -1;
+  }
+
+  if (sink(new_data, new_size, token) < new_size) {
+    fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno));
+    return 1;
+  }
+  if (ctx) {
+    SHA_update(ctx, new_data, new_size);
+  }
+  free(new_data);
+
+  return 0;
+}
+
+int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
+                        const char* patch_filename, ssize_t patch_offset,
+                        unsigned char** new_data, ssize_t* new_size) {
+
+  FILE* f;
+  if ((f = fopen(patch_filename, "rb")) == NULL) {
+    fprintf(stderr, "failed to open patch file\n");
+    return 1;
+  }
+
+  // File format:
+  //   0       8       "BSDIFF40"
+  //   8       8       X
+  //   16      8       Y
+  //   24      8       sizeof(newfile)
+  //   32      X       bzip2(control block)
+  //   32+X    Y       bzip2(diff block)
+  //   32+X+Y  ???     bzip2(extra block)
+  // with control block a set of triples (x,y,z) meaning "add x bytes
+  // from oldfile to x bytes from the diff block; copy y bytes from the
+  // extra block; seek forwards in oldfile by z bytes".
+
+  fseek(f, patch_offset, SEEK_SET);
+
+  unsigned char header[32];
+  if (fread(header, 1, 32, f) < 32) {
+    fprintf(stderr, "failed to read patch file header\n");
+    return 1;
+  }
+
+  if (memcmp(header, "BSDIFF40", 8) != 0) {
+    fprintf(stderr, "corrupt bsdiff patch file header (magic number)\n");
+    return 1;
+  }
+
+  ssize_t ctrl_len, data_len;
+  ctrl_len = offtin(header+8);
+  data_len = offtin(header+16);
+  *new_size = offtin(header+24);
+
+  if (ctrl_len < 0 || data_len < 0 || *new_size < 0) {
+    fprintf(stderr, "corrupt patch file header (data lengths)\n");
+    return 1;
+  }
+
+  fclose(f);
+
+  int bzerr;
+
+#define OPEN_AT(f, bzf, offset)                                          \
+  FILE* f;                                                               \
+  BZFILE* bzf;                                                           \
+  if ((f = fopen(patch_filename, "rb")) == NULL) {                       \
+    fprintf(stderr, "failed to open patch file\n");                      \
+    return 1;                                                            \
+  }                                                                      \
+  if (fseeko(f, offset+patch_offset, SEEK_SET)) {                        \
+    fprintf(stderr, "failed to seek in patch file\n");                   \
+    return 1;                                                            \
+  }                                                                      \
+  if ((bzf = BZ2_bzReadOpen(&bzerr, f, 0, 0, NULL, 0)) == NULL) {        \
+    fprintf(stderr, "failed to bzReadOpen in patch file (%d)\n", bzerr); \
+    return 1;                                                            \
+  }
+
+  OPEN_AT(cpf, cpfbz2, 32);
+  OPEN_AT(dpf, dpfbz2, 32+ctrl_len);
+  OPEN_AT(epf, epfbz2, 32+ctrl_len+data_len);
+
+#undef OPEN_AT
+
+  *new_data = malloc(*new_size);
+  if (*new_data == NULL) {
+    fprintf(stderr, "failed to allocate %d bytes of memory for output file\n",
+            (int)*new_size);
+    return 1;
+  }
+
+  off_t oldpos = 0, newpos = 0;
+  off_t ctrl[3];
+  off_t len_read;
+  int i;
+  unsigned char buf[8];
+  while (newpos < *new_size) {
+    // Read control data
+    for (i = 0; i < 3; ++i) {
+      len_read = BZ2_bzRead(&bzerr, cpfbz2, buf, 8);
+      if (len_read < 8 || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
+        fprintf(stderr, "corrupt patch (read control)\n");
+        return 1;
+      }
+      ctrl[i] = offtin(buf);
+    }
+
+    // Sanity check
+    if (newpos + ctrl[0] > *new_size) {
+      fprintf(stderr, "corrupt patch (new file overrun)\n");
+      return 1;
+    }
+
+    // Read diff string
+    len_read = BZ2_bzRead(&bzerr, dpfbz2, *new_data + newpos, ctrl[0]);
+    if (len_read < ctrl[0] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
+      fprintf(stderr, "corrupt patch (read diff)\n");
+      return 1;
+    }
+
+    // Add old data to diff string
+    for (i = 0; i < ctrl[0]; ++i) {
+      if ((oldpos+i >= 0) && (oldpos+i < old_size)) {
+        (*new_data)[newpos+i] += old_data[oldpos+i];
+      }
+    }
+
+    // Adjust pointers
+    newpos += ctrl[0];
+    oldpos += ctrl[0];
+
+    // Sanity check
+    if (newpos + ctrl[1] > *new_size) {
+      fprintf(stderr, "corrupt patch (new file overrun)\n");
+      return 1;
+    }
+
+    // Read extra string
+    len_read = BZ2_bzRead(&bzerr, epfbz2, *new_data + newpos, ctrl[1]);
+    if (len_read < ctrl[1] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
+      fprintf(stderr, "corrupt patch (read extra)\n");
+      return 1;
+    }
+
+    // Adjust pointers
+    newpos += ctrl[1];
+    oldpos += ctrl[2];
+  }
+
+  BZ2_bzReadClose(&bzerr, cpfbz2);
+  BZ2_bzReadClose(&bzerr, dpfbz2);
+  BZ2_bzReadClose(&bzerr, epfbz2);
+  fclose(cpf);
+  fclose(dpf);
+  fclose(epf);
+
+  return 0;
+}
diff --git a/tools/applypatch/imgdiff.c b/tools/applypatch/imgdiff.c
index 51835b4..6ff9b6f 100644
--- a/tools/applypatch/imgdiff.c
+++ b/tools/applypatch/imgdiff.c
@@ -119,6 +119,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <sys/types.h>
 
 #include "zlib.h"
 #include "imgdiff.h"
@@ -134,6 +135,8 @@
   size_t source_start;
   size_t source_len;
 
+  off_t* I;             // used by bsdiff
+
   // --- for CHUNK_DEFLATE chunks only: ---
 
   // original (compressed) deflate data
@@ -167,6 +170,10 @@
   }
 }
 
+// from bsdiff.c
+int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize,
+           const char* patch_filename);
+
 unsigned char* ReadZip(const char* filename,
                        int* num_chunks, ImageChunk** chunks,
                        int include_pseudo_chunk) {
@@ -278,6 +285,7 @@
     curr->len = st.st_size;
     curr->data = img;
     curr->filename = NULL;
+    curr->I = NULL;
     ++curr;
     ++*num_chunks;
   }
@@ -292,6 +300,7 @@
       curr->deflate_len = temp_entries[nextentry].deflate_len;
       curr->deflate_data = img + pos;
       curr->filename = temp_entries[nextentry].filename;
+      curr->I = NULL;
 
       curr->len = temp_entries[nextentry].uncomp_len;
       curr->data = malloc(curr->len);
@@ -336,6 +345,7 @@
     }
     curr->data = img + pos;
     curr->filename = NULL;
+    curr->I = NULL;
     pos += curr->len;
 
     ++*num_chunks;
@@ -400,6 +410,7 @@
       curr->type = CHUNK_NORMAL;
       curr->len = GZIP_HEADER_LEN;
       curr->data = p;
+      curr->I = NULL;
 
       pos += curr->len;
       p += curr->len;
@@ -407,6 +418,7 @@
 
       curr->type = CHUNK_DEFLATE;
       curr->filename = NULL;
+      curr->I = NULL;
 
       // We must decompress this chunk in order to discover where it
       // ends, and so we can put the uncompressed data and its length
@@ -452,6 +464,7 @@
       curr->start = pos;
       curr->len = GZIP_FOOTER_LEN;
       curr->data = img+pos;
+      curr->I = NULL;
 
       pos += curr->len;
       p += curr->len;
@@ -475,6 +488,7 @@
       *chunks = realloc(*chunks, *num_chunks * sizeof(ImageChunk));
       ImageChunk* curr = *chunks + (*num_chunks-1);
       curr->start = pos;
+      curr->I = NULL;
 
       // 'pos' is not the offset of the start of a gzip chunk, so scan
       // forward until we find a gzip header.
@@ -591,43 +605,12 @@
     }
   }
 
-  char stemp[] = "/tmp/imgdiff-src-XXXXXX";
-  char ttemp[] = "/tmp/imgdiff-tgt-XXXXXX";
   char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
-  mkstemp(stemp);
-  mkstemp(ttemp);
   mkstemp(ptemp);
 
-  FILE* f = fopen(stemp, "wb");
-  if (f == NULL) {
-    fprintf(stderr, "failed to open src chunk %s: %s\n",
-            stemp, strerror(errno));
-    return NULL;
-  }
-  if (fwrite(src->data, 1, src->len, f) != src->len) {
-    fprintf(stderr, "failed to write src chunk to %s: %s\n",
-            stemp, strerror(errno));
-    return NULL;
-  }
-  fclose(f);
-
-  f = fopen(ttemp, "wb");
-  if (f == NULL) {
-    fprintf(stderr, "failed to open tgt chunk %s: %s\n",
-            ttemp, strerror(errno));
-    return NULL;
-  }
-  if (fwrite(tgt->data, 1, tgt->len, f) != tgt->len) {
-    fprintf(stderr, "failed to write tgt chunk to %s: %s\n",
-            ttemp, strerror(errno));
-    return NULL;
-  }
-  fclose(f);
-
-  char cmd[200];
-  sprintf(cmd, "bsdiff %s %s %s", stemp, ttemp, ptemp);
-  if (system(cmd) != 0) {
-    fprintf(stderr, "failed to run bsdiff: %s\n", strerror(errno));
+  int r = bsdiff(src->data, src->len, &(src->I), tgt->data, tgt->len, ptemp);
+  if (r != 0) {
+    fprintf(stderr, "bsdiff() failed: %d\n", r);
     return NULL;
   }
 
@@ -641,8 +624,6 @@
   unsigned char* data = malloc(st.st_size);
 
   if (tgt->type == CHUNK_NORMAL && tgt->len <= st.st_size) {
-    unlink(stemp);
-    unlink(ttemp);
     unlink(ptemp);
 
     tgt->type = CHUNK_RAW;
@@ -652,7 +633,7 @@
 
   *size = st.st_size;
 
-  f = fopen(ptemp, "rb");
+  FILE* f = fopen(ptemp, "rb");
   if (f == NULL) {
     fprintf(stderr, "failed to open patch %s: %s\n", ptemp, strerror(errno));
     return NULL;
@@ -663,8 +644,6 @@
   }
   fclose(f);
 
-  unlink(stemp);
-  unlink(ttemp);
   unlink(ptemp);
 
   tgt->source_start = src->start;
@@ -784,6 +763,14 @@
   return NULL;
 }
 
+void DumpChunks(ImageChunk* chunks, int num_chunks) {
+    int i;
+    for (i = 0; i < num_chunks; ++i) {
+        printf("chunk %d: type %d start %d len %d\n",
+               i, chunks[i].type, chunks[i].start, chunks[i].len);
+    }
+}
+
 int main(int argc, char** argv) {
   if (argc != 4 && argc != 5) {
     usage:
@@ -829,14 +816,29 @@
     // Verify that the source and target images have the same chunk
     // structure (ie, the same sequence of deflate and normal chunks).
 
+    if (!zip_mode) {
+        // Merge the gzip header and footer in with any adjacent
+        // normal chunks.
+        MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
+        MergeAdjacentNormalChunks(src_chunks, &num_src_chunks);
+    }
+
     if (num_src_chunks != num_tgt_chunks) {
       fprintf(stderr, "source and target don't have same number of chunks!\n");
+      printf("source chunks:\n");
+      DumpChunks(src_chunks, num_src_chunks);
+      printf("target chunks:\n");
+      DumpChunks(tgt_chunks, num_tgt_chunks);
       return 1;
     }
     for (i = 0; i < num_src_chunks; ++i) {
       if (src_chunks[i].type != tgt_chunks[i].type) {
         fprintf(stderr, "source and target don't have same chunk "
                 "structure! (chunk %d)\n", i);
+        printf("source chunks:\n");
+        DumpChunks(src_chunks, num_src_chunks);
+        printf("target chunks:\n");
+        DumpChunks(tgt_chunks, num_tgt_chunks);
         return 1;
       }
     }
diff --git a/tools/apriori/prelinkmap.c b/tools/apriori/prelinkmap.c
index 739c181..9fb00e4 100644
--- a/tools/apriori/prelinkmap.c
+++ b/tools/apriori/prelinkmap.c
@@ -7,11 +7,14 @@
 
 typedef struct mapentry mapentry;
 
+#define MAX_ALIASES 10
+
 struct mapentry
 {
     mapentry *next;
     unsigned base;
-    char name[0];
+    char *names[MAX_ALIASES];
+    int num_names;
 };
 
 static mapentry *maplist = 0;
@@ -22,14 +25,13 @@
 */
 
 #define PRELINK_MIN 0x90000000
-#define PRELINK_MAX 0xB0000000
+#define PRELINK_MAX 0xBFFFFFFF
 
 void pm_init(const char *file)
 {
     unsigned line = 0;
     char buf[256];
     char *x;
-    unsigned n;
     FILE *fp;
     mapentry *me;
     unsigned last = -1UL;
@@ -65,26 +67,52 @@
             continue;
         }
         
-        n = strtoul(x, 0, 16);
-        /* Note that this is not the only bounds check.  If a library's size
-           exceeds its slot as defined in the prelink map, the prelinker will
-           exit with an error.  See pm_report_library_size_in_memory().
-        */
-        FAILIF((n < PRELINK_MIN) || (n > PRELINK_MAX),
-               "%s:%d base 0x%08x out of range.\n",
-               file, line, n);
-        
-        me = malloc(sizeof(mapentry) + strlen(buf) + 1);
-        FAILIF(me == NULL, "Out of memory parsing %s\n", file);
+        if (isalpha(*x)) {
+            /* Assume that this is an alias, and look through the list of
+               already-installed libraries.
+            */
+            me = maplist;
+            while(me) {
+                /* The strlen() call ignores the newline at the end of x */
+                if (!strncmp(me->names[0], x, strlen(me->names[0]))) {
+                    PRINT("Aliasing library %s to %s at %08x\n",
+                          buf, x, me->base);
+                    break;
+                }
+                me = me->next;
+            }
+            FAILIF(!me, "Nonexistent alias %s -> %s\n", buf, x);
+        }
+        else {
+            unsigned n = strtoul(x, 0, 16);
+            /* Note that this is not the only bounds check.  If a library's
+               size exceeds its slot as defined in the prelink map, the
+               prelinker will exit with an error.  See
+               pm_report_library_size_in_memory().
+            */
+            FAILIF((n < PRELINK_MIN) || (n > PRELINK_MAX),
+                   "%s:%d base 0x%08x out of range.\n",
+                   file, line, n);
 
-        FAILIF(last <= n, "The prelink map is not in descending order "
-               "at entry %s (%08x)!\n", buf, n);
-        last = n;
-        
-        me->base = n;
-        strcpy(me->name, buf);
-        me->next = maplist;
-        maplist = me;        
+            me = malloc(sizeof(mapentry));
+            FAILIF(me == NULL, "Out of memory parsing %s\n", file);
+
+            FAILIF(last <= n, "The prelink map is not in descending order "
+                   "at entry %s (%08x)!\n", buf, n);
+            last = n;
+
+            me->base = n;
+            me->next = maplist;
+            me->num_names = 0;
+            maplist = me;
+        }
+
+        FAILIF(me->num_names >= MAX_ALIASES,
+               "Too many aliases for library %s, maximum is %d.\n",
+               me->names[0],
+               MAX_ALIASES);
+        me->names[me->num_names] = strdup(buf);
+        me->num_names++;
     }
 
     fclose(fp);
@@ -99,41 +127,44 @@
 {
     char *x;
     mapentry *me;
+    int n;
     
     x = strrchr(name,'/');
     if(x) name = x+1;
 
     for(me = maplist; me; me = me->next){
-        if(!strcmp(name, me->name)) {
-            off_t slot = me->next ? me->next->base : PRELINK_MAX;
-            slot -= me->base;
-            FAILIF(fsize > slot,
-                   "prelink map error: library %s@0x%08x is too big "
-                   "at %lld bytes, it runs %lld bytes into "
-                   "library %s@0x%08x!\n",
-                   me->name, me->base, fsize, fsize - slot,
-                   me->next->name, me->next->base);
-            break;
+        for (n = 0; n < me->num_names; n++) {
+            if(!strcmp(name, me->names[n])) {
+                off_t slot = me->next ? me->next->base : PRELINK_MAX;
+                slot -= me->base;
+                FAILIF(fsize > slot,
+                       "prelink map error: library %s@0x%08x is too big "
+                       "at %lld bytes, it runs %lld bytes into "
+                       "library %s@0x%08x!\n",
+                       me->names[0], me->base, fsize, fsize - slot,
+                       me->next->names[0], me->next->base);
+                return;
+            }
         }
     }
     
-    FAILIF(!me,"library '%s' not in prelink map\n", name);
+    FAILIF(1, "library '%s' not in prelink map\n", name);
 }
 
 unsigned pm_get_next_link_address(const char *lookup_name)
 {
     char *x;
     mapentry *me;
+    int n;
     
     x = strrchr(lookup_name,'/');
     if(x) lookup_name = x+1;
     
-    for(me = maplist; me; me = me->next){
-        if(!strcmp(lookup_name, me->name)) {
-            return me->base;
-        }
-    }
-    
-    FAILIF(1==1,"library '%s' not in prelink map\n", lookup_name);
+    for(me = maplist; me; me = me->next)
+        for (n = 0; n < me->num_names; n++)
+            if(!strcmp(lookup_name, me->names[n]))
+                return me->base;
+
+    FAILIF(1, "library '%s' not in prelink map\n", lookup_name);
     return 0;
 }
diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh
index 5c738a2..af5aa47 100755
--- a/tools/buildinfo.sh
+++ b/tools/buildinfo.sh
@@ -24,6 +24,7 @@
 echo "ro.product.manufacturer=$PRODUCT_MANUFACTURER"
 echo "ro.product.locale.language=$PRODUCT_DEFAULT_LANGUAGE"
 echo "ro.product.locale.region=$PRODUCT_DEFAULT_REGION"
+echo "ro.wifi.channels=$PRODUCT_DEFAULT_WIFI_CHANNELS"
 echo "ro.board.platform=$TARGET_BOARD_PLATFORM"
 
 echo "# ro.build.product is obsolete; use ro.product.device"
diff --git a/tools/dexpreopt/Config.mk b/tools/dexpreopt/Config.mk
index c6639b2..443b8c9 100644
--- a/tools/dexpreopt/Config.mk
+++ b/tools/dexpreopt/Config.mk
@@ -77,6 +77,13 @@
     $(shell echo "$(p) $(PACKAGES.$(p).CERTIFICATE) $(PACKAGES.$(p).PRIVATE_KEY)" >> $(dexpreopt_package_certs_file)))
 endif
 
+# The kernel used for ARMv7 system images is different
+ifeq ($(TARGET_ARCH_VARIANT),armv7-a)
+BUILD_DEXPREOPT_KERNEL := prebuilt/android-arm/kernel/kernel-qemu-armv7
+else
+BUILD_DEXPREOPT_KERNEL := prebuilt/android-arm/kernel/kernel-qemu
+endif
+
 # Build an optimized image from the unoptimized image
 BUILT_DEXPREOPT_SYSTEMIMAGE := $(intermediates)/system.img
 $(BUILT_DEXPREOPT_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE_UNOPT)
@@ -99,7 +106,7 @@
 	$(hide) \
 	    PATH=$(HOST_OUT_EXECUTABLES):$$PATH \
 	    $(DEXPREOPT) \
-		    --kernel prebuilt/android-arm/kernel/kernel-qemu \
+		    --kernel $(BUILD_DEXPREOPT_KERNEL) \
 		    --ramdisk $(BUILT_DEXPREOPT_RAMDISK) \
 		    --image $(BUILT_SYSTEMIMAGE_UNOPT) \
 		    --system $(PRODUCT_OUT) \
diff --git a/tools/droiddoc/src/AnnotationInstanceInfo.java b/tools/droiddoc/src/AnnotationInstanceInfo.java
index 07d4aa3..c4abc7e 100644
--- a/tools/droiddoc/src/AnnotationInstanceInfo.java
+++ b/tools/droiddoc/src/AnnotationInstanceInfo.java
@@ -35,6 +35,7 @@
         return mElementValues;
     }
 
+    @Override
     public String toString()
     {
         StringBuilder str = new StringBuilder();
diff --git a/tools/droiddoc/src/AttrTagInfo.java b/tools/droiddoc/src/AttrTagInfo.java
index abc5452..7f1b4d9 100644
--- a/tools/droiddoc/src/AttrTagInfo.java
+++ b/tools/droiddoc/src/AttrTagInfo.java
@@ -98,7 +98,8 @@
     public FieldInfo reference() {
         return REF_COMMAND.equals(mCommand) ? mRefField : null;
     }
-    
+
+    @Override
     public String name() {
         return NAME_COMMAND.equals(mCommand) ? mAttrName : null;
     }
@@ -107,6 +108,7 @@
         return DESCRIPTION_COMMAND.equals(mCommand) ? mDescrComment : null;
     }
 
+    @Override
     public void makeHDF(HDF data, String base)
     {
         super.makeHDF(data, base);
diff --git a/tools/droiddoc/src/ClassInfo.java b/tools/droiddoc/src/ClassInfo.java
index 5c61941..f3f11de 100644
--- a/tools/droiddoc/src/ClassInfo.java
+++ b/tools/droiddoc/src/ClassInfo.java
@@ -101,7 +101,7 @@
         mSelfFields = null;
         mSelfAttributes = null;
         mDeprecatedKnown = false;
-        
+
         Arrays.sort(mEnumConstants, FieldInfo.comparator);
         Arrays.sort(mInnerClasses, ClassInfo.comparator);
     }
@@ -111,16 +111,16 @@
         // objects
         selfAttributes();
     }
-    
+
     public void init3(TypeInfo[] types, ClassInfo[] realInnerClasses){
       mTypeParameters = types;
       mRealInnerClasses = realInnerClasses;
     }
-    
+
     public ClassInfo[] getRealInnerClasses(){
       return mRealInnerClasses;
     }
-    
+
     public TypeInfo[] getTypeParameters(){
       return mTypeParameters;
     }
@@ -146,6 +146,7 @@
         }
     }
 
+    @Override
     public ContainerInfo parent()
     {
         return this;
@@ -351,7 +352,7 @@
     {
         return comment().briefTags();
     }
-    
+
     public boolean isDeprecated() {
         boolean deprecated = false;
         if (!mDeprecatedKnown) {
@@ -551,7 +552,7 @@
     public MethodInfo[] allSelfMethods() {
         return mAllSelfMethods;
     }
-    
+
     public void addMethod(MethodInfo method) {
         MethodInfo[] methods = new MethodInfo[mAllSelfMethods.length + 1];
         int i = 0;
@@ -596,7 +597,7 @@
                     }
                 }
             }
-            
+
             //constructors too
            for (MethodInfo m: constructors()) {
               for (AttrTagInfo tag: m.comment().attrTags()) {
@@ -1136,7 +1137,11 @@
         if (kind != null) {
             data.setValue(base + ".kind", kind);
         }
-        
+
+        if (cl.mIsIncluded) {
+            data.setValue(base + ".included", "true");
+        }
+
         // xml attributes
         i=0;
         for (AttributeInfo attr: cl.selfAttributes()) {
@@ -1170,6 +1175,7 @@
         }
     }
 
+    @Override
     public boolean isHidden()
     {
         int val = mHidden;
@@ -1301,7 +1307,7 @@
                 return f;
             }
         }
-        
+
         // then look at our enum constants (these are really fields, maybe
         // they should be mixed into fields().  not sure)
         for (FieldInfo f: enumConstants()) {
@@ -1346,11 +1352,11 @@
             return false;
         }
     }
-    
+
     public void setNonWrittenConstructors(MethodInfo[] nonWritten) {
         mNonWrittenConstructors = nonWritten;
     }
-    
+
     public MethodInfo[] getNonWrittenConstructors() {
         return mNonWrittenConstructors;
     }
@@ -1377,23 +1383,24 @@
         }
         return null;
     }
-    
+
     public void setHiddenMethods(MethodInfo[] mInfo){
         mHiddenMethods = mInfo;
     }
     public MethodInfo[] getHiddenMethods(){
         return mHiddenMethods;
     }
+    @Override
     public String toString(){
         return this.qualifiedName();
     }
-    
+
     public void setReasonIncluded(String reason) {
         mReasonIncluded = reason;
     }
-    
+
     public String getReasonIncluded() {
-        return mReasonIncluded; 
+        return mReasonIncluded;
     }
 
     private ClassDoc mClass;
diff --git a/tools/droiddoc/src/Comment.java b/tools/droiddoc/src/Comment.java
index 3f1bf6c..553cdf2 100644
--- a/tools/droiddoc/src/Comment.java
+++ b/tools/droiddoc/src/Comment.java
@@ -157,7 +157,7 @@
         else if (name.equals("@literal")) {
             mInlineTagsList.add(new LiteralTagInfo(name, name, text, pos));
         }
-        else if (name.equals("@hide") || name.equals("@doconly")) {
+        else if (name.equals("@hide") || name.equals("@pending") || name.equals("@doconly")) {
             // nothing
         }
         else if (name.equals("@attr")) {
@@ -206,7 +206,7 @@
         for (int i=0; i<N; i++) {
             if (mInlineTagsList.get(i).name().equals("@more")) {
                 more = i;
-            } 
+            }
         }
           if (more >= 0) {
             for (int i=0; i<more; i++) {
@@ -225,7 +225,7 @@
                     }
                 }
                 mBriefTagsList.add(t);
-                
+
             }
         }
     }
@@ -307,12 +307,12 @@
                 mHidden = 0;
                 return false;
             }
-            boolean b = mText.indexOf("@hide") >= 0;
+            boolean b = mText.indexOf("@hide") >= 0 || mText.indexOf("@pending") >= 0;
             mHidden = b ? 1 : 0;
             return b;
         }
     }
-    
+
     public boolean isDocOnly() {
         if (mDocOnly >= 0) {
             return mDocOnly != 0;
@@ -391,5 +391,5 @@
     ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>();
     ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>();
 
-    
+
 }
diff --git a/tools/droiddoc/src/Converter.java b/tools/droiddoc/src/Converter.java
index 4014f7f..ee911f4 100644
--- a/tools/droiddoc/src/Converter.java
+++ b/tools/droiddoc/src/Converter.java
@@ -238,6 +238,7 @@
     }
     private static Cache mClasses = new Cache()
     {
+        @Override
         protected Object make(Object o)
         {
             ClassDoc c = (ClassDoc)o;
@@ -268,19 +269,21 @@
             }
             return cl;
         }
+        @Override
         protected void made(Object o, Object r)
         {
             if (mClassesNeedingInit == null) {
                 initClass((ClassDoc)o, (ClassInfo)r);
                 ((ClassInfo)r).init2();
             }
-        } 
+        }
+        @Override
         ClassInfo[] all()
         {
             return (ClassInfo[])mCache.values().toArray(new ClassInfo[mCache.size()]);
         }
     };
-    
+
     private static MethodInfo[] getHiddenMethods(MethodDoc[] methods){
       if (methods == null) return null;
       ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
@@ -342,7 +345,7 @@
         }
         return out.toArray(new MethodInfo[out.size()]);
     }
-    
+
     private static MethodInfo[] convertNonWrittenConstructors(ConstructorDoc[] methods)
     {
         if (methods == null) return null;
@@ -367,6 +370,7 @@
     }
     private static Cache mMethods = new Cache()
     {
+        @Override
         protected Object make(Object o)
         {
             if (o instanceof AnnotationTypeElementDoc) {
@@ -374,7 +378,7 @@
                 MethodInfo result = new MethodInfo(
                                 m.getRawCommentText(),
                                 Converter.convertTypes(m.typeParameters()),
-                                m.name(), m.signature(), 
+                                m.name(), m.signature(),
                                 Converter.obtainClass(m.containingClass()),
                                 Converter.obtainClass(m.containingClass()),
                                 m.isPublic(), m.isProtected(),
@@ -399,7 +403,7 @@
                 MethodInfo result = new MethodInfo(
                                 m.getRawCommentText(),
                                 Converter.convertTypes(m.typeParameters()),
-                                m.name(), m.signature(), 
+                                m.name(), m.signature(),
                                 Converter.obtainClass(m.containingClass()),
                                 Converter.obtainClass(m.containingClass()),
                                 m.isPublic(), m.isProtected(),
@@ -424,7 +428,7 @@
                 MethodInfo result = new MethodInfo(
                                 m.getRawCommentText(),
                                 Converter.convertTypes(m.typeParameters()),
-                                m.name(), m.signature(), 
+                                m.name(), m.signature(),
                                 Converter.obtainClass(m.containingClass()),
                                 Converter.obtainClass(m.containingClass()),
                                 m.isPublic(), m.isProtected(),
@@ -472,6 +476,7 @@
     }
     private static Cache mFields = new Cache()
     {
+        @Override
         protected Object make(Object o)
         {
             FieldDoc f = (FieldDoc)o;
@@ -496,6 +501,7 @@
     }
     private static Cache mPackagees = new Cache()
     {
+        @Override
         protected Object make(Object o)
         {
             PackageDoc p = (PackageDoc)o;
@@ -510,7 +516,8 @@
     }
     private static Cache mTypes = new Cache()
     {
-       protected Object make(Object o)
+       @Override
+    protected Object make(Object o)
        {
            Type t = (Type)o;
            String simpleTypeName;
@@ -524,6 +531,7 @@
                    Converter.obtainClass(t.asClassDoc()));
            return ti;
        }
+        @Override
         protected void made(Object o, Object r)
         {
             Type t = (Type)o;
@@ -545,8 +553,9 @@
                              Converter.convertTypes(t.asWildcardType().extendsBounds()));
             }
         }
+        @Override
         protected Object keyFor(Object o)
-        {  
+        {
             Type t = (Type)o;
             String keyString = o.getClass().getName() + "/" + o.toString() + "/";
             if (t.asParameterizedType() != null){
@@ -584,13 +593,13 @@
             }else{
               keyString += "NoWildCardType//";
             }
-            
-            
-            
+
+
+
             return keyString;
         }
     };
-    
+
 
 
     private static MemberInfo obtainMember(MemberDoc o)
@@ -599,6 +608,7 @@
     }
     private static Cache mMembers = new Cache()
     {
+        @Override
         protected Object make(Object o)
         {
             if (o instanceof MethodDoc) {
@@ -633,6 +643,7 @@
     }
     private static Cache mAnnotationInstances = new Cache()
     {
+        @Override
         protected Object make(Object o)
         {
             AnnotationDesc a = (AnnotationDesc)o;
diff --git a/tools/droiddoc/src/DroidDoc.java b/tools/droiddoc/src/DroidDoc.java
index 4ff26bc..f48b56c 100644
--- a/tools/droiddoc/src/DroidDoc.java
+++ b/tools/droiddoc/src/DroidDoc.java
@@ -40,7 +40,7 @@
     private static final int TYPE_WIDGET = 1;
     private static final int TYPE_LAYOUT = 2;
     private static final int TYPE_LAYOUT_PARAM = 3;
-    
+
     public static final int SHOW_PUBLIC = 0x00000001;
     public static final int SHOW_PROTECTED = 0x00000003;
     public static final int SHOW_PACKAGE = 0x00000007;
@@ -84,7 +84,7 @@
         }
         return false;
     }
-    
+
     public static boolean start(RootDoc r)
     {
         String keepListFile = null;
@@ -95,6 +95,7 @@
         String stubsDir = null;
         //Create the dependency graph for the stubs directory
         boolean apiXML = false;
+        boolean noDocs = false;
         String apiFile = null;
         String debugStubsFile = "";
         HashSet<String> stubPackages = null;
@@ -187,6 +188,9 @@
                 apiXML = true;
                 apiFile = a[1];
             }
+            else if (a[0].equals("-nodocs")) {
+                noDocs = true;
+            }
             else if (a[0].equals("-since")) {
                 sinceTagger.addVersion(a[1], a[2]);
             }
@@ -200,62 +204,70 @@
         // Set up the data structures
         Converter.makeInfo(r);
 
-        // Files for proofreading
-        if (proofreadFile != null) {
-            Proofread.initProofread(proofreadFile);
+        if (!noDocs) {
+            long startTime = System.nanoTime();
+
+            // Apply @since tags from the XML file
+            sinceTagger.tagAll(Converter.rootClasses());
+
+            // Files for proofreading
+            if (proofreadFile != null) {
+                Proofread.initProofread(proofreadFile);
+            }
+            if (todoFile != null) {
+                TodoFile.writeTodoFile(todoFile);
+            }
+
+            // HTML Pages
+            if (ClearPage.htmlDir != null) {
+                writeHTMLPages();
+            }
+
+            // Navigation tree
+            NavTree.writeNavTree(javadocDir);
+
+            // Packages Pages
+            writePackages(javadocDir
+                            + (ClearPage.htmlDir!=null
+                                ? "packages" + htmlExtension
+                                : "index" + htmlExtension));
+
+            // Classes
+            writeClassLists();
+            writeClasses();
+            writeHierarchy();
+     //      writeKeywords();
+
+            // Lists for JavaScript
+            writeLists();
+            if (keepListFile != null) {
+                writeKeepList(keepListFile);
+            }
+
+            // Sample Code
+            for (SampleCode sc: sampleCodes) {
+                sc.write();
+            }
+
+            // Index page
+            writeIndex();
+
+            Proofread.finishProofread(proofreadFile);
+
+            if (sdkValuePath != null) {
+                writeSdkValues(sdkValuePath);
+            }
+
+            long time = System.nanoTime() - startTime;
+            System.out.println("DroidDoc took " + (time / 1000000000) + " sec. to write docs to "
+                    + ClearPage.outputDir);
         }
-        if (todoFile != null) {
-            TodoFile.writeTodoFile(todoFile);
-        }
-
-        // Apply @since tags from the XML file
-        sinceTagger.tagAll(Converter.rootClasses());
-
-        // HTML Pages
-        if (ClearPage.htmlDir != null) {
-            writeHTMLPages();
-        }
-
-        // Navigation tree
-        NavTree.writeNavTree(javadocDir);
-
-        // Packages Pages
-        writePackages(javadocDir
-                        + (ClearPage.htmlDir!=null
-                            ? "packages" + htmlExtension
-                            : "index" + htmlExtension));
-
-        // Classes
-        writeClassLists();
-        writeClasses();
-        writeHierarchy();
- //      writeKeywords();
-
-        // Lists for JavaScript
-        writeLists();
-        if (keepListFile != null) {
-            writeKeepList(keepListFile);
-        }
-
-        // Sample Code
-        for (SampleCode sc: sampleCodes) {
-            sc.write();
-        }
-
-        // Index page
-        writeIndex();
-
-        Proofread.finishProofread(proofreadFile);
 
         // Stubs
         if (stubsDir != null) {
             Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
         }
 
-        if (sdkValuePath != null) {
-            writeSdkValues(sdkValuePath);
-        }
-
         Errors.printErrors();
         return !Errors.hadError;
     }
@@ -401,6 +413,9 @@
         if (option.equals("-apixml")) {
             return 2;
         }
+        if (option.equals("-nodocs")) {
+            return 1;
+        }
         if (option.equals("-since")) {
             return 3;
         }
@@ -777,7 +792,7 @@
         data.setValue("package.since", pkg.getSince());
         data.setValue("package.descr", "...description...");
 
-        makeClassListHDF(data, "package.interfaces", 
+        makeClassListHDF(data, "package.interfaces",
                          ClassInfo.sortByName(pkg.interfaces()));
         makeClassListHDF(data, "package.classes",
                          ClassInfo.sortByName(pkg.ordinaryClasses()));
@@ -871,7 +886,7 @@
         HDF data = makeHDF();
 
         Collections.sort(keywords);
-        
+
         int i=0;
         for (KeywordEntry entry: keywords) {
             String base = "keywords." + entry.firstChar() + "." + i;
@@ -964,10 +979,11 @@
     }
 
     /**
-     * Returns true if the given element has an @hide annotation.
+     * Returns true if the given element has an @hide or @pending annotation.
      */
     private static boolean hasHideAnnotation(Doc doc) {
-        return doc.getRawCommentText().indexOf("@hide") != -1;
+        String comment = doc.getRawCommentText();
+        return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -1;
     }
 
     /**
@@ -1059,7 +1075,7 @@
             if (methodName.equals("getRawCommentText")) {
                 return filterComment((String) method.invoke(target, args));
             }
-            
+
             // escape "&" in disjunctive types.
             if (proxy instanceof Type && methodName.equals("toString")) {
                 return ((String) method.invoke(target, args))
@@ -1114,7 +1130,7 @@
             throw new RuntimeException("invalid scope for object " + scoped);
         }
     }
-    
+
     /**
      * Collect the values used by the Dev tools and write them in files packaged with the SDK
      * @param output the ouput directory for the files.
@@ -1124,16 +1140,16 @@
         ArrayList<String> broadcastActions = new ArrayList<String>();
         ArrayList<String> serviceActions = new ArrayList<String>();
         ArrayList<String> categories = new ArrayList<String>();
-        
+
         ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>();
         ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>();
         ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>();
-        
+
         ClassInfo[] classes = Converter.allClasses();
 
         // Go through all the fields of all the classes, looking SDK stuff.
         for (ClassInfo clazz : classes) {
-            
+
             // first check constant fields for the SdkConstant annotation.
             FieldInfo[] fields = clazz.allSelfFields();
             for (FieldInfo field : fields) {
@@ -1162,7 +1178,7 @@
                     }
                 }
             }
-            
+
             // Now check the class for @Widget or if its in the android.widget package
             // (unless the class is hidden or abstract, or non public)
             if (clazz.isHidden() == false && clazz.isPublic() && clazz.isAbstract() == false) {
@@ -1181,7 +1197,7 @@
                         }
                     }
                 }
-                
+
                 if (annotated == false) {
                     // lets check if this is inside android.widget
                     PackageInfo pckg = clazz.containingPackage();
@@ -1221,7 +1237,7 @@
 
         Collections.sort(categories);
         writeValues(output + "/categories.txt", categories);
-        
+
         // before writing the list of classes, we do some checks, to make sure the layout params
         // are enclosed by a layout class (and not one that has been declared as a widget)
         for (int i = 0 ; i < layoutParams.size();) {
@@ -1233,10 +1249,10 @@
                 i++;
             }
         }
-        
+
         writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams);
     }
-    
+
     /**
      * Writes a list of values into a text files.
      * @param pathname the absolute os path of the output file.
@@ -1248,7 +1264,7 @@
         try {
             fw = new FileWriter(pathname, false);
             bw = new BufferedWriter(fw);
-            
+
             for (String value : values) {
                 bw.append(value).append('\n');
             }
@@ -1282,7 +1298,7 @@
         try {
             fw = new FileWriter(pathname, false);
             bw = new BufferedWriter(fw);
-            
+
             // write the 3 types of classes.
             for (ClassInfo clazz : widgets) {
                 writeClass(bw, clazz, 'W');
@@ -1325,7 +1341,7 @@
         }
         writer.append('\n');
     }
-    
+
     /**
      * Checks the inheritance of {@link ClassInfo} objects. This method return
      * <ul>
@@ -1333,7 +1349,7 @@
      * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li>
      * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends <code>android.view.ViewGroup$LayoutParams</code></li>
      * <li>{@link #TYPE_NONE}: in all other cases</li>
-     * </ul> 
+     * </ul>
      * @param clazz the {@link ClassInfo} to check.
      */
     private static int checkInheritance(ClassInfo clazz) {
@@ -1344,12 +1360,12 @@
         } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
             return TYPE_LAYOUT_PARAM;
         }
-        
+
         ClassInfo parent = clazz.superclass();
         if (parent != null) {
             return checkInheritance(parent);
         }
-        
+
         return TYPE_NONE;
     }
 }
diff --git a/tools/droiddoc/src/Errors.java b/tools/droiddoc/src/Errors.java
index 95439f1..77852f8 100644
--- a/tools/droiddoc/src/Errors.java
+++ b/tools/droiddoc/src/Errors.java
@@ -41,6 +41,7 @@
             return this.msg.compareTo(that.msg);
         }
 
+        @Override
         public String toString() {
             String whereText = this.pos == null ? "unknown: " : this.pos.toString() + ':';
             return whereText + this.msg;
diff --git a/tools/droiddoc/src/FieldInfo.java b/tools/droiddoc/src/FieldInfo.java
index 1c975e4..d9371e8 100644
--- a/tools/droiddoc/src/FieldInfo.java
+++ b/tools/droiddoc/src/FieldInfo.java
@@ -26,7 +26,7 @@
             return a.name().compareTo(b.name());
         }
     };
-    
+
     public FieldInfo(String name, ClassInfo containingClass, ClassInfo realContainingClass,
                         boolean isPublic, boolean isProtected,
                         boolean isPackagePrivate, boolean isPrivate,
@@ -92,7 +92,7 @@
     {
         return constantLiteralValue(mConstantValue);
     }
-    
+
     public boolean isDeprecated() {
         boolean deprecated = false;
         if (!mDeprecatedKnown) {
@@ -124,7 +124,7 @@
             if (val instanceof Boolean
                     || val instanceof Byte
                     || val instanceof Short
-                    || val instanceof Integer) 
+                    || val instanceof Integer)
             {
                 str = val.toString();
             }
@@ -291,6 +291,7 @@
         }
     }
 
+    @Override
     public boolean isExecutable()
     {
         return false;
diff --git a/tools/droiddoc/src/MemberInfo.java b/tools/droiddoc/src/MemberInfo.java
index 2a2572a..05da583 100644
--- a/tools/droiddoc/src/MemberInfo.java
+++ b/tools/droiddoc/src/MemberInfo.java
@@ -115,6 +115,7 @@
         return mIsSynthetic;
     }
 
+    @Override
     public ContainerInfo parent()
     {
         return mContainingClass;
@@ -130,7 +131,7 @@
     {
         return mKind;
     }
-    
+
     public AnnotationInstanceInfo[] annotations()
     {
         return mAnnotations;
diff --git a/tools/droiddoc/src/MethodInfo.java b/tools/droiddoc/src/MethodInfo.java
index bded88b..3211038 100644
--- a/tools/droiddoc/src/MethodInfo.java
+++ b/tools/droiddoc/src/MethodInfo.java
@@ -25,9 +25,9 @@
             return a.name().compareTo(b.name());
         }
     };
-    
+
     private class InlineTags implements InheritedTags
-    { 
+    {
         public TagInfo[] tags()
         {
             return comment().tags();
@@ -42,7 +42,7 @@
             }
         }
     }
-    
+
     private static void addInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue)
     {
         for (ClassInfo i: ifaces) {
@@ -79,7 +79,7 @@
         }
         return null;
     }
-    
+
     private static void addRealInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue)
     {
         for (ClassInfo i: ifaces) {
@@ -92,7 +92,7 @@
             addInterfaces(i.realInterfaces(), queue);
         }
     }
-    
+
     public MethodInfo findRealOverriddenMethod(String name, String signature, HashSet notStrippable) {
         if (mReturnType == null) {
         // ctor
@@ -103,7 +103,7 @@
         }
 
         ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
-        if (containingClass().realSuperclass() != null && 
+        if (containingClass().realSuperclass() != null &&
             containingClass().realSuperclass().isAbstract()) {
             queue.add(containingClass());
         }
@@ -121,7 +121,7 @@
         }
         return null;
     }
-    
+
     public MethodInfo findSuperclassImplementation(HashSet notStrippable) {
         if (mReturnType == null) {
             // ctor
@@ -138,7 +138,7 @@
         }
 
         ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
-        if (containingClass().realSuperclass() != null && 
+        if (containingClass().realSuperclass() != null &&
                 containingClass().realSuperclass().isAbstract()) {
             queue.add(containingClass());
         }
@@ -154,7 +154,7 @@
         }
         return null;
     }
-    
+
     public ClassInfo findRealOverriddenClass(String name, String signature) {
         if (mReturnType == null) {
         // ctor
@@ -165,7 +165,7 @@
         }
 
         ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
-        if (containingClass().realSuperclass() != null && 
+        if (containingClass().realSuperclass() != null &&
             containingClass().realSuperclass().isAbstract()) {
             queue.add(containingClass());
         }
@@ -199,7 +199,7 @@
             }
         }
     }
-    
+
     private class ReturnTags implements InheritedTags {
         public TagInfo[] tags() {
             return comment().returnTags();
@@ -213,7 +213,7 @@
             }
         }
     }
-    
+
     public boolean isDeprecated() {
         boolean deprecated = false;
         if (!mDeprecatedKnown) {
@@ -237,7 +237,7 @@
         }
         return mIsDeprecated;
     }
-    
+
     public TypeInfo[] getTypeParameters(){
         return mTypeParameters;
     }
@@ -274,7 +274,7 @@
 
         // The underlying MethodDoc for an interface's declared methods winds up being marked
         // non-abstract.  Correct that here by looking at the immediate-parent class, and marking
-        // this method abstract if it is an unimplemented interface method. 
+        // this method abstract if it is an unimplemented interface method.
         if (containingClass.isInterface()) {
             isAbstract = true;
         }
@@ -448,7 +448,7 @@
                             + tag.parameterName() + "'");
                 }
             }
-             
+
             // get our parent's tags to fill in the blanks
             MethodInfo overridden = this.findOverriddenMethod(name(), signature());
             if (overridden != null) {
@@ -508,7 +508,7 @@
     {
         return mParameters;
     }
-    
+
 
     public boolean matchesParams(String[] params, String[] dimensions)
     {
@@ -589,6 +589,7 @@
         return result;
     }
 
+    @Override
     public boolean isExecutable()
     {
         return true;
@@ -617,21 +618,23 @@
     {
         return mDefaultAnnotationElementValue;
     }
-    
+
     public void setVarargs(boolean set){
         mIsVarargs = set;
     }
     public boolean isVarArgs(){
       return mIsVarargs;
     }
+
+    @Override
     public String toString(){
       return this.name();
     }
-    
+
     public void setReason(String reason) {
         mReasonOpened = reason;
     }
-    
+
     public String getReason() {
         return mReasonOpened;
     }
diff --git a/tools/droiddoc/src/PackageInfo.java b/tools/droiddoc/src/PackageInfo.java
index 18c636e..17ad1b7 100644
--- a/tools/droiddoc/src/PackageInfo.java
+++ b/tools/droiddoc/src/PackageInfo.java
@@ -57,11 +57,13 @@
         return s;
     }
 
+    @Override
     public ContainerInfo parent()
     {
         return null;
     }
 
+    @Override
     public boolean isHidden()
     {
         return comment().isHidden();
diff --git a/tools/droiddoc/src/ParamTagInfo.java b/tools/droiddoc/src/ParamTagInfo.java
index c21ecd5..d6f2b6b 100644
--- a/tools/droiddoc/src/ParamTagInfo.java
+++ b/tools/droiddoc/src/ParamTagInfo.java
@@ -76,6 +76,7 @@
         return mParameterName;
     }
 
+    @Override
     public void makeHDF(HDF data, String base)
     {
         data.setValue(base + ".name", parameterName());
diff --git a/tools/droiddoc/src/SampleTagInfo.java b/tools/droiddoc/src/SampleTagInfo.java
index c80083b..c7ad1cc 100644
--- a/tools/droiddoc/src/SampleTagInfo.java
+++ b/tools/droiddoc/src/SampleTagInfo.java
@@ -36,7 +36,7 @@
  * Both tags accept either a filename and an id or just a filename.  If no id
  * is provided, the entire file is copied.  If an id is provided, the lines
  * in the given file between the first two lines containing BEGIN_INCLUDE(id)
- * and END_INCLUDE(id), for the given id, are copied.  The id may be only 
+ * and END_INCLUDE(id), for the given id, are copied.  The id may be only
  * letters, numbers and underscore (_).
  *
  * Four examples:
@@ -274,6 +274,7 @@
         return result.substring(0);
     }
 
+    @Override
     public void makeHDF(HDF data, String base)
     {
         data.setValue(base + ".name", name());
diff --git a/tools/droiddoc/src/SeeTagInfo.java b/tools/droiddoc/src/SeeTagInfo.java
index 94863b5..8420ed3 100644
--- a/tools/droiddoc/src/SeeTagInfo.java
+++ b/tools/droiddoc/src/SeeTagInfo.java
@@ -45,6 +45,7 @@
         return linkReference().label;
     }
 
+    @Override
     public void makeHDF(HDF data, String base)
     {
         LinkReference linkRef = linkReference();
diff --git a/tools/droiddoc/src/SourcePositionInfo.java b/tools/droiddoc/src/SourcePositionInfo.java
index 6244803..ac605ec 100644
--- a/tools/droiddoc/src/SourcePositionInfo.java
+++ b/tools/droiddoc/src/SourcePositionInfo.java
@@ -76,6 +76,7 @@
         return new SourcePositionInfo(that.file, line, 0);
     }
 
+    @Override
     public String toString()
     {
         return file + ':' + line;
diff --git a/tools/droiddoc/src/TypeInfo.java b/tools/droiddoc/src/TypeInfo.java
index 5196c13..45e9db9 100644
--- a/tools/droiddoc/src/TypeInfo.java
+++ b/tools/droiddoc/src/TypeInfo.java
@@ -249,6 +249,7 @@
         }
     }
 
+    @Override
     public String toString(){
       String returnString = "";
       returnString += "Primitive?: " + mIsPrimitive + " TypeVariable?: " +
diff --git a/tools/droiddoc/templates-sdk/customization.cs b/tools/droiddoc/templates-sdk/customization.cs
index 6bdb992..6ae8446 100644
--- a/tools/droiddoc/templates-sdk/customization.cs
+++ b/tools/droiddoc/templates-sdk/customization.cs
@@ -73,9 +73,10 @@
           call:default_search_box() ?><?cs 
     	 	  if:reference ?>
     			  <div id="api-level-toggle">
-    			    <label for="apiLevelControl"><a href="<?cs var:toroot ?>guide/appendix/api-levels.html">Filter by API Level</a>: </label>
-    			    <select id="apiLevelControl">
-    			      <!-- option elements added by buildApiLevelToggle() -->
+    			    <input type="checkbox" id="apiLevelCheckbox" onclick="toggleApiLevelSelector(this)" />
+    			    <label for="apiLevelCheckbox" class="disabled">Filter by API Level: </label>
+    			    <select id="apiLevelSelector">
+    			      <!-- option elements added by buildApiLevelSelector() -->
     			    </select>
     			  </div>
     	 	    <script>
@@ -85,7 +86,7 @@
                   if:!last(since) ?>, <?cs /if ?><?cs
                 /each 
               ?> ];
-              buildApiLevelToggle();
+              buildApiLevelSelector();
             </script><?cs 
     			/if ?>
       </div><!-- headerRight -->
diff --git a/tools/droiddoc/templates-sdk/devdoc-nav.cs b/tools/droiddoc/templates-sdk/devdoc-nav.cs
deleted file mode 100644
index a69c175..0000000
--- a/tools/droiddoc/templates-sdk/devdoc-nav.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-<ul>
-  <li><div><a href="<?cs var:toroot ?>index.html">Home</a></div></li>
-  <li><div><a href="<?cs var:toroot ?>what-is-android.html">What is Android?</a></div></li>
-  <li><div><a href="<?cs var:toroot ?>intro/index.html">Getting Started</a></div>
-    <ul>
-      <li><div><a href="<?cs var:toroot ?>intro/installing.html">Installing the SDK</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>intro/upgrading.html">Upgrading the SDK</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>intro/develop-and-debug.html">Developing/Debugging</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>intro/hello-android.html">Hello Android</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>intro/anatomy.html">Anatomy of an App</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>intro/tutorial.html">Notepad Tutorial</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>intro/tools.html">Development Tools</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>intro/appmodel.html">Application Model</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>intro/lifecycle.html">Application Life Cycle</a></div></li>
-    </ul>
-  </li>
-  <li><div><div><a href="<?cs var:toroot ?>devel/index.html">Developing Applications</a></div>
-    <ul>
-      <li><div><a href="<?cs var:toroot ?>devel/implementing-ui.html">Implementing a UI</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>devel/building-blocks.html">Building Blocks</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>devel/data.html">Data Storage and Retrieval</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>devel/security.html">Security Model</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>devel/resources-i18n.html">Resources and i18n</a></div></li>
-    </ul>
-  </li>
-  <li><div><a href="<?cs var:toroot ?>toolbox/index.html">Developer Toolbox</a></div>
-    <ul>
-      <li><div><a href="<?cs var:toroot ?>toolbox/philosophy.html">Design Philosophy</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>toolbox/custom-components.html">Building Custom Components</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>toolbox/optional-apis.html">Optional APIs</a></div></li>
-    </ul>
-  </li>
-  <li><div><a href="<?cs var:toroot ?>samples/index.html">Sample Code</a></div>
-    <ul>
-      <li><div><a href="<?cs var:toroot ?>samples/ApiDemos/index.html">API Demos</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>samples/LunarLander/index.html">Lunar Lander</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>samples/NotePad/index.html">Note Pad</a></div></li>
-    </ul>
-  </li>
-  <li> <a href="<?cs var:toroot ?>reference/index.html"><strong>Reference Information</strong></a>
-    <ul>
-      <li><a href="<?cs var:toroot ?>reference/packages.html">Package Index</a></li>
-      <li><a href="<?cs var:toroot ?>reference/classes.html">Class Index</a></li>
-      <li><a href="<?cs var:toroot ?>reference/hierarchy.html">Class Hierarchy</a></li>
-      <li><a href="<?cs var:toroot ?>reference/view-gallery.html">List of Views</a></li>
-      <li><a href="<?cs var:toroot ?>reference/available-intents.html">List of Intents</a></li>
-      <li><a href="<?cs var:toroot ?>reference/android/Manifest.permission.html">List of Permissions</a></li>
-      <li><a href="<?cs var:toroot ?>reference/available-resources.html">List of Resource Types</a></li>
-      <li><a href="<?cs var:toroot ?>reference/aidl.html">Android IDL</a></li>
-      <li><a href="<?cs var:toroot ?>reference/glossary.html">Glossary</a></li>
-      <li><a href="<?cs var:toroot ?>reference/keywords.html">Index</a></li>
-    </ul>
-  </li>
-  <li><div><a href="<?cs var:toroot ?>kb/index.html">FAQs</a></div>
-    <ul>
-      <li><div><a href="<?cs var:toroot ?>kb/general.html">General</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>kb/commontasks.html">Common Tasks</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>kb/troubleshooting.html">Troubleshooting</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>kb/licensingandoss.html">Open Source Licensing</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>kb/framework.html">Application Framework</a></div></li>
-      <li><div><a href="<?cs var:toroot ?>kb/security.html">Security</a></div></li>
-    </ul>
-  </li>
-  <li><div><a href="<?cs var:toroot ?>roadmap.html">Roadmap</a></div></li>
-  <li><div><a href="<?cs var:toroot ?>goodies/index.html">Goodies</a></div></li>
-</ul>
\ No newline at end of file
diff --git a/tools/droiddoc/templates-sdk/header_tabs.cs b/tools/droiddoc/templates-sdk/header_tabs.cs
index 2a897cb..97d9048 100644
--- a/tools/droiddoc/templates-sdk/header_tabs.cs
+++ b/tools/droiddoc/templates-sdk/header_tabs.cs
@@ -5,80 +5,82 @@
 	elif:home ?>home<?cs
 	elif:community ?>community<?cs
 	elif:videos ?>videos<?cs /if ?>">
-         
-	<li id="home-link"><a href="<?cs var:toroot ?><?cs if:android.whichdoc != "online" ?>offline.html<?cs else ?>index.html<?cs /if ?>">
+    
+	<li id="home-link"><a href="<?cs var:toroot ?><?cs 
+	                            if:android.whichdoc != "online" ?>offline.html<?cs 
+	                            else ?>index.html<?cs /if ?>">
 	<?cs if:!sdk.redirect ?>
 		<span class="en">Home</span>
-		<span class="de">Startseite</span>
-		<span class="es"></span>
-		<span class="fr"></span>
-		<span class="it"></span>
-                <span class="ja">ホーム</span>
-		<span class="zh-CN">主页</span>
-		<span class="zh-TW">首頁</span>
+		<span style="display:none" class="de">Startseite</span>
+		<span style="display:none" class="es"></span>
+		<span style="display:none" class="fr"></span>
+		<span style="display:none" class="it"></span>
+    <span style="display:none" class="ja">ホーム</span>
+		<span style="display:none" class="zh-CN">主页</span>
+		<span style="display:none" class="zh-TW">首頁</span>
 	<?cs /if ?>
 	</a></li>
-	<li id="sdk-link"><a href="<?cs var:toroot ?>sdk/<?cs var:sdk.current ?>/index.html">
+	<li id="sdk-link"><a href="<?cs var:toroot ?>sdk/index.html">
 		<span class="en">SDK</span>
 	</a></li>
 	<li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html" onClick="return loadLast('guide')">
 	<?cs if:!sdk.redirect ?>
 		<span class="en">Dev Guide</span>
-		<span class="de">Handbuch</span>
-		<span class="es">Guía</span>
-		<span class="fr">Guide</span>
-		<span class="it">Guida</span>
-                <span class="ja">開発ガイド</span>
-		<span class="zh-CN">开发人员指南</span>
-		<span class="zh-TW">開發指南</span>
+		<span style="display:none" class="de">Handbuch</span>
+		<span style="display:none" class="es">Guía</span>
+		<span style="display:none" class="fr">Guide</span>
+		<span style="display:none" class="it">Guida</span>
+    <span style="display:none" class="ja">開発ガイド</span>
+		<span style="display:none" class="zh-CN">开发人员指南</span>
+		<span style="display:none" class="zh-TW">開發指南</span>
 	<?cs /if ?>
 	</a></li>
 	<li id="reference-link"><a href="<?cs var:toroot ?>reference/packages.html" onClick="return loadLast('reference')">
 	<?cs if:!sdk.redirect ?>
 		<span class="en">Reference</span>
-		<span class="de">Referenz</span>
-		<span class="es">Referencia</span>
-		<span class="fr">Référence</span>
-		<span class="it">Riferimento</span>
-                <span class="ja">リファレンス</span>
-		<span class="zh-CN">参考</span>
-		<span class="zh-TW">參考資料</span>
+		<span style="display:none" class="de">Referenz</span>
+		<span style="display:none" class="es">Referencia</span>
+		<span style="display:none" class="fr">Référence</span>
+		<span style="display:none" class="it">Riferimento</span>
+    <span style="display:none" class="ja">リファレンス</span>
+		<span style="display:none" class="zh-CN">参考</span>
+		<span style="display:none" class="zh-TW">參考資料</span>
 	<?cs /if ?>
 	</a></li>
 	<li><a href="http://android-developers.blogspot.com" onClick="return requestAppendHL(this.href)">
 	<?cs if:!sdk.redirect ?>
 		<span class="en">Blog</span>
-		<span class="de"></span>
-		<span class="es"></span>
-		<span class="fr"></span>
-		<span class="it"></span>
-                <span class="ja">ブログ</span>
-		<span class="zh-CN">博客</span>
-		<span class="zh-TW">網誌</span>
+		<span style="display:none" class="de"></span>
+		<span style="display:none" class="es"></span>
+		<span style="display:none" class="fr"></span>
+		<span style="display:none" class="it"></span>
+    <span style="display:none" class="ja">ブログ</span>
+		<span style="display:none" class="zh-CN">博客</span>
+		<span style="display:none" class="zh-TW">網誌</span>
 	<?cs /if ?>
 	</a></li>
 	<li id="videos-link"><a href="<?cs var:toroot ?>videos/index.html" onClick="return loadLast('videos')">
 	<?cs if:!sdk.redirect ?>
 		<span class="en">Videos</span>
-		<span class="de"></span>
-		<span class="es"></span>
-		<span class="fr"></span>
-		<span class="it"></span>
-                <span class="ja">ビデオ</span>
-		<span class="zh-CN"></span>
-		<span class="zh-TW"></span>
+		<span style="display:none" class="de"></span>
+		<span style="display:none" class="es"></span>
+		<span style="display:none" class="fr"></span>
+		<span style="display:none" class="it"></span>
+    <span style="display:none" class="ja">ビデオ</span>
+		<span style="display:none" class="zh-CN"></span>
+		<span style="display:none" class="zh-TW"></span>
 	<?cs /if ?>
 	</a></li>
 	<li id="community-link"><a href="<?cs var:toroot ?>community/index.html">
 	<?cs if:!sdk.redirect ?>
 		<span class="en">Community</span>
-		<span class="de"></span>
-		<span class="es">Comunidad</span>
-		<span class="fr">Communauté</span>
-		<span class="it"></span>
-                <span class="ja">コミュニティ</span>
-		<span class="zh-CN">社区</span>
-		<span class="zh-TW">社群</span>
+		<span style="display:none" class="de"></span>
+		<span style="display:none" class="es">Comunidad</span>
+		<span style="display:none" class="fr">Communauté</span>
+		<span style="display:none" class="it"></span>
+    <span style="display:none" class="ja">コミュニティ</span>
+		<span style="display:none" class="zh-CN">社区</span>
+		<span style="display:none" class="zh-TW">社群</span>
 	<?cs /if ?>
 	</a></li>
      
diff --git a/tools/droiddoc/templates-sdk/sdkpage.cs b/tools/droiddoc/templates-sdk/sdkpage.cs
index fdc1df5..ebaee07 100644
--- a/tools/droiddoc/templates-sdk/sdkpage.cs
+++ b/tools/droiddoc/templates-sdk/sdkpage.cs
@@ -33,28 +33,29 @@
 <div class="g-unit" id="doc-content" >
   <div id="jd-header" class="guide-header" >
     <span class="crumb">&nbsp;</span>
-    <h1><?cs if:android.whichdoc == "online" ?>Download <?cs /if ?><?cs var:page.title ?></h1>
+    <h1><?cs if:android.whichdoc == "online" ?>Download the <?cs /if ?><?cs var:page.title ?></h1>
   </div>
 
   <div id="jd-content">
-    <p><em><?cs 
-    if:ndk ?><?cs 
-      var:ndk.date ?><?cs 
+    <?cs 
+    if:ndk ?><p><em><?cs 
+      var:ndk.date ?></em></p><?cs 
     else ?><?cs 
-      var:sdk.date ?><?cs 
-    /if ?></em>
-    </p>
+      if:android.whichdoc == "online" ?><p><em><?cs 
+      var:sdk.date ?></em></p><?cs 
+      /if ?><?cs
+    /if ?>
 
 <?cs if:sdk.not_latest_version ?>
   <div class="special">
     <p><strong>This is NOT the current Android SDK release.</strong></p>
-    <p><a href="/sdk/<?cs var:sdk.current ?>/index.html">Download the current Android SDK</a></p>
+    <p><a href="/sdk/index.html">Download the current Android SDK</a></p>
   </div>
 <?cs /if ?>
 
 <?cs if:android.whichdoc != "online" && !android.preview ?>
 
-<p>The sections below provide an overview of the SDK package. </p>
+<!-- <p>The sections below provide an overview of how to install the SDK package. </p> -->
 
 <?cs else ?>
   <?cs if:ndk ?>
@@ -62,10 +63,10 @@
 <p>The Android NDK is a companion tool to the Android SDK that lets Android
 application developers build performance-critical portions of their apps in
 native code. It is designed for use <em>only</em> in conjunction with the
-Android SDK, so if you have not already installed the Android 1.5 SDK, please do
-so before downloading the NDK. Also, please read <a href="#overview">What is the 
-Android NDK?</a> to get an understanding of what the NDK offers and whether it 
-will be useful to you.</p>
+Android SDK, so if you have not already installed the latest Android SDK, please
+do so before downloading the NDK. Also, please read <a href="#overview">What is 
+the Android NDK?</a> to get an understanding of what the NDK offers and whether
+it will be useful to you.</p>
 
 <p>Select the download package that is appropriate for your development
 computer. </p>
@@ -110,16 +111,49 @@
   Android 1.6 and we are pleased to announce the availability of an early look
   SDK to give you a head-start on developing applications for it. </p>
 
-  <p>The Android 1.6 platform includes a variety of improvements and new
-  features for users and developers. Additionally, the SDK itself introduces
-  several new capabilities that enable you to develop applications more
-  efficiently. See the <a href="features.html">Android 1.6 Highlights</a> 
-  document for a list of highlights.</p>
-  <?cs /if ?>
+  <p>The Android <?cs var:sdk.preview.version ?> platform includes a variety of
+  improvements and new features for users and developers. Additionally, the SDK
+  itself introduces several new capabilities that enable you to develop
+  applications more efficiently. See the <a href="features.html">Android <?cs
+  var:sdk.preview.version ?> Platform Highlights</a> document for a list of 
+  highlights.</p>
+<?cs /if ?> 
+<?cs # end if NDK ... the following is for the SDK ?>
 
-<p>Before downloading, please read the <a href="requirements.html">
-System Requirements</a> document. As you start the download, you will also need to review and agree to 
-the Terms and Conditions that govern the use of the Android SDK. </p>
+  <div class="toggle-content special">
+    <p>The Android SDK has changed! If you've worked with the Android SDK before, 
+    you will notice several important differences:</p>
+    
+    <div class="toggle-content-toggleme" style="display:none">
+    <ul style="padding-bottom:.0;">
+    <li style="margin-top:.5em">The SDK downloadable package includes <em>only</em>
+    the latest version of the Android SDK Tools.</li>
+    <li>Once you've installed the SDK, you now use the Android SDK and AVD Manager
+    to download all of the SDK components that you need, such as Android platforms,
+    SDK add-ons, tools, and documentation. </li>
+    <li>The new approach is modular &mdash; you can install only the components you
+    need and update any or all components without affecting other parts of your
+    development environment.</li>
+    <li>In short, once you've installed the new SDK, you will not need to download
+    an SDK package again. Instead, you will use the Android SDK and AVD Manager to
+    keep your development environment up-to-date. </li>
+    </ul>
+    <p style="margin-top:0">If you are currently using the Android 1.6 SDK, you
+    do not need to install the new SDK, because your existing SDK already 
+    includes the Android SDK and AVD Manager tool. To develop against Android 
+    2.0, for example, you can just download the Android 2.0 platform (and 
+    updated SDK Tools) into your existing SDK. Refer to <a 
+    href="adding-components.html">Adding SDK Components</a>.</p>
+    </div>
+    
+    <a href='#' class='toggle-content-button show' onclick="toggleContent(this);return false;">
+      <span>show more</span><span style='display:none'>show less</span>
+    </a>
+  </div>
+
+  <p>If you are new to the Android SDK, please read the <a href="#quickstart">Quick Start</a>,
+  below, for an overview of how to install and set up the SDK.</p>
+  
   
   <table class="download">
     <tr>
@@ -156,7 +190,7 @@
   <tr class="alt-color">
     <td>ADT Plugin for Eclipse <?cs var:adt.zip_version ?></td>
     <td>
-  <a href="<?cs var:toroot ?>sdk/download.html?v=<?cs var:adt.zip_download ?>"><?cs var:adt.zip_download ?></a>
+  <a href="http://dl.google.com/android/<?cs var:adt.zip_download ?>"><?cs var:adt.zip_download ?></a>
     </td>
     <td><?cs var:adt.zip_bytes ?> bytes</td>
     <td><?cs var:adt.zip_checksum ?></td>
@@ -170,14 +204,17 @@
 
 <?cs if:android.whichdoc != "online" && sdk.preview ?>
   <p>Welcome developers! The next release of the Android platform will be
-  Android 1.6 and we are pleased to announce the availability of an early look SDK
-  to give you a head-start on developing applications for it. </p>
+Android <?cs var:sdk.preview.version ?> and we are pleased to announce the
+availability of an early look SDK to give you a head-start on developing
+applications for it. </p>
 
-  <p>The Android 1.6 platform includes a variety of improvements and new features
-  for users and developers. Additionally, the SDK itself introduces several new
-  capabilities that enable you to develop applications more efficiently.
-  See the <a href="http://developer.android.com/sdk/preview/features.html">
-  Android 1.6 Highlights</a> document for a list of highlights.</p>
+  <p>The Android <?cs var:sdk.preview.version ?> platform includes a variety of
+improvements and new features for users and developers. Additionally, the SDK
+itself introduces several new capabilities that enable you to develop
+applications more efficiently. See the <a
+href="http://developer.android.com/sdk/preview/features.html">Android 
+<?cs var:sdk.preview.version ?> Highlights</a> document for a list of
+highlights.</p>
 <?cs /if ?>
 
       <?cs call:tag_list(root.descr) ?>
diff --git a/tools/droiddoc/templates/assets/android-developer-core.css b/tools/droiddoc/templates/assets/android-developer-core.css
index acb873d..2b30c05 100644
--- a/tools/droiddoc/templates/assets/android-developer-core.css
+++ b/tools/droiddoc/templates/assets/android-developer-core.css
@@ -48,11 +48,12 @@
 }
 
 input, select,
-textarea, option {
+textarea, option, label {
   font-family:inherit;
   font-size:inherit;
   padding:0;
   margin:0;
+  vertical-align:middle;
 }
 
 option {
@@ -81,6 +82,7 @@
   padding:10px;
   margin:0 0 1em 1em;
   overflow:auto;
+  line-height:inherit; /* fixes vertical scrolling in webkit */
 }
 
 h1,h2,h3,h4,h5 {
@@ -194,7 +196,7 @@
   height: 114px;
   position:relative;
   z-index:100;
-  min-width:576px;
+  min-width:675px; /* min width for the tabs, before they wrap */
   padding:0 10px;
   border-bottom:3px solid #94b922;
 }
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.css b/tools/droiddoc/templates/assets/android-developer-docs.css
index 4e0ea97..1029813 100644
--- a/tools/droiddoc/templates/assets/android-developer-docs.css
+++ b/tools/droiddoc/templates/assets/android-developer-docs.css
@@ -339,9 +339,12 @@
 }
 
 #api-level-toggle {
-  float:right;
   padding:0 10px;
   font-size:11px;
+  float:right;
+}
+
+#api-level-toggle label.disabled {
   color:#999;
 }
 
@@ -660,7 +663,27 @@
 div.special {
   padding: .5em 1em 1em 1em;
   margin: 0 0 1em;
-  background-color: #ddf0f2;
+  background-color: #DAF3FC;
+  border:1px solid #d3ecf5;
+  border-radius:5px;
+  -moz-border-radius:5px;
+  -webkit-border-radius:5px;
+}
+
+.toggle-content-toggleme {
+  display:none;
+}
+
+.toggle-content-button {
+  font-size:.9em;
+  line-height:.9em;
+  text-decoration:none;
+  position:relative;
+  top:5px;
+}
+
+.toggle-content-button:hover {
+  text-decoration:underline;
 }
 
 div.special p {
@@ -751,6 +774,8 @@
   font-weight: bold;
   color: red;
   text-decoration: none;
+  vertical-align:top;
+  line-height:.9em;
 }
 
 pre.classic {
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.js b/tools/droiddoc/templates/assets/android-developer-docs.js
index b16ed0d..6431163 100644
--- a/tools/droiddoc/templates/assets/android-developer-docs.js
+++ b/tools/droiddoc/templates/assets/android-developer-docs.js
@@ -10,6 +10,7 @@
 var nav_pref;
 var toRoot;
 var isMobile = false; // true if mobile, so we can adjust some layout
+var isIE6 = false; // true if IE6
 
 function addLoadEvent(newfun) {
   var current = window.onload;
@@ -24,17 +25,24 @@
 }
 
 var agent = navigator['userAgent'];
+// If a mobile phone, set flag and do mobile setup
 if ((agent.indexOf("Mobile") != -1) || 
     (agent.indexOf("BlackBerry") != -1) || 
     (agent.indexOf("Mini") != -1)) {
   isMobile = true;
   addLoadEvent(mobileSetup);
+// If not a mobile browser, set the onresize event for IE6, and others
+} else if (agent.indexOf("MSIE 6.0") != -1) {
+  isIE6 = true;
+  addLoadEvent(function() {
+    window.onresize = resizeAll;
+  });
+} else {
+  addLoadEvent(function() {
+    window.onresize = resizeHeight;
+  });
 }
 
-addLoadEvent(function() {
-window.onresize = resizeAll;
-});
-
 function mobileSetup() {
   $("body").css({'overflow':'auto'});
   $("html").css({'overflow':'auto'});
@@ -50,7 +58,7 @@
   var lists = document.createElement("script");
   lists.setAttribute("type","text/javascript");
   lists.setAttribute("src", toRoot+"reference/lists.js");
-  $("head").append($(lists));
+  document.getElementsByTagName("head")[0].appendChild(lists);
 } );
 
 function setToRoot(root) {
@@ -60,8 +68,12 @@
 
 function restoreWidth(navWidth) {
   var windowWidth = $(window).width() + "px";
-  content.css({marginLeft:parseInt(navWidth) + 6 + "px", //account for 6px-wide handle-bar
-               width:parseInt(windowWidth) - parseInt(navWidth) - 6 + "px"});
+  content.css({marginLeft:parseInt(navWidth) + 6 + "px"}); //account for 6px-wide handle-bar
+
+  if (isIE6) {
+    content.css({width:parseInt(windowWidth) - parseInt(navWidth) - 6 + "px"}); // necessary in order for scrollbars to be visible
+  }
+
   sidenav.css({width:navWidth});
   resizePackagesNav.css({width:navWidth});
   classesNav.css({width:navWidth});
@@ -99,14 +111,14 @@
 }
 
 function writeCookie(cookie, val, section, expiration) {
-  if (!val) return;  
+  if (val==undefined) return;
   section = section == null ? "_" : "_"+section+"_";
   if (expiration == null) {
     var date = new Date();
     date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
     expiration = date.toGMTString();
   }
-  document.cookie = cookie_namespace+section+cookie+"="+val+"; expires="+expiration+"; path=/";
+  document.cookie = cookie_namespace + section + cookie + "=" + val + "; expires=" + expiration+"; path=/";
 } 
 
 function init() {
@@ -124,7 +136,7 @@
   }
 
   if (!isMobile) {
-    $("#resize-packages-nav").resizable({handles: "s", resize: function(e, ui) { resizeHeight(); } });
+    $("#resize-packages-nav").resizable({handles: "s", resize: function(e, ui) { resizePackagesHeight(); } });
     $(".side-nav-resizable").resizable({handles: "e", resize: function(e, ui) { resizeWidth(); } });
     var cookieWidth = readCookie(cookiePath+'width');
     var cookieHeight = readCookie(cookiePath+'height');
@@ -156,8 +168,8 @@
   var htmlPos = fullPageName.lastIndexOf(".html", fullPageName.length);
   var pathPageName = fullPageName.slice(firstSlashPos, htmlPos + 5);
   var link = $("#devdoc-nav a[href$='"+ pathPageName+"']");
-  if ((link.length == 0) && ((fullPageName.indexOf("/guide/") != -1) || (fullPageName.indexOf("/sdk/") != -1))) { 
-// if there's no match, then let's backstep through the directory until we find an index.html page that matches our ancestor directories (only for dev guide and sdk)
+  if ((link.length == 0) && (fullPageName.indexOf("/guide/") != -1)) { 
+// if there's no match, then let's backstep through the directory until we find an index.html page that matches our ancestor directories (only for dev guide)
     lastBackstep = pathPageName.lastIndexOf("/");
     while (link.length == 0) {
       backstepDirectory = pathPageName.lastIndexOf("/", lastBackstep);
@@ -174,23 +186,46 @@
   }
 }
 
-function resizeHeight() {
+/* Resize the height of the nav panels in the reference,
+ * and save the new size to a cookie */
+function resizePackagesHeight() {
   var windowHeight = ($(window).height() - HEADER_HEIGHT);
-  var swapperHeight = windowHeight - 13;
-  $("#swapper").css({height:swapperHeight + "px"});
-  sidenav.css({height:windowHeight + "px"});
-  content.css({height:windowHeight + "px"});
+  var swapperHeight = windowHeight - 13; // move 13px for swapper link at the bottom
   resizePackagesNav.css({maxHeight:swapperHeight + "px"});
   classesNav.css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"});
+
+  $("#swapper").css({height:swapperHeight + "px"});
   $("#packages-nav").css({height:parseInt(resizePackagesNav.css("height")) - 6 + "px"}); //move 6px for handle
-  devdocNav.css({height:sidenav.css("height")});
-  $("#nav-tree").css({height:swapperHeight + "px"});
-  
+
   var basePath = getBaseUri(location.pathname);
   var section = basePath.substring(1,basePath.indexOf("/",1));
   writeCookie("height", resizePackagesNav.css("height"), section, null);
 }
 
+/* Resize the height of the side-nav and doc-content divs,
+ * which creates the frame effect */
+function resizeHeight() {
+  // Get the window height and always resize the doc-content and side-nav divs
+  var windowHeight = ($(window).height() - HEADER_HEIGHT);
+  content.css({height:windowHeight + "px"});
+  sidenav.css({height:windowHeight + "px"});
+
+  var href = location.href;
+  // If in the reference docs, also resize the "swapper", "classes-nav", and "nav-tree"  divs
+  if (href.indexOf("/reference/") != -1) {
+    var swapperHeight = windowHeight - 13;
+    $("#swapper").css({height:swapperHeight + "px"});
+    $("#classes-nav").css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"});
+    $("#nav-tree").css({height:swapperHeight + "px"});
+
+  // If in the dev guide docs, also resize the "devdoc-nav" div
+  } else if (href.indexOf("/guide/") != -1) {
+    $("#devdoc-nav").css({height:sidenav.css("height")});
+  }
+}
+
+/* Resize the width of the "side-nav" and the left margin of the "doc-content" div,
+ * which creates the resizable side bar */
 function resizeWidth() {
   var windowWidth = $(window).width() + "px";
   if (sidenav.length) {
@@ -198,24 +233,27 @@
   } else {
     var sidenavWidth = 0;
   }
-  content.css({marginLeft:parseInt(sidenavWidth) + 6 + "px", //account for 6px-wide handle-bar
-               width:parseInt(windowWidth) - parseInt(sidenavWidth) - 6 + "px"});
+  content.css({marginLeft:parseInt(sidenavWidth) + 6 + "px"}); //account for 6px-wide handle-bar
+
+  if (isIE6) {
+    content.css({width:parseInt(windowWidth) - parseInt(sidenavWidth) - 6 + "px"}); // necessary in order to for scrollbars to be visible
+  }
+
   resizePackagesNav.css({width:sidenavWidth});
   classesNav.css({width:sidenavWidth});
   $("#packages-nav").css({width:sidenavWidth});
-  
+
   var basePath = getBaseUri(location.pathname);
   var section = basePath.substring(1,basePath.indexOf("/",1));
   writeCookie("width", sidenavWidth, section, null);
 }
 
+/* For IE6 only,
+ * because it can't properly perform auto width for "doc-content" div,
+ * avoiding this for all browsers provides better performance */
 function resizeAll() {
-  if (!isMobile) {
-    resizeHeight();
-    if ($(".side-nav-resizable").length) {
-      resizeWidth();
-    }
-  }
+  resizeHeight();
+  resizeWidth();
 }
 
 function getBaseUri(uri) {
@@ -448,3 +486,18 @@
   }
   return (lang != 0) ? lang : 'en';
 }
+
+
+function toggleContent(obj) {
+  var button = $(obj);
+  var div = $(obj.parentNode);
+  var toggleMe = $(".toggle-content-toggleme",div);
+  if (button.hasClass("show")) {
+    toggleMe.slideDown();
+    button.removeClass("show").addClass("hide");
+  } else {
+    toggleMe.slideUp();
+    button.removeClass("hide").addClass("show");
+  }
+  $("span", button).toggle();
+}
diff --git a/tools/droiddoc/templates/assets/android-developer-reference.js b/tools/droiddoc/templates/assets/android-developer-reference.js
index 3080760..6299596 100644
--- a/tools/droiddoc/templates/assets/android-developer-reference.js
+++ b/tools/droiddoc/templates/assets/android-developer-reference.js
@@ -1,48 +1,78 @@
 
 /* API LEVEL TOGGLE */
 addLoadEvent(changeApiLevel);
+
+var API_LEVEL_ENABLED_COOKIE = "api_level_enabled";
 var API_LEVEL_COOKIE = "api_level";
 var minLevel = 1;
 
-function buildApiLevelToggle() {
-	var maxLevel = SINCE_DATA.length;
-	var userApiLevel = readCookie(API_LEVEL_COOKIE);
-	
-	if (userApiLevel != 0) {
-		selectedLevel = userApiLevel;
-	} else {
-	  selectedLevel = maxLevel;
-	}
+function toggleApiLevelSelector(checkbox) {
+  var date = new Date();
+  date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
+  var expiration = date.toGMTString();
+  if (checkbox.checked) {
+    $("#apiLevelSelector").removeAttr("disabled");
+    $("#api-level-toggle label").removeClass("disabled");
+    writeCookie(API_LEVEL_ENABLED_COOKIE, 1, null, expiration);
+  } else {
+    $("#apiLevelSelector").attr("disabled","disabled");
+    $("#api-level-toggle label").addClass("disabled");
+    writeCookie(API_LEVEL_ENABLED_COOKIE, 0, null, expiration);
+  }
+  changeApiLevel();
+}
+
+function buildApiLevelSelector() {
+  var maxLevel = SINCE_DATA.length;
+  var userApiLevelEnabled = readCookie(API_LEVEL_ENABLED_COOKIE);
+  var userApiLevel = readCookie(API_LEVEL_COOKIE);
+  userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
+
+  if (userApiLevelEnabled == 0) {
+    $("#apiLevelSelector").attr("disabled","disabled");
+  } else {
+    $("#apiLevelCheckbox").attr("checked","checked");
+    $("#api-level-toggle label").removeClass("disabled");
+  }
 	
   minLevel = $("body").attr("class");
-	var select = $("#apiLevelControl").html("").change(changeApiLevel);
-	for (var i = maxLevel-1; i >= 0; i--) {
-		var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
-//		if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
-		select.append(option);
-	}
+  var select = $("#apiLevelSelector").html("").change(changeApiLevel);
+  for (var i = maxLevel-1; i >= 0; i--) {
+    var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
+  //  if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
+	  select.append(option);
+  }
 	
   // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
-	var selectedLevelItem = $("#apiLevelControl option[value='"+selectedLevel+"']").get(0); 
+  var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0); 
   selectedLevelItem.setAttribute('selected',true); 
 }
 
 function changeApiLevel() {
-	var selectedLevel = $("#apiLevelControl option:selected").val();
-  toggleVisisbleApis(selectedLevel, "body");
+  var maxLevel = SINCE_DATA.length;
+  var userApiLevelEnabled = readCookie(API_LEVEL_ENABLED_COOKIE);
+  var selectedLevel = maxLevel;	
+	
+  if (userApiLevelEnabled == 0) {
+    toggleVisisbleApis(selectedLevel, "body");
+  } else {
+    selectedLevel = $("#apiLevelSelector option:selected").val();
+    toggleVisisbleApis(selectedLevel, "body");
+    
+    var date = new Date();
+    date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
+    var expiration = date.toGMTString();
+    writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
+  }
   
-  var date = new Date();
-  date.setTime(date.getTime()+(50*365*24*60*60*1000)); // keep this for 50 years
-  writeCookie(API_LEVEL_COOKIE, selectedLevel, null, date);
-  
-	if (selectedLevel < minLevel) {
-	  var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
-	  $("#naMessage").show().html("<div><p><strong>This " + thing + " is not available with API Level " + selectedLevel + ".</strong></p>"
+  if (selectedLevel < minLevel) {
+    var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
+    $("#naMessage").show().html("<div><p><strong>This " + thing + " is not available with API Level " + selectedLevel + ".</strong></p>"
                               + "<p>To use this " + thing + ", your application must specify API Level " + minLevel + " or higher in its manifest "
                               + "and be compiled against a version of the Android library that supports an equal or higher API Level. To reveal this "
                               + "document, change the value of the API Level filter above.</p>"
                               + "<p><a href='" +toRoot+ "guide/appendix/api-levels.html'>What is the API Level?</a></p></div>");
-	} else {
+  } else {
     $("#naMessage").hide();
   }
 }
@@ -156,7 +186,7 @@
     node.expanded = true;
     
     // perform api level toggling because new nodes are new to the DOM 
-	  var selectedLevel = $("#apiLevelControl option:selected").val();
+	  var selectedLevel = $("#apiLevelSelector option:selected").val();
     toggleVisisbleApis(selectedLevel, "#side-nav");
   }
 }
@@ -228,7 +258,7 @@
   init_navtree("nav-tree", toroot, NAVTREE_DATA);
   
   // perform api level toggling because because the whole tree is new to the DOM 
-	var selectedLevel = $("#apiLevelControl option:selected").val();
+	var selectedLevel = $("#apiLevelSelector option:selected").val();
   toggleVisisbleApis(selectedLevel, "#side-nav");
 }
 
diff --git a/tools/droiddoc/templates/assets/images/home/donut-android.png b/tools/droiddoc/templates/assets/images/home/donut-android.png
old mode 100644
new mode 100755
index 85bc952..6aba06b
--- a/tools/droiddoc/templates/assets/images/home/donut-android.png
+++ b/tools/droiddoc/templates/assets/images/home/donut-android.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/eclair-android.png b/tools/droiddoc/templates/assets/images/home/eclair-android.png
new file mode 100644
index 0000000..d476ce9
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/eclair-android.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/search_autocomplete.js b/tools/droiddoc/templates/assets/search_autocomplete.js
index 929751f..086674a 100644
--- a/tools/droiddoc/templates/assets/search_autocomplete.js
+++ b/tools/droiddoc/templates/assets/search_autocomplete.js
@@ -168,6 +168,6 @@
 
 function submit_search() {
   var query = document.getElementById('search_autocomplete').value;
-  document.location = toRoot + 'search.html#q=' + query; // toRoot is initialized in android-developer-docs.js 
+  document.location = toRoot + 'search.html#q=' + query + '&t=0';
   return false;
 }
diff --git a/tools/droiddoc/templates/class.cs b/tools/droiddoc/templates/class.cs
index 9bc2ba0..89eb927 100644
--- a/tools/droiddoc/templates/class.cs
+++ b/tools/droiddoc/templates/class.cs
@@ -188,7 +188,7 @@
 
 <?cs # summary macros ?>
 
-<?cs def:write_method_summary(methods) ?>
+<?cs def:write_method_summary(methods, included) ?>
 <?cs set:count = #1 ?>
 <?cs each:method = methods ?>
 	 <?cs # The apilevel-N class MUST BE LAST in the sequence of class names ?>
@@ -202,8 +202,7 @@
             <?cs call:type_link(method.returnType) ?></nobr>
         </td>
         <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad"><a href="<?cs var:toroot ?><?cs var:method.href ?>">
-        <?cs var:method.name ?></a></span>(<?cs call:parameter_list(method.params) ?>)</nobr>
+        <span class="sympad"><?cs call:cond_link(method.name, toroot, method.href, included) ?></span>(<?cs call:parameter_list(method.params) ?>)</nobr>
         <?cs if:subcount(method.shortDescr) || subcount(method.deprecated) ?>
         <div class="jd-descrdiv"><?cs call:short_descr(method) ?></div>
   <?cs /if ?>
@@ -212,7 +211,7 @@
 <?cs /each ?>
 <?cs /def ?>
 
-<?cs def:write_field_summary(fields) ?>
+<?cs def:write_field_summary(fields, included) ?>
 <?cs set:count = #1 ?>
     <?cs each:field=fields ?>
       <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:field.since ?>" >
@@ -221,26 +220,26 @@
           <?cs var:field.static ?>
           <?cs var:field.final ?>
           <?cs call:type_link(field.type) ?></nobr></td>
-          <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a></td>
+          <td class="jd-linkcol"><?cs call:cond_link(field.name, toroot, field.href, included) ?></td>
           <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?></td>
       </tr>
       <?cs set:count = count + #1 ?>
     <?cs /each ?>
 <?cs /def ?>
 
-<?cs def:write_constant_summary(fields) ?>
+<?cs def:write_constant_summary(fields, included) ?>
 <?cs set:count = #1 ?>
     <?cs each:field=fields ?>
     <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:field.since ?>" >
         <td class="jd-typecol"><?cs call:type_link(field.type) ?></td>
-        <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a></td>
+        <td class="jd-linkcol"><?cs call:cond_link(field.name, toroot, field.href, included) ?></td>
         <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?></td>
     </tr>
     <?cs set:count = count + #1 ?>
     <?cs /each ?>
 <?cs /def ?>
 
-<?cs def:write_attr_summary(attrs) ?>
+<?cs def:write_attr_summary(attrs, included) ?>
 <?cs set:count = #1 ?>
     <tr>
         <td><nobr><em>Attribute Name</em></nobr></td>
@@ -249,9 +248,9 @@
     </tr>
     <?cs each:attr=attrs ?>
     <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:attr.since ?>" >
-        <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:attr.href ?>"><?cs var:attr.name ?></a></td>
+        <td class="jd-linkcol"><?cs if:included ?><a href="<?cs var:toroot ?><?cs var:attr.href ?>"><?cs /if ?><?cs var:attr.name ?><?cs if:included ?></a><?cs /if ?></td>
         <td class="jd-linkcol"><?cs each:m=attr.methods ?>
-            <a href="<?cs var:toroot ?><?cs var:m.href ?>"><?cs var:m.name ?></a>
+            <?cs call:cond_link(m.name, toroot, m.href, included) ?>
             <?cs /each ?>
         </td>
         <td class="jd-descrcol" width="100%"><?cs call:short_descr(attr) ?>&nbsp;</td>
@@ -293,7 +292,7 @@
 <?cs if:subcount(class.attrs) ?>
 <!-- =========== FIELD SUMMARY =========== -->
 <table id="lattrs" class="jd-sumtable"><tr><th colspan="12">XML Attributes</th></tr>
-<?cs call:write_attr_summary(class.attrs) ?>
+<?cs call:write_attr_summary(class.attrs, 1) ?>
 <?cs /if ?>
 
 <?cs # if there are inherited attrs, write the table ?>
@@ -308,14 +307,14 @@
 <tr class="api apilevel-<?cs var:cl.since ?>" >
 <td colspan="12">
 <?cs call:expando_trigger("inherited-attrs-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
-<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+<?cs call:cond_link(cl.qualified, toroot, cl.link, cl.included) ?>
 <div id="inherited-attrs-<?cs var:cl.qualified ?>">
   <div id="inherited-attrs-<?cs var:cl.qualified ?>-list"
         class="jd-inheritedlinks">
   </div>
   <div id="inherited-attrs-<?cs var:cl.qualified ?>-summary" style="display: none;">
     <table class="jd-sumtable-expando">
-    <?cs call:write_attr_summary(cl.attrs) ?></table>
+    <?cs call:write_attr_summary(cl.attrs, cl.included) ?></table>
   </div>
 </div>
 </td></tr>
@@ -332,7 +331,7 @@
     <?cs each:field=class.enumConstants ?>
     <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:field.since ?>" >
         <td class="jd-descrcol"><?cs call:type_link(field.type) ?>&nbsp;</td>
-        <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a>&nbsp;</td>
+        <td class="jd-linkcol"><?cs call:cond_link(field.name, toroot, field.href, cl.included) ?>&nbsp;</td>
         <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?>&nbsp;</td>
     </tr>
     <?cs set:count = count + #1 ?>
@@ -343,7 +342,7 @@
 <?cs # this next line must be exactly like this to be parsed by eclipse ?>
 <!-- =========== ENUM CONSTANT SUMMARY =========== -->
 <table id="constants" class="jd-sumtable"><tr><th colspan="12">Constants</th></tr>
-<?cs call:write_constant_summary(class.constants) ?>
+<?cs call:write_constant_summary(class.constants, 1) ?>
 </table>
 <?cs /if ?>
 
@@ -359,14 +358,14 @@
 <tr class="api apilevel-<?cs var:cl.since ?>" >
 <td colspan="12">
 <?cs call:expando_trigger("inherited-constants-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
-<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+<?cs call:cond_link(cl.qualified, toroot, cl.link, cl.included) ?>
 <div id="inherited-constants-<?cs var:cl.qualified ?>">
   <div id="inherited-constants-<?cs var:cl.qualified ?>-list"
         class="jd-inheritedlinks">
   </div>
   <div id="inherited-constants-<?cs var:cl.qualified ?>-summary" style="display: none;">
     <table class="jd-sumtable-expando">
-    <?cs call:write_constant_summary(cl.constants) ?></table>
+    <?cs call:write_constant_summary(cl.constants, cl.included) ?></table>
   </div>
 </div>
 </td></tr>
@@ -379,7 +378,7 @@
 <?cs # this next line must be exactly like this to be parsed by eclipse ?>
 <!-- =========== FIELD SUMMARY =========== -->
 <table id="lfields" class="jd-sumtable"><tr><th colspan="12">Fields</th></tr>
-<?cs call:write_field_summary(class.fields) ?>
+<?cs call:write_field_summary(class.fields, 1) ?>
 </table>
 <?cs /if ?>
 
@@ -395,14 +394,14 @@
 <tr class="api apilevel-<?cs var:cl.since ?>" >
 <td colspan="12">
 <?cs call:expando_trigger("inherited-fields-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
-<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+<?cs call:cond_link(cl.qualified, toroot, cl.link, cl.included) ?>
 <div id="inherited-fields-<?cs var:cl.qualified ?>">
   <div id="inherited-fields-<?cs var:cl.qualified ?>-list"
         class="jd-inheritedlinks">
   </div>
   <div id="inherited-fields-<?cs var:cl.qualified ?>-summary" style="display: none;">
     <table class="jd-sumtable-expando">
-    <?cs call:write_field_summary(cl.fields) ?></table>
+    <?cs call:write_field_summary(cl.fields, cl.included) ?></table>
   </div>
 </div>
 </td></tr>
@@ -415,7 +414,7 @@
 <?cs # this next line must be exactly like this to be parsed by eclipse ?>
 <!-- ======== CONSTRUCTOR SUMMARY ======== -->
 <table id="pubctors" class="jd-sumtable"><tr><th colspan="12">Public Constructors</th></tr>
-<?cs call:write_method_summary(class.ctors.public) ?>
+<?cs call:write_method_summary(class.ctors.public, 1) ?>
 </table>
 <?cs /if ?>
 
@@ -423,7 +422,7 @@
 <?cs # this next line must be exactly like this to be parsed by eclipse ?>
 <!-- ======== CONSTRUCTOR SUMMARY ======== -->
 <table id="proctors" class="jd-sumtable"><tr><th colspan="12">Protected Constructors</th></tr>
-<?cs call:write_method_summary(class.ctors.protected) ?>
+<?cs call:write_method_summary(class.ctors.protected, 1) ?>
 </table>
 <?cs /if ?>
 
@@ -431,7 +430,7 @@
 <?cs # this next line must be exactly like this to be parsed by eclipse ?>
 <!-- ========== METHOD SUMMARY =========== -->
 <table id="pubmethods" class="jd-sumtable"><tr><th colspan="12">Public Methods</th></tr>
-<?cs call:write_method_summary(class.methods.public) ?>
+<?cs call:write_method_summary(class.methods.public, 1) ?>
 </table>
 <?cs /if ?>
 
@@ -439,7 +438,7 @@
 <?cs # this next line must be exactly like this to be parsed by eclipse ?>
 <!-- ========== METHOD SUMMARY =========== -->
 <table id="promethods" class="jd-sumtable"><tr><th colspan="12">Protected Methods</th></tr>
-<?cs call:write_method_summary(class.methods.protected) ?>
+<?cs call:write_method_summary(class.methods.protected, 1) ?>
 </table>
 <?cs /if ?>
 
@@ -454,14 +453,14 @@
 <?cs if:subcount(cl.methods) ?>
 <tr class="api apilevel-<?cs var:cl.since ?>" >
 <td colspan="12"><?cs call:expando_trigger("inherited-methods-"+cl.qualified, "closed") ?>
-From <?cs var:cl.kind ?> <a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+From <?cs var:cl.kind ?> <?cs call:cond_link(cl.qualified, toroot, cl.link, cl.included) ?>
 <div id="inherited-methods-<?cs var:cl.qualified ?>">
   <div id="inherited-methods-<?cs var:cl.qualified ?>-list"
         class="jd-inheritedlinks">
   </div>
   <div id="inherited-methods-<?cs var:cl.qualified ?>-summary" style="display: none;">
     <table class="jd-sumtable-expando">
-    <?cs call:write_method_summary(cl.methods) ?></table>
+    <?cs call:write_method_summary(cl.methods, cl.included) ?></table>
   </div>
 </div>
 </td></tr>
diff --git a/tools/droiddoc/templates/macros.cs b/tools/droiddoc/templates/macros.cs
index 14dc90e..0c59f32 100644
--- a/tools/droiddoc/templates/macros.cs
+++ b/tools/droiddoc/templates/macros.cs
@@ -39,6 +39,15 @@
 <?cs def:class_name(type) ?><?cs call:type_link_impl(type, "false") ?><?cs /def ?>
 <?cs def:type_link(type) ?><?cs call:type_link_impl(type, "true") ?><?cs /def ?>
 
+<?cs # a conditional link.
+      if the "condition" parameter evals to true then the link is displayed
+      otherwise only the text is displayed
+?><?cs
+def:cond_link(text, root, path, condition) ?><?cs
+  if:condition ?><a href="<?cs var:root ?><?cs var:path ?>"><?cs /if ?><?cs var:text ?><?cs if:condition ?></a><?cs /if ?><?cs
+/def ?>
+
+
 <?cs # A comma separated parameter list ?><?cs 
 def:parameter_list(params) ?><?cs
   each:param = params ?><?cs
@@ -64,6 +73,15 @@
       elif:tag.kind == "@sdkCurrent" ?><?cs var:sdk.current ?><?cs
       elif:tag.kind == "@sdkCurrentVersion" ?><?cs var:sdk.version ?><?cs
       elif:tag.kind == "@sdkCurrentRelId" ?><?cs var:sdk.rel.id ?><?cs
+      elif:tag.kind == "@sdkPlatformVersion" ?><?cs var:sdk.platform.version ?><?cs
+      elif:tag.kind == "@sdkPlatformApiLevel" ?><?cs var:sdk.platform.apiLevel ?><?cs
+      elif:tag.kind == "@sdkPlatformMajorMinor" ?><?cs var:sdk.platform.majorMinor ?><?cs
+      elif:tag.kind == "@sdkPlatformReleaseDate" ?><?cs var:sdk.platform.releaseDate ?><?cs
+      elif:tag.kind == "@sdkPlatformDeployableDate" ?><?cs var:sdk.platform.deployableDate ?><?cs
+      elif:tag.kind == "@adtZipVersion" ?><?cs var:adt.zip.version ?><?cs
+      elif:tag.kind == "@adtZipDownload" ?><?cs var:adt.zip.download ?><?cs
+      elif:tag.kind == "@adtZipBytes" ?><?cs var:adt.zip.bytes ?><?cs
+      elif:tag.kind == "@adtZipChecksum" ?><?cs var:adt.zip.checksum ?><?cs
       elif:tag.kind == "@inheritDoc" ?><?cs # This is the case when @inheritDoc is in something
                                               that doesn't inherit from anything?><?cs
       elif:tag.kind == "@attr" ?><?cs
@@ -89,8 +107,8 @@
 <?cs # Show the red box with the deprecated warning ?><?cs 
 def:deprecated_warning(obj) ?><?cs 
   if:subcount(obj.deprecated) ?><p>
-  <p class="warning jd-deprecated-warning">
-      <strong><?cs call:deprecated_text(obj.kind) ?></strong><?cs 
+  <p class="caution">
+      <strong><?cs call:deprecated_text(obj.kind) ?></strong><br/> <?cs 
       call:tag_list(obj.deprecated) ?>
   </p><?cs 
   /if ?><?cs 
diff --git a/tools/dump-package-stats b/tools/dump-package-stats
index 589bab5..d11e727 100755
--- a/tools/dump-package-stats
+++ b/tools/dump-package-stats
@@ -102,7 +102,7 @@
                 $1 != "Length" ||
                 $2 != "Method" ||
                 $3 != "Size" ||
-                $4 != "Ratio" ||
+                ($4 != "Ratio" && $4 != "Cmpr") ||
                 $5 != "Date" ||
                 $6 != "Time" ||
                 $7 != "CRC-32" ||
diff --git a/tools/findleaves.py b/tools/findleaves.py
new file mode 100755
index 0000000..0adf188
--- /dev/null
+++ b/tools/findleaves.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+#
+# Finds files with the specified name under a particular directory, stopping
+# the search in a given subdirectory when the file is found.
+#
+
+import os
+import sys
+
+def perform_find(mindepth, prune, dirlist, filename):
+  result = []
+  pruneleaves = set(map(lambda x: os.path.split(x)[1], prune))
+  for rootdir in dirlist:
+    rootdepth = rootdir.count("/")
+    for root, dirs, files in os.walk(rootdir):
+      # prune
+      check_prune = False
+      for d in dirs:
+        if d in pruneleaves:
+          check_prune = True
+          break
+      if check_prune:
+        i = 0
+        while i < len(dirs):
+          if dirs[i] in prune:
+            del dirs[i]
+          else:
+            i += 1
+      # mindepth
+      if mindepth > 0:
+        depth = 1 + root.count("/") - rootdepth
+        if depth < mindepth:
+          continue
+      # match
+      if filename in files:
+        result.append(os.path.join(root, filename))
+        del dirs[:]
+  return result
+
+def usage():
+  sys.stderr.write("""Usage: %(progName)s [<options>] <dirlist> <filename>
+Options:
+   --mindepth=<mindepth>
+       Both behave in the same way as their find(1) equivalents.
+   --prune=<dirname>
+       Avoids returning results from inside any directory called <dirname>
+       (e.g., "*/out/*"). May be used multiple times.
+""" % {
+      "progName": os.path.split(sys.argv[0])[1],
+    })
+  sys.exit(1)
+
+def main(argv):
+  mindepth = -1
+  prune = []
+  i=1
+  while i<len(argv) and len(argv[i])>2 and argv[i][0:2] == "--":
+    arg = argv[i]
+    if arg.startswith("--mindepth="):
+      try:
+        mindepth = int(arg[len("--mindepth="):])
+      except ValueError:
+        usage()
+    elif arg.startswith("--prune="):
+      p = arg[len("--prune="):]
+      if len(p) == 0:
+        usage()
+      prune.append(p)
+    else:
+      usage()
+    i += 1
+  if len(argv)-i < 2: # need both <dirlist> and <filename>
+    usage()
+  dirlist = argv[i:-1]
+  filename = argv[-1]
+  results = perform_find(mindepth, prune, dirlist, filename)
+  results.sort()
+  for r in set(results):
+    print r
+
+if __name__ == "__main__":
+  main(sys.argv)
diff --git a/tools/findleaves.sh b/tools/findleaves.sh
deleted file mode 100755
index 74bfd24..0000000
--- a/tools/findleaves.sh
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/bin/bash
-#
-# 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.
-#
-
-#
-# Finds files with the specified name under a particular directory, stopping
-# the search in a given subdirectory when the file is found.
-#
-
-set -o nounset  # fail when dereferencing unset variables
-set -o errexit  # fail if any subcommand fails
-
-progName=`basename $0`
-
-function warn() {
-    echo "$progName: $@" >&2
-}
-
-function trace() {
-    echo "$progName: $@"
-}
-
-function usage() {
-    if [[ $# > 0 ]]
-    then
-        warn $@
-    fi
-    cat <<-EOF
-Usage: $progName [<options>] <dirlist> <filename>
-Options:
-       --mindepth=<mindepth>
-       --maxdepth=<maxdepth>
-       Both behave in the same way as their find(1) equivalents.
-       --prune=<glob>
-       Avoids returning results from any path matching the given glob-style
-       pattern (e.g., "*/out/*"). May be used multiple times.
-EOF
-    exit 1
-}
-
-function fail() {
-    warn $@
-    exit 1
-}
-
-if [ $# -lt 2 ]
-then
-    usage
-fi
-
-findargs=""
-while [[ "${1:0:2}" == "--" ]]
-do
-    arg=${1:2}
-    name=${arg%%=*}
-    value=${arg##*=}
-    if [[ "$name" == "mindepth" || "$name" == "maxdepth" ]]
-    then
-        # Add to beginning of findargs; these must come before the expression.
-        findargs="-$name $value $findargs"
-    elif [[ "$name" == "prune" ]]
-    then
-        # Add to end of findargs; these are part of the expression.
-        findargs="$findargs -path $value -prune -or"
-    fi
-    shift
-done
-
-nargs=$#
-# The filename is the last argument
-filename="${!nargs}"
-
-# Print out all files that match, as long as the path isn't explicitly
-# pruned. This will print out extraneous results from directories whose
-# parents have a match. These are filtered out by the awk script below.
-find -L "${@:1:$nargs-1}" $findargs -type f -name "$filename" -print 2>/dev/null |
-
-# Only pass along the directory of each match.
-sed -e 's/\/[^\/]*$/\//' |
-
-# Sort the output, so directories appear immediately before their contents.
-# If there are any duplicates, the awk script will implicitly ignore them.
-# The LC_ALL=C forces sort(1) to use bytewise ordering instead of listening
-# to the locale, which may do case-insensitive and/or alphanumeric-only
-# sorting.
-LC_ALL=C sort |
-
-# Always print the first line, which can't possibly be covered by a
-# parent directory match. After that, only print lines where the last
-# line printed isn't a prefix.
-awk -v "filename=$filename" '
-    (NR == 1) || (index($0, last) != 1) {
-        last = $0;
-        printf("%s%s\n", $0, filename);
-    }
-'
diff --git a/tools/releasetools/amend_generator.py b/tools/releasetools/amend_generator.py
index 70e71d4..f8f4344 100644
--- a/tools/releasetools/amend_generator.py
+++ b/tools/releasetools/amend_generator.py
@@ -87,6 +87,10 @@
     'dur' seconds."""
     self.script.append("show_progress %f %d" % (frac, int(dur)))
 
+  def SetProgress(self, frac):
+    """Not implemented in amend."""
+    pass
+
   def PatchCheck(self, filename, *sha1):
     """Check that the given file (or MTD reference) has one of the
     given *sha1 hashes."""
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 2e3138c..27264dd 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -15,6 +15,7 @@
 import errno
 import getopt
 import getpass
+import imp
 import os
 import re
 import shutil
@@ -33,7 +34,7 @@
 OPTIONS.max_image_size = {}
 OPTIONS.verbose = False
 OPTIONS.tempfiles = []
-
+OPTIONS.device_specific = None
 
 class ExternalError(RuntimeError): pass
 
@@ -184,14 +185,20 @@
   return key_passwords
 
 
-def SignFile(input_name, output_name, key, password, align=None):
+def SignFile(input_name, output_name, key, password, align=None,
+             whole_file=False):
   """Sign the input_name zip/jar/apk, producing output_name.  Use the
   given key and password (the latter may be None if the key does not
   have a password.
 
   If align is an integer > 1, zipalign is run to align stored files in
   the output zip on 'align'-byte boundaries.
+
+  If whole_file is true, use the "-w" option to SignApk to embed a
+  signature that covers the whole file in the archive comment of the
+  zip file.
   """
+
   if align == 0 or align == 1:
     align = None
 
@@ -201,13 +208,14 @@
   else:
     sign_name = output_name
 
-  p = Run(["java", "-jar",
-           os.path.join(OPTIONS.search_path, "framework", "signapk.jar"),
-           key + ".x509.pem",
-           key + ".pk8",
-           input_name, sign_name],
-          stdin=subprocess.PIPE,
-          stdout=subprocess.PIPE)
+  cmd = ["java", "-Xmx512m", "-jar",
+           os.path.join(OPTIONS.search_path, "framework", "signapk.jar")]
+  if whole_file:
+    cmd.append("-w")
+  cmd.extend([key + ".x509.pem", key + ".pk8",
+              input_name, sign_name])
+
+  p = Run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
   if password is not None:
     password += "\n"
   p.communicate(password)
@@ -247,6 +255,10 @@
       Prepend <dir>/bin to the list of places to search for binaries
       run by this script, and expect to find jars in <dir>/framework.
 
+  -s  (--device_specific) <file>
+      Path to the python module containing device-specific
+      releasetools code.
+
   -v  (--verbose)
       Show command lines being executed.
 
@@ -271,8 +283,9 @@
 
   try:
     opts, args = getopt.getopt(
-        argv, "hvp:" + extra_opts,
-        ["help", "verbose", "path="] + list(extra_long_opts))
+        argv, "hvp:s:" + extra_opts,
+        ["help", "verbose", "path=", "device_specific="] +
+          list(extra_long_opts))
   except getopt.GetoptError, err:
     Usage(docstring)
     print "**", str(err), "**"
@@ -288,6 +301,8 @@
       OPTIONS.verbose = True
     elif o in ("-p", "--path"):
       OPTIONS.search_path = a
+    elif o in ("-s", "--device_specific"):
+      OPTIONS.device_specific = a
     else:
       if extra_option_handler is None or not extra_option_handler(o, a):
         assert False, "unknown option \"%s\"" % (o,)
@@ -412,3 +427,68 @@
   zinfo.compress_type = zip.compression
   zinfo.external_attr = perms << 16
   zip.writestr(zinfo, data)
+
+
+class DeviceSpecificParams(object):
+  module = None
+  def __init__(self, **kwargs):
+    """Keyword arguments to the constructor become attributes of this
+    object, which is passed to all functions in the device-specific
+    module."""
+    for k, v in kwargs.iteritems():
+      setattr(self, k, v)
+
+    if self.module is None:
+      path = OPTIONS.device_specific
+      if not path: return
+      try:
+        if os.path.isdir(path):
+          info = imp.find_module("releasetools", [path])
+        else:
+          d, f = os.path.split(path)
+          b, x = os.path.splitext(f)
+          if x == ".py":
+            f = b
+          info = imp.find_module(f, [d])
+        self.module = imp.load_module("device_specific", *info)
+      except ImportError:
+        print "unable to load device-specific module; assuming none"
+
+  def _DoCall(self, function_name, *args, **kwargs):
+    """Call the named function in the device-specific module, passing
+    the given args and kwargs.  The first argument to the call will be
+    the DeviceSpecific object itself.  If there is no module, or the
+    module does not define the function, return the value of the
+    'default' kwarg (which itself defaults to None)."""
+    if self.module is None or not hasattr(self.module, function_name):
+      return kwargs.get("default", None)
+    return getattr(self.module, function_name)(*((self,) + args), **kwargs)
+
+  def FullOTA_Assertions(self):
+    """Called after emitting the block of assertions at the top of a
+    full OTA package.  Implementations can add whatever additional
+    assertions they like."""
+    return self._DoCall("FullOTA_Assertions")
+
+  def FullOTA_InstallEnd(self):
+    """Called at the end of full OTA installation; typically this is
+    used to install the image for the device's baseband processor."""
+    return self._DoCall("FullOTA_InstallEnd")
+
+  def IncrementalOTA_Assertions(self):
+    """Called after emitting the block of assertions at the top of an
+    incremental OTA package.  Implementations can add whatever
+    additional assertions they like."""
+    return self._DoCall("IncrementalOTA_Assertions")
+
+  def IncrementalOTA_VerifyEnd(self):
+    """Called at the end of the verification phase of incremental OTA
+    installation; additional checks can be placed here to abort the
+    script before any changes are made."""
+    return self._DoCall("IncrementalOTA_VerifyEnd")
+
+  def IncrementalOTA_InstallEnd(self):
+    """Called at the end of incremental OTA installation; typically
+    this is used to install the image for the device's baseband
+    processor."""
+    return self._DoCall("IncrementalOTA_InstallEnd")
diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py
index e7a15cd..d1902e1 100644
--- a/tools/releasetools/edify_generator.py
+++ b/tools/releasetools/edify_generator.py
@@ -100,9 +100,16 @@
 
   def ShowProgress(self, frac, dur):
     """Update the progress bar, advancing it over 'frac' over the next
-    'dur' seconds."""
+    'dur' seconds.  'dur' may be zero to advance it via SetProgress
+    commands instead of by time."""
     self.script.append("show_progress(%f, %d);" % (frac, int(dur)))
 
+  def SetProgress(self, frac):
+    """Set the position of the progress bar within the chunk defined
+    by the most recent ShowProgress call.  'frac' should be in
+    [0,1]."""
+    self.script.append("set_progress(%f);" % (frac,))
+
   def PatchCheck(self, filename, *sha1):
     """Check that the given file (or MTD reference) has one of the
     given *sha1 hashes."""
diff --git a/tools/releasetools/img_from_target_files b/tools/releasetools/img_from_target_files
index 00abde4..d157dca 100755
--- a/tools/releasetools/img_from_target_files
+++ b/tools/releasetools/img_from_target_files
@@ -31,6 +31,7 @@
   print >> sys.stderr, "Python 2.4 or newer is required."
   sys.exit(1)
 
+import errno
 import os
 import re
 import shutil
@@ -82,8 +83,16 @@
   # The name of the directory it is making an image out of matters to
   # mkyaffs2image.  It wants "system" but we have a directory named
   # "SYSTEM", so create a symlink.
-  os.symlink(os.path.join(OPTIONS.input_tmp, "SYSTEM"),
-             os.path.join(OPTIONS.input_tmp, "system"))
+  try:
+    os.symlink(os.path.join(OPTIONS.input_tmp, "SYSTEM"),
+               os.path.join(OPTIONS.input_tmp, "system"))
+  except OSError, e:
+      # bogus error on my mac version?
+      #   File "./build/tools/releasetools/img_from_target_files", line 86, in AddSystem
+      #     os.path.join(OPTIONS.input_tmp, "system"))
+      # OSError: [Errno 17] File exists
+    if (e.errno == errno.EEXIST):
+      pass
 
   p = common.Run(["mkyaffs2image", "-f",
                   os.path.join(OPTIONS.input_tmp, "system"), img.name])
diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files
index 4aaad37..f129da1 100755
--- a/tools/releasetools/ota_from_target_files
+++ b/tools/releasetools/ota_from_target_files
@@ -57,11 +57,13 @@
   sys.exit(1)
 
 import copy
+import errno
 import os
 import re
 import sha
 import subprocess
 import tempfile
+import threading
 import time
 import zipfile
 
@@ -80,6 +82,7 @@
 OPTIONS.omit_prereq = False
 OPTIONS.extra_script = None
 OPTIONS.script_mode = 'auto'
+OPTIONS.worker_threads = 3
 
 def MostPopularKey(d, default):
   """Given a dict, return the key corresponding to the largest
@@ -273,19 +276,14 @@
   key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
   pw = key_passwords[OPTIONS.package_key]
 
-  common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw)
+  common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
+                  whole_file=True)
 
 
 def AppendAssertions(script, input_zip):
   device = GetBuildProp("ro.product.device", input_zip)
   script.AssertDevice(device)
 
-  info = input_zip.read("OTA/android-info.txt")
-  m = re.search(r"require\s+version-bootloader\s*=\s*(\S+)", info)
-  if m:
-    bootloaders = m.group(1).split("|")
-    script.AssertSomeBootloader(*bootloaders)
-
 
 def MakeRecoveryPatch(output_zip, recovery_img, boot_img):
   """Generate a binary patch that creates the recovery image starting
@@ -302,8 +300,9 @@
   executable.
   """
 
-  patch = Difference(recovery_img, boot_img, "imgdiff")
-  common.ZipWriteStr(output_zip, "system/recovery-from-boot.p", patch)
+  d = Difference(recovery_img, boot_img)
+  _, _, patch = d.ComputePatch()
+  common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
   Item.Get("system/recovery-from-boot.p", dir=False)
 
   # Images with different content will have a different first page, so
@@ -324,7 +323,7 @@
         'header_sha1': header_sha1,
         'recovery_size': recovery_img.size,
         'recovery_sha1': recovery_img.sha1 }
-  common.ZipWriteStr(output_zip, "system/etc/install-recovery.sh", sh)
+  common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
   return Item.Get("system/etc/install-recovery.sh", dir=False)
 
 
@@ -339,19 +338,18 @@
     # change very often.
     script = edify_generator.EdifyGenerator(2)
 
+  device_specific = common.DeviceSpecificParams(
+      input_zip=input_zip,
+      output_zip=output_zip,
+      script=script,
+      input_tmp=OPTIONS.input_tmp)
+
   if not OPTIONS.omit_prereq:
     ts = GetBuildProp("ro.build.date.utc", input_zip)
     script.AssertOlderBuild(ts)
 
   AppendAssertions(script, input_zip)
-
-  script.ShowProgress(0.1, 0)
-
-  try:
-    common.ZipWriteStr(output_zip, "radio.img", input_zip.read("RADIO/image"))
-    script.WriteFirmwareImage("radio", "radio.img")
-  except KeyError:
-    print "warning: no radio image in input target_files; not flashing radio"
+  device_specific.FullOTA_Assertions()
 
   script.ShowProgress(0.5, 0)
 
@@ -360,6 +358,7 @@
 
   script.FormatPartition("system")
   script.Mount("MTD", "system", "/system")
+  script.UnpackPackageDir("recovery", "/system")
   script.UnpackPackageDir("system", "/system")
 
   symlinks = CopySystemFiles(input_zip, output_zip)
@@ -385,8 +384,11 @@
   common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
   script.ShowProgress(0.2, 0)
 
-  script.WriteRawImage("boot", "boot.img")
   script.ShowProgress(0.2, 10)
+  script.WriteRawImage("boot", "boot.img")
+
+  script.ShowProgress(0.1, 0)
+  device_specific.FullOTA_InstallEnd()
 
   if OPTIONS.extra_script is not None:
     script.AppendExtra(OPTIONS.extra_script)
@@ -423,38 +425,111 @@
   return out
 
 
-def Difference(tf, sf, diff_program):
-  """Return the patch (as a string of data) needed to turn sf into tf.
-  diff_program is the name of an external program (or list, if
-  additional arguments are desired) to run to generate the diff.
-  """
+DIFF_PROGRAM_BY_EXT = {
+    ".gz" : "imgdiff",
+    ".zip" : ["imgdiff", "-z"],
+    ".jar" : ["imgdiff", "-z"],
+    ".apk" : ["imgdiff", "-z"],
+    ".img" : "imgdiff",
+    }
 
-  ttemp = tf.WriteToTemp()
-  stemp = sf.WriteToTemp()
 
-  ext = os.path.splitext(tf.name)[1]
+class Difference(object):
+  def __init__(self, tf, sf):
+    self.tf = tf
+    self.sf = sf
+    self.patch = None
 
-  try:
-    ptemp = tempfile.NamedTemporaryFile()
-    if isinstance(diff_program, list):
-      cmd = copy.copy(diff_program)
-    else:
-      cmd = [diff_program]
-    cmd.append(stemp.name)
-    cmd.append(ttemp.name)
-    cmd.append(ptemp.name)
-    p = common.Run(cmd)
-    _, err = p.communicate()
-    if err or p.returncode != 0:
-      print "WARNING: failure running %s:\n%s\n" % (diff_program, err)
-      return None
-    diff = ptemp.read()
-  finally:
-    ptemp.close()
-    stemp.close()
-    ttemp.close()
+  def ComputePatch(self):
+    """Compute the patch (as a string of data) needed to turn sf into
+    tf.  Returns the same tuple as GetPatch()."""
 
-  return diff
+    tf = self.tf
+    sf = self.sf
+
+    ext = os.path.splitext(tf.name)[1]
+    diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
+
+    ttemp = tf.WriteToTemp()
+    stemp = sf.WriteToTemp()
+
+    ext = os.path.splitext(tf.name)[1]
+
+    try:
+      ptemp = tempfile.NamedTemporaryFile()
+      if isinstance(diff_program, list):
+        cmd = copy.copy(diff_program)
+      else:
+        cmd = [diff_program]
+      cmd.append(stemp.name)
+      cmd.append(ttemp.name)
+      cmd.append(ptemp.name)
+      p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+      _, err = p.communicate()
+      if err or p.returncode != 0:
+        print "WARNING: failure running %s:\n%s\n" % (diff_program, err)
+        return None
+      diff = ptemp.read()
+    finally:
+      ptemp.close()
+      stemp.close()
+      ttemp.close()
+
+    self.patch = diff
+    return self.tf, self.sf, self.patch
+
+
+  def GetPatch(self):
+    """Return a tuple (target_file, source_file, patch_data).
+    patch_data may be None if ComputePatch hasn't been called, or if
+    computing the patch failed."""
+    return self.tf, self.sf, self.patch
+
+
+def ComputeDifferences(diffs):
+  """Call ComputePatch on all the Difference objects in 'diffs'."""
+  print len(diffs), "diffs to compute"
+
+  # Do the largest files first, to try and reduce the long-pole effect.
+  by_size = [(i.tf.size, i) for i in diffs]
+  by_size.sort(reverse=True)
+  by_size = [i[1] for i in by_size]
+
+  lock = threading.Lock()
+  diff_iter = iter(by_size)   # accessed under lock
+
+  def worker():
+    try:
+      lock.acquire()
+      for d in diff_iter:
+        lock.release()
+        start = time.time()
+        d.ComputePatch()
+        dur = time.time() - start
+        lock.acquire()
+
+        tf, sf, patch = d.GetPatch()
+        if sf.name == tf.name:
+          name = tf.name
+        else:
+          name = "%s (%s)" % (tf.name, sf.name)
+        if patch is None:
+          print "patching failed!                                  %s" % (name,)
+        else:
+          print "%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % (
+              dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name)
+      lock.release()
+    except Exception, e:
+      print e
+      raise
+
+  # start worker threads; wait for them all to finish.
+  threads = [threading.Thread(target=worker)
+             for i in range(OPTIONS.worker_threads)]
+  for th in threads:
+    th.start()
+  while threads:
+    threads.pop().join()
 
 
 def GetBuildProp(property, z):
@@ -484,6 +559,7 @@
     except KeyError:
       return 0
 
+
 def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
   source_version = GetRecoveryAPIVersion(source_zip)
 
@@ -502,6 +578,12 @@
   else:
     raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
 
+  device_specific = common.DeviceSpecificParams(
+      source_zip=source_zip,
+      target_zip=target_zip,
+      output_zip=output_zip,
+      script=script)
+
   print "Loading target..."
   target_data = LoadSystemFiles(target_zip)
   print "Loading source..."
@@ -509,9 +591,11 @@
 
   verbatim_targets = []
   patch_list = []
+  diffs = []
   largest_source_size = 0
   for fn in sorted(target_data.keys()):
     tf = target_data[fn]
+    assert fn == tf.name
     sf = source_data.get(fn, None)
 
     if sf is None or fn in OPTIONS.require_verbatim:
@@ -523,26 +607,23 @@
       verbatim_targets.append((fn, tf.size))
     elif tf.sha1 != sf.sha1:
       # File is different; consider sending as a patch
-      diff_method = "bsdiff"
-      if tf.name.endswith(".gz"):
-        diff_method = "imgdiff"
-      d = Difference(tf, sf, diff_method)
-      if d is not None:
-        print fn, tf.size, len(d), (float(len(d)) / tf.size)
-      if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
-        # patch is almost as big as the file; don't bother patching
-        tf.AddToZip(output_zip)
-        verbatim_targets.append((fn, tf.size))
-      else:
-        common.ZipWriteStr(output_zip, "patch/" + fn + ".p", d)
-        patch_list.append((fn, tf, sf, tf.size))
-        largest_source_size = max(largest_source_size, sf.size)
+      diffs.append(Difference(tf, sf))
     else:
       # Target file identical to source.
       pass
 
-  total_verbatim_size = sum([i[1] for i in verbatim_targets])
-  total_patched_size = sum([i[3] for i in patch_list])
+  ComputeDifferences(diffs)
+
+  for diff in diffs:
+    tf, sf, d = diff.GetPatch()
+    if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
+      # patch is almost as big as the file; don't bother patching
+      tf.AddToZip(output_zip)
+      verbatim_targets.append((tf.name, tf.size))
+    else:
+      common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
+      patch_list.append((tf.name, tf, sf, tf.size))
+      largest_source_size = max(largest_source_size, sf.size)
 
   source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
   target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
@@ -566,35 +647,31 @@
       os.path.join(OPTIONS.target_tmp, "RECOVERY")))
   updating_recovery = (source_recovery.data != target_recovery.data)
 
-  source_radio = source_zip.read("RADIO/image")
-  target_radio = target_zip.read("RADIO/image")
-  updating_radio = (source_radio != target_radio)
-
-  # The last 0.1 is reserved for creating symlinks, fixing
-  # permissions, and writing the boot image (if necessary).
-  progress_bar_total = 1.0
-  if updating_boot:
-    progress_bar_total -= 0.1
-  if updating_radio:
-    progress_bar_total -= 0.3
+  # Here's how we divide up the progress bar:
+  #  0.1 for verifying the start state (PatchCheck calls)
+  #  0.8 for applying patches (ApplyPatch calls)
+  #  0.1 for unpacking verbatim files, symlinking, and doing the
+  #      device-specific commands.
 
   AppendAssertions(script, target_zip)
+  device_specific.IncrementalOTA_Assertions()
 
   script.Print("Verifying current system...")
 
-  pb_verify = progress_bar_total * 0.3 * \
-              (total_patched_size /
-               float(total_patched_size+total_verbatim_size+1))
+  script.ShowProgress(0.1, 0)
+  total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
+  if updating_boot:
+    total_verify_size += source_boot.size
+  so_far = 0
 
-  for i, (fn, tf, sf, size) in enumerate(patch_list):
-    if i % 5 == 0:
-      next_sizes = sum([i[3] for i in patch_list[i:i+5]])
-      script.ShowProgress(next_sizes * pb_verify / (total_patched_size+1), 1)
-
+  for fn, tf, sf, size in patch_list:
     script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
+    so_far += sf.size
+    script.SetProgress(so_far / total_verify_size)
 
   if updating_boot:
-    d = Difference(target_boot, source_boot, "imgdiff")
+    d = Difference(target_boot, source_boot)
+    _, _, d = d.ComputePatch()
     print "boot      target: %d  source: %d  diff: %d" % (
         target_boot.size, source_boot.size, len(d))
 
@@ -603,12 +680,16 @@
     script.PatchCheck("MTD:boot:%d:%s:%d:%s" %
                       (source_boot.size, source_boot.sha1,
                        target_boot.size, target_boot.sha1))
+    so_far += source_boot.size
+    script.SetProgress(so_far / total_verify_size)
 
   if patch_list or updating_recovery or updating_boot:
     script.CacheFreeSpaceCheck(largest_source_size)
     script.Print("Unpacking patches...")
     script.UnpackPackageDir("patch", "/tmp/patchtmp")
 
+  device_specific.IncrementalOTA_VerifyEnd()
+
   script.Comment("---- start making changes here ----")
 
   if OPTIONS.wipe_user_data:
@@ -621,6 +702,19 @@
                             if i not in target_data] +
                      ["/system/recovery.img"])
 
+  script.ShowProgress(0.8, 0)
+  total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
+  if updating_boot:
+    total_patch_size += target_boot.size
+  so_far = 0
+
+  script.Print("Patching system files...")
+  for fn, tf, sf, size in patch_list:
+    script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1,
+                      sf.sha1, "/tmp/patchtmp/"+fn+".p")
+    so_far += tf.size
+    script.SetProgress(so_far / total_patch_size)
+
   if updating_boot:
     # Produce the boot image by applying a patch to the current
     # contents of the boot partition, and write it back to the
@@ -632,6 +726,8 @@
                       "-",
                       target_boot.size, target_boot.sha1,
                       source_boot.sha1, "/tmp/patchtmp/boot.img.p")
+    so_far += target_boot.size
+    script.SetProgress(so_far / total_patch_size)
     print "boot image changed; including."
   else:
     print "boot image unchanged; skipping."
@@ -650,29 +746,12 @@
     # as fodder for constructing the recovery image.
     recovery_sh_item = MakeRecoveryPatch(output_zip,
                                          target_recovery, target_boot)
+    script.UnpackPackageDir("recovery", "/system")
     print "recovery image changed; including as patch from boot."
   else:
     print "recovery image unchanged; skipping."
 
-  if updating_radio:
-    script.ShowProgress(0.3, 10)
-    script.Print("Writing radio image...")
-    script.WriteFirmwareImage("radio", "radio.img")
-    common.ZipWriteStr(output_zip, "radio.img", target_radio)
-    print "radio image changed; including."
-  else:
-    print "radio image unchanged; skipping."
-
-  script.Print("Patching system files...")
-  pb_apply = progress_bar_total * 0.7 * \
-             (total_patched_size /
-              float(total_patched_size+total_verbatim_size+1))
-  for i, (fn, tf, sf, size) in enumerate(patch_list):
-    if i % 5 == 0:
-      next_sizes = sum([i[3] for i in patch_list[i:i+5]])
-      script.ShowProgress(next_sizes * pb_apply / (total_patched_size+1), 1)
-    script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1,
-                      sf.sha1, "/tmp/patchtmp/"+fn+".p")
+  script.ShowProgress(0.1, 10)
 
   target_symlinks = CopySystemFiles(target_zip, None)
 
@@ -700,14 +779,10 @@
   script.DeleteFiles(to_delete)
 
   if verbatim_targets:
-    pb_verbatim = progress_bar_total * \
-                  (total_verbatim_size /
-                   float(total_patched_size+total_verbatim_size+1))
-    script.ShowProgress(pb_verbatim, 5)
     script.Print("Unpacking new files...")
     script.UnpackPackageDir("system", "/system")
 
-  script.Print("Finishing up...")
+  script.Print("Symlinks and permissions...")
 
   # Create all the symlinks that don't already exist, or point to
   # somewhere different than what we want.  Delete each symlink before
@@ -726,6 +801,9 @@
   # permissions.
   script.AppendScript(temp_script)
 
+  # Do device-specific installation (eg, write radio image).
+  device_specific.IncrementalOTA_InstallEnd()
+
   if OPTIONS.extra_script is not None:
     scirpt.AppendExtra(OPTIONS.extra_script)
 
@@ -749,6 +827,8 @@
       OPTIONS.extra_script = a
     elif o in ("-m", "--script_mode"):
       OPTIONS.script_mode = a
+    elif o in ("--worker_threads"):
+      OPTIONS.worker_threads = int(a)
     else:
       return False
     return True
@@ -761,7 +841,8 @@
                                               "wipe_user_data",
                                               "no_prereq",
                                               "extra_script=",
-                                              "script_mode="],
+                                              "script_mode=",
+                                              "worker_threads="],
                              extra_option_handler=option_handler)
 
   if len(args) != 2:
@@ -777,6 +858,23 @@
   print "unzipping target target-files..."
   OPTIONS.input_tmp = common.UnzipTemp(args[0])
 
+  if OPTIONS.device_specific is None:
+    # look for the device-specific tools extension location in the input
+    try:
+      f = open(os.path.join(OPTIONS.input_tmp, "META", "tool-extensions.txt"))
+      ds = f.read().strip()
+      f.close()
+      if ds:
+        ds = os.path.normpath(ds)
+        print "using device-specific extensions in", ds
+        OPTIONS.device_specific = ds
+    except IOError, e:
+      if e.errno == errno.ENOENT:
+        # nothing specified in the file
+        pass
+      else:
+        raise
+
   common.LoadMaxSizes()
   if not OPTIONS.max_image_size:
     print
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index 158845c..961e643 100755
--- a/tools/releasetools/sign_target_files_apks
+++ b/tools/releasetools/sign_target_files_apks
@@ -20,10 +20,6 @@
 
 Usage:  sign_target_files_apks [flags] input_target_files output_target_files
 
-  -s  (--signapk_jar)  <path>
-      Path of the signapks.jar file used to sign an individual APK
-      file.
-
   -e  (--extra_apks)  <name,name,...=key>
       Add extra APK name/key pairs as though they appeared in
       apkcerts.txt (so mappings specified by -k and -d are applied).
@@ -307,9 +303,7 @@
 def main(argv):
 
   def option_handler(o, a):
-    if o in ("-s", "--signapk_jar"):
-      OPTIONS.signapk_jar = a
-    elif o in ("-e", "--extra_apks"):
+    if o in ("-e", "--extra_apks"):
       names, key = a.split("=")
       names = names.split(",")
       for n in names:
@@ -339,9 +333,8 @@
     return True
 
   args = common.ParseOptions(argv, __doc__,
-                             extra_opts="s:e:d:k:ot:",
-                             extra_long_opts=["signapk_jar=",
-                                              "extra_apks=",
+                             extra_opts="e:d:k:ot:",
+                             extra_long_opts=["extra_apks=",
                                               "default_key_mappings=",
                                               "key_mapping=",
                                               "replace_ota_keys",
diff --git a/tools/signapk/SignApk.java b/tools/signapk/SignApk.java
index caf7935..3244a49 100644
--- a/tools/signapk/SignApk.java
+++ b/tools/signapk/SignApk.java
@@ -247,7 +247,7 @@
         }
     }
 
-    /** Write a .SF file with a digest the specified manifest. */
+    /** Write a .SF file with a digest of the specified manifest. */
     private static void writeSignatureFile(Manifest manifest, OutputStream out)
             throws IOException, GeneralSecurityException {
         Manifest sf = new Manifest();
@@ -304,6 +304,75 @@
         pkcs7.encodeSignedData(out);
     }
 
+    private static void signWholeOutputFile(byte[] zipData,
+                                            OutputStream outputStream,
+                                            X509Certificate publicKey,
+                                            PrivateKey privateKey)
+        throws IOException, GeneralSecurityException {
+
+        // For a zip with no archive comment, the
+        // end-of-central-directory record will be 22 bytes long, so
+        // we expect to find the EOCD marker 22 bytes from the end.
+        if (zipData[zipData.length-22] != 0x50 ||
+            zipData[zipData.length-21] != 0x4b ||
+            zipData[zipData.length-20] != 0x05 ||
+            zipData[zipData.length-19] != 0x06) {
+            throw new IllegalArgumentException("zip data already has an archive comment");
+        }
+
+        Signature signature = Signature.getInstance("SHA1withRSA");
+        signature.initSign(privateKey);
+        signature.update(zipData, 0, zipData.length-2);
+
+        ByteArrayOutputStream temp = new ByteArrayOutputStream();
+
+        // put a readable message and a null char at the start of the
+        // archive comment, so that tools that display the comment
+        // (hopefully) show something sensible.
+        // TODO: anything more useful we can put in this message?
+        byte[] message = "signed by SignApk".getBytes("UTF-8");
+        temp.write(message);
+        temp.write(0);
+        writeSignatureBlock(signature, publicKey, temp);
+        int total_size = temp.size() + 6;
+        if (total_size > 0xffff) {
+            throw new IllegalArgumentException("signature is too big for ZIP file comment");
+        }
+        // signature starts this many bytes from the end of the file
+        int signature_start = total_size - message.length - 1;
+        temp.write(signature_start & 0xff);
+        temp.write((signature_start >> 8) & 0xff);
+        // Why the 0xff bytes?  In a zip file with no archive comment,
+        // bytes [-6:-2] of the file are the little-endian offset from
+        // the start of the file to the central directory.  So for the
+        // two high bytes to be 0xff 0xff, the archive would have to
+        // be nearly 4GB in side.  So it's unlikely that a real
+        // commentless archive would have 0xffs here, and lets us tell
+        // an old signed archive from a new one.
+        temp.write(0xff);
+        temp.write(0xff);
+        temp.write(total_size & 0xff);
+        temp.write((total_size >> 8) & 0xff);
+        temp.flush();
+
+        // Signature verification checks that the EOCD header is the
+        // last such sequence in the file (to avoid minzip finding a
+        // fake EOCD appended after the signature in its scan).  The
+        // odds of producing this sequence by chance are very low, but
+        // let's catch it here if it does.
+        byte[] b = temp.toByteArray();
+        for (int i = 0; i < b.length-3; ++i) {
+            if (b[i] == 0x50 && b[i+1] == 0x4b && b[i+2] == 0x05 && b[i+3] == 0x06) {
+                throw new IllegalArgumentException("found spurious EOCD header at " + i);
+            }
+        }
+
+        outputStream.write(zipData, 0, zipData.length-2);
+        outputStream.write(total_size & 0xff);
+        outputStream.write((total_size >> 8) & 0xff);
+        temp.writeTo(outputStream);
+    }
+
     /**
      * Copy all the files in a manifest from input to output.  We set
      * the modification times in the output to a fixed time, so as to
@@ -340,25 +409,40 @@
     }
 
     public static void main(String[] args) {
-        if (args.length != 4) {
-            System.err.println("Usage: signapk " +
+        if (args.length != 4 && args.length != 5) {
+            System.err.println("Usage: signapk [-w] " +
                     "publickey.x509[.pem] privatekey.pk8 " +
                     "input.jar output.jar");
             System.exit(2);
         }
 
+        boolean signWholeFile = false;
+        int argstart = 0;
+        if (args[0].equals("-w")) {
+            signWholeFile = true;
+            argstart = 1;
+        }
+
         JarFile inputJar = null;
         JarOutputStream outputJar = null;
+        FileOutputStream outputFile = null;
 
         try {
-            X509Certificate publicKey = readPublicKey(new File(args[0]));
+            X509Certificate publicKey = readPublicKey(new File(args[argstart+0]));
 
             // Assume the certificate is valid for at least an hour.
             long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
 
-            PrivateKey privateKey = readPrivateKey(new File(args[1]));
-            inputJar = new JarFile(new File(args[2]), false);  // Don't verify.
-            outputJar = new JarOutputStream(new FileOutputStream(args[3]));
+            PrivateKey privateKey = readPrivateKey(new File(args[argstart+1]));
+            inputJar = new JarFile(new File(args[argstart+2]), false);  // Don't verify.
+
+            OutputStream outputStream = null;
+            if (signWholeFile) {
+                outputStream = new ByteArrayOutputStream();
+            } else {
+                outputStream = outputFile = new FileOutputStream(args[argstart+3]);
+            }
+            outputJar = new JarOutputStream(outputStream);
             outputJar.setLevel(9);
 
             JarEntry je;
@@ -387,13 +471,23 @@
 
             // Everything else
             copyFiles(manifest, inputJar, outputJar, timestamp);
+
+            outputJar.close();
+            outputJar = null;
+            outputStream.flush();
+
+            if (signWholeFile) {
+                outputFile = new FileOutputStream(args[argstart+3]);
+                signWholeOutputFile(((ByteArrayOutputStream)outputStream).toByteArray(),
+                                    outputFile, publicKey, privateKey);
+            }
         } catch (Exception e) {
             e.printStackTrace();
             System.exit(1);
         } finally {
             try {
                 if (inputJar != null) inputJar.close();
-                if (outputJar != null) outputJar.close();
+                if (outputFile != null) outputFile.close();
             } catch (IOException e) {
                 e.printStackTrace();
                 System.exit(1);
diff --git a/tools/warn.py b/tools/warn.py
new file mode 100755
index 0000000..6fea20a
--- /dev/null
+++ b/tools/warn.py
@@ -0,0 +1,523 @@
+#!/usr/bin/env python
+
+import sys
+import re
+
+if len(sys.argv) == 1:
+    print 'usage: ' + sys.argv[0] + ' <build.log>'
+    sys.exit()
+
+# if you add another level, don't forget to give it a color below
+class severity:
+    UNKNOWN=0
+    SKIP=100
+    FIXMENOW=1
+    HIGH=2
+    MEDIUM=3
+    LOW=4
+    HARMLESS=5
+
+def colorforseverity(sev):
+    if sev == severity.FIXMENOW:
+        return 'fuchsia'
+    if sev == severity.HIGH:
+        return 'red'
+    if sev == severity.MEDIUM:
+        return 'orange'
+    if sev == severity.LOW:
+        return 'yellow'
+    if sev == severity.HARMLESS:
+        return 'limegreen'
+    if sev == severity.UNKNOWN:
+        return 'blue'
+    return 'grey'
+
+warnpatterns = [
+    { 'category':'make',    'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'make: overriding commands/ignoring old commands',
+        'patterns':[r".*: warning: overriding commands for target .+",
+                    r".*: warning: ignoring old commands for target .+"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-Wimplicit-function-declaration',
+        'description':'Implicit function declaration',
+        'patterns':[r".*: warning: implicit declaration of function .+"] },
+    { 'category':'C/C++',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: conflicting types for '.+'"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-Wtype-limits',
+        'description':'Expression always evaluates to true or false',
+        'patterns':[r".*: warning: comparison is always false due to limited range of data type",
+                    r".*: warning: comparison of unsigned expression >= 0 is always true",
+                    r".*: warning: comparison of unsigned expression < 0 is always false"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Incompatible pointer types',
+        'patterns':[r".*: warning: assignment from incompatible pointer type",
+                    r".*: warning: passing argument [0-9]+ of '.*' from incompatible pointer type",
+                    r".*: warning: initialization from incompatible pointer type"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-fno-builtin',
+        'description':'Incompatible declaration of built in function',
+        'patterns':[r".*: warning: incompatible implicit declaration of built-in function .+"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wunused-parameter',
+        'description':'Unused parameter',
+        'patterns':[r".*: warning: unused parameter '.*'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wunused',
+        'description':'Unused function, variable or label',
+        'patterns':[r".*: warning: '.+' defined but not used"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wunused-value',
+        'description':'Statement with no effect',
+        'patterns':[r".*: warning: statement with no effect"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmissing-field-initializers',
+        'description':'Missing initializer',
+        'patterns':[r".*: warning: missing initializer"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: \(near initialization for '.+'\)"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wformat',
+        'description':'Format string does not match arguments',
+        'patterns':[r".*: warning: format '.+' expects type '.+', but argument [0-9]+ has type '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wformat-extra-args',
+        'description':'Too many arguments for format string',
+        'patterns':[r".*: warning: too many arguments for format"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wsign-compare',
+        'description':'Comparison between signed and unsigned',
+        'patterns':[r".*: warning: comparison between signed and unsigned",
+                    r".*: warning: comparison of promoted \~unsigned with unsigned",
+                    r".*: warning: signed and unsigned type in conditional expression"] },
+    { 'category':'libpng',  'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'libpng: zero area',
+        'patterns':[r".*libpng warning: Ignoring attempt to set cHRM RGB triangle with zero area"] },
+    { 'category':'aapt',    'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'aapt: no comment for public symbol',
+        'patterns':[r".*: warning: No comment for public symbol .+"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmissing-braces',
+        'description':'Missing braces around initializer',
+        'patterns':[r".*: warning: missing braces around initializer.*"] },
+    { 'category':'C/C++',   'severity':severity.HARMLESS, 'members':[], 'option':'',
+        'description':'No newline at end of file',
+        'patterns':[r".*: warning: no newline at end of file"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wcast-qual',
+        'description':'Qualifier discarded',
+        'patterns':[r".*: warning: passing argument [0-9]+ of '.+' discards qualifiers from pointer target type",
+                    r".*: warning: assignment discards qualifiers from pointer target type",
+                    r".*: warning: return discards qualifiers from pointer target type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wattributes',
+        'description':'Attribute ignored',
+        'patterns':[r".*: warning: '_*packed_*' attribute ignored"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wattributes',
+        'description':'Visibility mismatch',
+        'patterns':[r".*: warning: '.+' declared with greater visibility than the type of its field '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Shift count greater than width of type',
+        'patterns':[r".*: warning: (left|right) shift count >= width of type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'extern &lt;foo&gt; is initialized',
+        'patterns':[r".*: warning: '.+' initialized and declared 'extern'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wold-style-declaration',
+        'description':'Old style declaration',
+        'patterns':[r".*: warning: 'static' is not at beginning of declaration"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wuninitialized',
+        'description':'Variable may be used uninitialized',
+        'patterns':[r".*: warning: '.+' may be used uninitialized in this function"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-Wuninitialized',
+        'description':'Variable is used uninitialized',
+        'patterns':[r".*: warning: '.+' is used uninitialized in this function"] },
+    { 'category':'ld',      'severity':severity.MEDIUM,   'members':[], 'option':'-fshort-enums',
+        'description':'ld: possible enum size mismatch',
+        'patterns':[r".*: warning: .* uses variable-size enums yet the output is to use 32-bit enums; use of enum values across objects may fail"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wpointer-sign',
+        'description':'Pointer targets differ in signedness',
+        'patterns':[r".*: warning: pointer targets in initialization differ in signedness",
+                    r".*: warning: pointer targets in assignment differ in signedness",
+                    r".*: warning: pointer targets in return differ in signedness",
+                    r".*: warning: pointer targets in passing argument [0-9]+ of '.+' differ in signedness"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wstrict-overflow',
+        'description':'Assuming overflow does not occur',
+        'patterns':[r".*: warning: assuming signed overflow does not occur when assuming that .* is always (true|false)"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wempty-body',
+        'description':'Suggest adding braces around empty body',
+        'patterns':[r".*: warning: suggest braces around empty body in an 'if' statement",
+                    r".*: warning: empty body in an if-statement",
+                    r".*: warning: suggest braces around empty body in an 'else' statement",
+                    r".*: warning: empty body in an else-statement"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wparentheses',
+        'description':'Suggest adding parentheses',
+        'patterns':[r".*: warning: suggest explicit braces to avoid ambiguous 'else'",
+                    r".*: warning: suggest parentheses around arithmetic in operand of '.+'",
+                    r".*: warning: suggest parentheses around comparison in operand of '.+'",
+                    r".*: warning: suggest parentheses around '.+?' .+ '.+?'",
+                    r".*: warning: suggest parentheses around assignment used as truth value"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Static variable used in non-static inline function',
+        'patterns':[r".*: warning: '.+' is static but used in inline function '.+' which is not static"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wimplicit int',
+        'description':'No type or storage class (will default to int)',
+        'patterns':[r".*: warning: data definition has no type or storage class"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: type defaults to 'int' in declaration of '.+'"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: parameter names \(without types\) in function declaration"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wstrict-aliasing',
+        'description':'Dereferencing &lt;foo&gt; breaks strict aliasing rules',
+        'patterns':[r".*: warning: dereferencing .* break strict-aliasing rules"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wpointer-to-int-cast',
+        'description':'Cast from pointer to integer of different size',
+        'patterns':[r".*: warning: cast from pointer to integer of different size"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wint-to-pointer-cast',
+        'description':'Cast to pointer from integer of different size',
+        'patterns':[r".*: warning: cast to pointer from integer of different size"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Symbol redefined',
+        'patterns':[r".*: warning: "".+"" redefined"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: this is the location of the previous definition"] },
+    { 'category':'ld',      'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'ld: type and size of dynamic symbol are not defined',
+        'patterns':[r".*: warning: type and size of dynamic symbol `.+' are not defined"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Pointer from integer without cast',
+        'patterns':[r".*: warning: assignment makes pointer from integer without a cast"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Pointer from integer without cast',
+        'patterns':[r".*: warning: passing argument [0-9]+ of '.+' makes pointer from integer without a cast"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Integer from pointer without cast',
+        'patterns':[r".*: warning: assignment makes integer from pointer without a cast"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Integer from pointer without cast',
+        'patterns':[r".*: warning: passing argument [0-9]+ of '.+' makes integer from pointer without a cast"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Integer from pointer without cast',
+        'patterns':[r".*: warning: return makes integer from pointer without a cast"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wunknown-pragmas',
+        'description':'Ignoring pragma',
+        'patterns':[r".*: warning: ignoring #pragma .+"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wclobbered',
+        'description':'Variable might be clobbered by longjmp or vfork',
+        'patterns':[r".*: warning: variable '.+' might be clobbered by 'longjmp' or 'vfork'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wclobbered',
+        'description':'Argument might be clobbered by longjmp or vfork',
+        'patterns':[r".*: warning: argument '.+' might be clobbered by 'longjmp' or 'vfork'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wredundant-decls',
+        'description':'Redundant declaration',
+        'patterns':[r".*: warning: redundant redeclaration of '.+'"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: previous declaration of '.+' was here"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wswitch-enum',
+        'description':'Enum value not handled in switch',
+        'patterns':[r".*: warning: enumeration value '.+' not handled in switch"] },
+    { 'category':'java',    'severity':severity.MEDIUM,   'members':[], 'option':'-encoding',
+        'description':'Java: Non-ascii characters used, but ascii encoding specified',
+        'patterns':[r".*: warning: unmappable character for encoding ascii"] },
+    { 'category':'java',    'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Java: Non-varargs call of varargs method with inexact argument type for last parameter',
+        'patterns':[r".*: warning: non-varargs call of varargs method with inexact argument type for last parameter"] },
+    { 'category':'aapt',    'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'aapt: String marked untranslatable, but translation exists',
+        'patterns':[r".*: warning: string '.+' in .* marked untranslatable but exists in locale '??_??'"] },
+    { 'category':'aapt',    'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'aapt: empty span in string',
+        'patterns':[r".*: warning: empty '.+' span found in text '.+"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Taking address of temporary',
+        'patterns':[r".*: warning: taking address of temporary"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Possible broken line continuation',
+        'patterns':[r".*: warning: backslash and newline separated by space"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Warray-bounds',
+        'description':'Array subscript out of bounds',
+        'patterns':[r".*: warning: array subscript is above array bounds"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Decimal constant is unsigned only in ISO C90',
+        'patterns':[r".*: warning: this decimal constant is unsigned only in ISO C90"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmain',
+        'description':'main is usually a function',
+        'patterns':[r".*: warning: 'main' is usually a function"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Typedef ignored',
+        'patterns':[r".*: warning: 'typedef' was ignored in this declaration"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-Waddress',
+        'description':'Address always evaluates to true',
+        'patterns':[r".*: warning: the address of '.+' will always evaluate as 'true'"] },
+    { 'category':'C/C++',   'severity':severity.FIXMENOW, 'members':[], 'option':'',
+        'description':'Freeing a non-heap object',
+        'patterns':[r".*: warning: attempt to free a non-heap object '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wchar-subscripts',
+        'description':'Array subscript has type char',
+        'patterns':[r".*: warning: array subscript has type 'char'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Constant too large for type',
+        'patterns':[r".*: warning: integer constant is too large for '.+' type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Woverflow',
+        'description':'Constant too large for type, truncated',
+        'patterns':[r".*: warning: large integer implicitly truncated to unsigned type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Woverflow',
+        'description':'Overflow in implicit constant conversion',
+        'patterns':[r".*: warning: overflow in implicit constant conversion"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Declaration does not declare anything',
+        'patterns':[r".*: warning: declaration 'class .+' does not declare anything"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wreorder',
+        'description':'Initialization order will be different',
+        'patterns':[r".*: warning: '.+' will be initialized after"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning:   '.+'"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning:   when initialized here"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmissing-parameter-type',
+        'description':'Parameter type not specified',
+        'patterns':[r".*: warning: type of '.+' defaults to 'int'"] },
+    { 'category':'gcc',     'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Invalid option for C file',
+        'patterns':[r".*: warning: command line option "".+"" is valid for C\+\+\/ObjC\+\+ but not for C"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'User warning',
+        'patterns':[r".*: warning: #warning "".+"""] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wextra',
+        'description':'Dereferencing void*',
+        'patterns':[r".*: warning: dereferencing 'void \*' pointer"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wextra',
+        'description':'Comparison of pointer to zero',
+        'patterns':[r".*: warning: ordered comparison of pointer with integer zero"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wwrite-strings',
+        'description':'Conversion of string constant to non-const char*',
+        'patterns':[r".*: warning: deprecated conversion from string constant to '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wstrict-prototypes',
+        'description':'Function declaration isn''t a prototype',
+        'patterns':[r".*: warning: function declaration isn't a prototype"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wignored-qualifiers',
+        'description':'Type qualifiers ignored on function return value',
+        'patterns':[r".*: warning: type qualifiers ignored on function return type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'&lt;foo&gt; declared inside parameter list, scope limited to this definition',
+        'patterns':[r".*: warning: '.+' declared inside parameter list"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: its scope is only this definition or declaration, which is probably not what you want"] },
+    { 'category':'C/C++',   'severity':severity.LOW,      'members':[], 'option':'-Wcomment',
+        'description':'Line continuation inside comment',
+        'patterns':[r".*: warning: multi-line comment"] },
+    { 'category':'C/C++',   'severity':severity.HARMLESS, 'members':[], 'option':'',
+        'description':'Extra tokens after #endif',
+        'patterns':[r".*: warning: extra tokens at end of #endif directive"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wenum-compare',
+        'description':'Comparison between different enums',
+        'patterns':[r".*: warning: comparison between 'enum .+' and 'enum .+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wconversion',
+        'description':'Implicit conversion of negative number to unsigned type',
+        'patterns':[r".*: warning: converting negative value '.+' to '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Passing NULL as non-pointer argument',
+        'patterns':[r".*: warning: passing NULL to non-pointer argument [0-9]+ of '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wctor-dtor-privacy',
+        'description':'Class seems unusable because of private ctor/dtor' ,
+        'patterns':[r".*: warning: all member functions in class '.+' are private"] },
+    # skip this next one, because it only points out some RefBase-based classes where having a private destructor is perfectly fine
+    { 'category':'C/C++',   'severity':severity.SKIP,     'members':[], 'option':'-Wctor-dtor-privacy',
+        'description':'Class seems unusable because of private ctor/dtor' ,
+        'patterns':[r".*: warning: 'class .+' only defines a private destructor and has no friends"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wctor-dtor-privacy',
+        'description':'Class seems unusable because of private ctor/dtor' ,
+        'patterns':[r".*: warning: 'class .+' only defines private constructors and has no friends"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wpointer-arith',
+        'description':'void* used in arithmetic' ,
+        'patterns':[r".*: warning: pointer of type 'void \*' used in (arithmetic|subtraction)",
+                    r".*: warning: wrong type argument to increment"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wsign-promo',
+        'description':'Overload resolution chose to promote from unsigned or enum to signed type' ,
+        'patterns':[r".*: warning: passing '.+' chooses 'int' over '.* int'"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning:   in call to '.+'"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-Wextra',
+        'description':'Base should be explicitly initialized in copy constructor',
+        'patterns':[r".*: warning: base class '.+' should be explicitly initialized in the copy constructor"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'',
+        'description':'Converting from <type> to <other type>',
+        'patterns':[r".*: warning: converting to '.+' from '.+'"] },
+
+    # these next ones are to deal with formatting problems resulting from the log being mixed up by 'make -j'
+    { 'category':'C/C++',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: ,$"] },
+    { 'category':'C/C++',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: $"] },
+    { 'category':'C/C++',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: In file included from .+,"] },
+
+    # catch-all for warnings this script doesn't know about yet
+    { 'category':'C/C++',   'severity':severity.UNKNOWN,  'members':[], 'option':'',
+        'description':'Unclassified/unrecognized warnings',
+        'patterns':[r".*: warning: .+"] },
+]
+
+anchor = 0
+cur_row_color = 0
+row_colors = [ 'e0e0e0', 'd0d0d0' ]
+
+def output(text):
+    print text,
+
+def htmlbig(param):
+    return '<font size="+2">' + param + '</font>'
+
+def dumphtmlprologue(title):
+    output('<html>\n<head>\n<title>' + title + '</title>\n<body>\n')
+    output(htmlbig(title))
+    output('<p>\n')
+
+def tablerow(text):
+    global cur_row_color
+    output('<tr bgcolor="' + row_colors[cur_row_color] + '"><td colspan="2">',)
+    cur_row_color = 1 - cur_row_color
+    output(text,)
+    output('</td></tr>')
+
+def begintable(text, backgroundcolor):
+    global anchor
+    output('<table border="1" rules="cols" frame="box" width="100%" bgcolor="black"><tr bgcolor="' +
+        backgroundcolor + '"><a name="anchor' + str(anchor) + '"><td>')
+    output(htmlbig(text[0]) + '<br>')
+    for i in text[1:]:
+        output(i + '<br>')
+    output('</td>')
+    output('<td width="100" bgcolor="grey"><a align="right" href="#anchor' + str(anchor-1) +
+        '">previous</a><br><a align="right" href="#anchor' + str(anchor+1) + '">next</a>')
+    output('</td></a></tr>')
+    anchor += 1
+
+def endtable():
+    output('</table><p>')
+
+
+# dump some stats about total number of warnings and such
+def dumpstats():
+    known = 0
+    unknown = 0
+    for i in warnpatterns:
+        if i['severity'] == severity.UNKNOWN:
+            unknown += len(i['members'])
+        elif i['severity'] != severity.SKIP:
+            known += len(i['members'])
+    output('Number of classified warnings: <b>' + str(known) + '</b><br>' )
+    output('Number of unclassified warnings: <b>' + str(unknown) + '</b><br>')
+    total = unknown + known
+    output('Total number of warnings: <b>' + str(total) + '</b>')
+    if total < 1000:
+        output('(low count may indicate incremental build)')
+    output('<p>')
+
+def allpatterns(cat):
+    pats = ''
+    for i in cat['patterns']:
+        pats += i
+        pats += ' / '
+    return pats
+
+def descriptionfor(cat):
+    if cat['description'] != '':
+        return cat['description']
+    return allpatterns(cat)
+
+
+# show which warnings no longer occur
+def dumpfixed():
+    tablestarted = False
+    for i in warnpatterns:
+        if len(i['members']) == 0 and i['severity'] != severity.SKIP:
+            if tablestarted == False:
+                tablestarted = True
+                begintable(['Fixed warnings', 'No more occurences. Please consider turning these in to errors if possible, before they are reintroduced in to the build'], 'blue')
+            tablerow(i['description'] + ' (' + allpatterns(i) + ') ' + i['option'])
+    if tablestarted:
+        endtable()
+
+
+# dump a category, provided it is not marked as 'SKIP' and has more than 0 occurrences
+def dumpcategory(cat):
+    if cat['severity'] != severity.SKIP and len(cat['members']) != 0:
+        header = [descriptionfor(cat),str(len(cat['members'])) + ' occurences:']
+        if cat['option'] != '':
+            header[1:1] = [' (related option: ' + cat['option'] +')']
+        begintable(header, colorforseverity(cat['severity']))
+        for i in cat['members']:
+            tablerow(i)
+        endtable()
+
+
+# dump everything for a given severity
+def dumpseverity(sev):
+    for i in warnpatterns:
+        if i['severity'] == sev:
+            dumpcategory(i)
+
+
+def classifywarning(line):
+    for i in warnpatterns:
+        for cpat in i['compiledpatterns']:
+            if cpat.match(line):
+                i['members'].append(line)
+                return
+    else:
+        # If we end up here, there was a problem parsing the log
+        # probably caused by 'make -j' mixing the output from
+        # 2 or more concurrent compiles
+        pass
+
+# precompiling every pattern speeds up parsing by about 30x
+def compilepatterns():
+    for i in warnpatterns:
+        i['compiledpatterns'] = []
+        for pat in i['patterns']:
+            i['compiledpatterns'].append(re.compile(pat))
+
+infile = open(sys.argv[1], 'r')
+warnings = []
+
+platformversion = 'unknown'
+targetproduct = 'unknown'
+targetvariant = 'unknown'
+linecounter = 0
+
+warningpattern = re.compile('.* warning:.*')
+compilepatterns()
+
+# read the log file and classify all the warnings
+lastmatchedline = ''
+for line in infile:
+    if warningpattern.match(line):
+        if line != lastmatchedline:
+            classifywarning(line)
+            lastmatchedline = line
+    else:
+        # save a little bit of time by only doing this for the first few lines
+        if linecounter < 50:
+            linecounter +=1
+            m = re.search('(?<=^PLATFORM_VERSION=).*', line)
+            if m != None:
+                platformversion = m.group(0)
+            m = re.search('(?<=^TARGET_PRODUCT=).*', line)
+            if m != None:
+                targetproduct = m.group(0)
+            m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
+            if m != None:
+                targetvariant = m.group(0)
+
+
+# dump the html output to stdout
+dumphtmlprologue('Warnings for ' + platformversion + ' - ' + targetproduct + ' - ' + targetvariant)
+dumpstats()
+dumpseverity(severity.FIXMENOW)
+dumpseverity(severity.HIGH)
+dumpseverity(severity.MEDIUM)
+dumpseverity(severity.LOW)
+dumpseverity(severity.HARMLESS)
+dumpseverity(severity.UNKNOWN)
+dumpfixed()
+
diff --git a/tools/zipalign/Android.mk b/tools/zipalign/Android.mk
index e23b699..f90a31c 100644
--- a/tools/zipalign/Android.mk
+++ b/tools/zipalign/Android.mk
@@ -8,7 +8,9 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-	ZipAlign.cpp
+	ZipAlign.cpp \
+	ZipEntry.cpp \
+	ZipFile.cpp
 
 LOCAL_C_INCLUDES += external/zlib
 
diff --git a/tools/zipalign/README.txt b/tools/zipalign/README.txt
index a2e1a5e..9c7d07e 100644
--- a/tools/zipalign/README.txt
+++ b/tools/zipalign/README.txt
@@ -1,7 +1,9 @@
 zipalign -- zip archive alignment tool
 
 usage: zipalign [-f] [-v] <align> infile.zip outfile.zip
+       zipalign -c [-v] <align> infile.zip
 
+  -c : check alignment only (does not modify file)
   -f : overwrite existing outfile.zip
   -v : verbose output
   <align> is in bytes, e.g. "4" provides 32-bit alignment
@@ -29,3 +31,5 @@
 By default, zipalign will not overwrite an existing output file.  With the
 "-f" flag, an existing file will be overwritten.
 
+You can use the "-c" flag to test whether a zip archive is properly aligned.
+
diff --git a/tools/zipalign/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp
index 058f9ed..c2d8159 100644
--- a/tools/zipalign/ZipAlign.cpp
+++ b/tools/zipalign/ZipAlign.cpp
@@ -13,10 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 /*
  * Zip alignment tool
  */
-#include "utils/ZipFile.h"
+#include "ZipFile.h"
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -29,9 +30,15 @@
 void usage(void)
 {
     fprintf(stderr, "Zip alignment utility\n");
+    fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n");
     fprintf(stderr,
         "Usage: zipalign [-f] [-v] <align> infile.zip outfile.zip\n"
-        "       zipalign -c [-v] <align> infile.zip\n" );
+        "       zipalign -c [-v] <align> infile.zip\n\n" );
+    fprintf(stderr,
+        "  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n");
+    fprintf(stderr, "  -c: check alignment only (does not modify file)\n");
+    fprintf(stderr, "  -f: overwrite existing outfile.zip\n");
+    fprintf(stderr, "  -v: verbose output\n");
 }
 
 /*
diff --git a/tools/zipalign/ZipEntry.cpp b/tools/zipalign/ZipEntry.cpp
new file mode 100644
index 0000000..bed0333
--- /dev/null
+++ b/tools/zipalign/ZipEntry.cpp
@@ -0,0 +1,696 @@
+/*
+ * 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.
+ */
+
+//
+// Access to entries in a Zip archive.
+//
+
+#define LOG_TAG "zip"
+
+#include "ZipEntry.h"
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+using namespace android;
+
+/*
+ * Initialize a new ZipEntry structure from a FILE* positioned at a
+ * CentralDirectoryEntry.
+ *
+ * On exit, the file pointer will be at the start of the next CDE or
+ * at the EOCD.
+ */
+status_t ZipEntry::initFromCDE(FILE* fp)
+{
+    status_t result;
+    long posn;
+    bool hasDD;
+
+    //LOGV("initFromCDE ---\n");
+
+    /* read the CDE */
+    result = mCDE.read(fp);
+    if (result != NO_ERROR) {
+        LOGD("mCDE.read failed\n");
+        return result;
+    }
+
+    //mCDE.dump();
+
+    /* using the info in the CDE, go load up the LFH */
+    posn = ftell(fp);
+    if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
+        LOGD("local header seek failed (%ld)\n",
+            mCDE.mLocalHeaderRelOffset);
+        return UNKNOWN_ERROR;
+    }
+
+    result = mLFH.read(fp);
+    if (result != NO_ERROR) {
+        LOGD("mLFH.read failed\n");
+        return result;
+    }
+
+    if (fseek(fp, posn, SEEK_SET) != 0)
+        return UNKNOWN_ERROR;
+
+    //mLFH.dump();
+
+    /*
+     * We *might* need to read the Data Descriptor at this point and
+     * integrate it into the LFH.  If this bit is set, the CRC-32,
+     * compressed size, and uncompressed size will be zero.  In practice
+     * these seem to be rare.
+     */
+    hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
+    if (hasDD) {
+        // do something clever
+        //LOGD("+++ has data descriptor\n");
+    }
+
+    /*
+     * Sanity-check the LFH.  Note that this will fail if the "kUsesDataDescr"
+     * flag is set, because the LFH is incomplete.  (Not a problem, since we
+     * prefer the CDE values.)
+     */
+    if (!hasDD && !compareHeaders()) {
+        LOGW("WARNING: header mismatch\n");
+        // keep going?
+    }
+
+    /*
+     * If the mVersionToExtract is greater than 20, we may have an
+     * issue unpacking the record -- could be encrypted, compressed
+     * with something we don't support, or use Zip64 extensions.  We
+     * can defer worrying about that to when we're extracting data.
+     */
+
+    return NO_ERROR;
+}
+
+/*
+ * Initialize a new entry.  Pass in the file name and an optional comment.
+ *
+ * Initializes the CDE and the LFH.
+ */
+void ZipEntry::initNew(const char* fileName, const char* comment)
+{
+    assert(fileName != NULL && *fileName != '\0');  // name required
+
+    /* most fields are properly initialized by constructor */
+    mCDE.mVersionMadeBy = kDefaultMadeBy;
+    mCDE.mVersionToExtract = kDefaultVersion;
+    mCDE.mCompressionMethod = kCompressStored;
+    mCDE.mFileNameLength = strlen(fileName);
+    if (comment != NULL)
+        mCDE.mFileCommentLength = strlen(comment);
+    mCDE.mExternalAttrs = 0x81b60020;   // matches what WinZip does
+
+    if (mCDE.mFileNameLength > 0) {
+        mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
+        strcpy((char*) mCDE.mFileName, fileName);
+    }
+    if (mCDE.mFileCommentLength > 0) {
+        /* TODO: stop assuming null-terminated ASCII here? */
+        mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
+        strcpy((char*) mCDE.mFileComment, comment);
+    }
+
+    copyCDEtoLFH();
+}
+
+/*
+ * Initialize a new entry, starting with the ZipEntry from a different
+ * archive.
+ *
+ * Initializes the CDE and the LFH.
+ */
+status_t ZipEntry::initFromExternal(const ZipFile* pZipFile,
+    const ZipEntry* pEntry)
+{
+    /*
+     * Copy everything in the CDE over, then fix up the hairy bits.
+     */
+    memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
+
+    if (mCDE.mFileNameLength > 0) {
+        mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
+        if (mCDE.mFileName == NULL)
+            return NO_MEMORY;
+        strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
+    }
+    if (mCDE.mFileCommentLength > 0) {
+        mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
+        if (mCDE.mFileComment == NULL)
+            return NO_MEMORY;
+        strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
+    }
+    if (mCDE.mExtraFieldLength > 0) {
+        /* we null-terminate this, though it may not be a string */
+        mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1];
+        if (mCDE.mExtraField == NULL)
+            return NO_MEMORY;
+        memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
+            mCDE.mExtraFieldLength+1);
+    }
+
+    /* construct the LFH from the CDE */
+    copyCDEtoLFH();
+
+    /*
+     * The LFH "extra" field is independent of the CDE "extra", so we
+     * handle it here.
+     */
+    assert(mLFH.mExtraField == NULL);
+    mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
+    if (mLFH.mExtraFieldLength > 0) {
+        mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
+        if (mLFH.mExtraField == NULL)
+            return NO_MEMORY;
+        memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
+            mLFH.mExtraFieldLength+1);
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Insert pad bytes in the LFH by tweaking the "extra" field.  This will
+ * potentially confuse something that put "extra" data in here earlier,
+ * but I can't find an actual problem.
+ */
+status_t ZipEntry::addPadding(int padding)
+{
+    if (padding <= 0)
+        return INVALID_OPERATION;
+
+    //LOGI("HEY: adding %d pad bytes to existing %d in %s\n",
+    //    padding, mLFH.mExtraFieldLength, mCDE.mFileName);
+
+    if (mLFH.mExtraFieldLength > 0) {
+        /* extend existing field */
+        unsigned char* newExtra;
+
+        newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
+        if (newExtra == NULL)
+            return NO_MEMORY;
+        memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
+        memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
+
+        delete[] mLFH.mExtraField;
+        mLFH.mExtraField = newExtra;
+        mLFH.mExtraFieldLength += padding;
+    } else {
+        /* create new field */
+        mLFH.mExtraField = new unsigned char[padding];
+        memset(mLFH.mExtraField, 0, padding);
+        mLFH.mExtraFieldLength = padding;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Set the fields in the LFH equal to the corresponding fields in the CDE.
+ *
+ * This does not touch the LFH "extra" field.
+ */
+void ZipEntry::copyCDEtoLFH(void)
+{
+    mLFH.mVersionToExtract  = mCDE.mVersionToExtract;
+    mLFH.mGPBitFlag         = mCDE.mGPBitFlag;
+    mLFH.mCompressionMethod = mCDE.mCompressionMethod;
+    mLFH.mLastModFileTime   = mCDE.mLastModFileTime;
+    mLFH.mLastModFileDate   = mCDE.mLastModFileDate;
+    mLFH.mCRC32             = mCDE.mCRC32;
+    mLFH.mCompressedSize    = mCDE.mCompressedSize;
+    mLFH.mUncompressedSize  = mCDE.mUncompressedSize;
+    mLFH.mFileNameLength    = mCDE.mFileNameLength;
+    // the "extra field" is independent
+
+    delete[] mLFH.mFileName;
+    if (mLFH.mFileNameLength > 0) {
+        mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
+        strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
+    } else {
+        mLFH.mFileName = NULL;
+    }
+}
+
+/*
+ * Set some information about a file after we add it.
+ */
+void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
+    int compressionMethod)
+{
+    mCDE.mCompressionMethod = compressionMethod;
+    mCDE.mCRC32 = crc32;
+    mCDE.mCompressedSize = compLen;
+    mCDE.mUncompressedSize = uncompLen;
+    mCDE.mCompressionMethod = compressionMethod;
+    if (compressionMethod == kCompressDeflated) {
+        mCDE.mGPBitFlag |= 0x0002;      // indicates maximum compression used
+    }
+    copyCDEtoLFH();
+}
+
+/*
+ * See if the data in mCDE and mLFH match up.  This is mostly useful for
+ * debugging these classes, but it can be used to identify damaged
+ * archives.
+ *
+ * Returns "false" if they differ.
+ */
+bool ZipEntry::compareHeaders(void) const
+{
+    if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
+        LOGV("cmp: VersionToExtract\n");
+        return false;
+    }
+    if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
+        LOGV("cmp: GPBitFlag\n");
+        return false;
+    }
+    if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
+        LOGV("cmp: CompressionMethod\n");
+        return false;
+    }
+    if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
+        LOGV("cmp: LastModFileTime\n");
+        return false;
+    }
+    if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
+        LOGV("cmp: LastModFileDate\n");
+        return false;
+    }
+    if (mCDE.mCRC32 != mLFH.mCRC32) {
+        LOGV("cmp: CRC32\n");
+        return false;
+    }
+    if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
+        LOGV("cmp: CompressedSize\n");
+        return false;
+    }
+    if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
+        LOGV("cmp: UncompressedSize\n");
+        return false;
+    }
+    if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
+        LOGV("cmp: FileNameLength\n");
+        return false;
+    }
+#if 0       // this seems to be used for padding, not real data
+    if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
+        LOGV("cmp: ExtraFieldLength\n");
+        return false;
+    }
+#endif
+    if (mCDE.mFileName != NULL) {
+        if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
+            LOGV("cmp: FileName\n");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+/*
+ * Convert the DOS date/time stamp into a UNIX time stamp.
+ */
+time_t ZipEntry::getModWhen(void) const
+{
+    struct tm parts;
+
+    parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
+    parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
+    parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
+    parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
+    parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
+    parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
+    parts.tm_wday = parts.tm_yday = 0;
+    parts.tm_isdst = -1;        // DST info "not available"
+
+    return mktime(&parts);
+}
+
+/*
+ * Set the CDE/LFH timestamp from UNIX time.
+ */
+void ZipEntry::setModWhen(time_t when)
+{
+#ifdef HAVE_LOCALTIME_R
+    struct tm tmResult;
+#endif
+    time_t even;
+    unsigned short zdate, ztime;
+
+    struct tm* ptm;
+
+    /* round up to an even number of seconds */
+    even = (time_t)(((unsigned long)(when) + 1) & (~1));
+
+    /* expand */
+#ifdef HAVE_LOCALTIME_R
+    ptm = localtime_r(&even, &tmResult);
+#else
+    ptm = localtime(&even);
+#endif
+
+    int year;
+    year = ptm->tm_year;
+    if (year < 80)
+        year = 80;
+
+    zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
+    ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+
+    mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
+    mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
+}
+
+
+/*
+ * ===========================================================================
+ *      ZipEntry::LocalFileHeader
+ * ===========================================================================
+ */
+
+/*
+ * Read a local file header.
+ *
+ * On entry, "fp" points to the signature at the start of the header.
+ * On exit, "fp" points to the start of data.
+ */
+status_t ZipEntry::LocalFileHeader::read(FILE* fp)
+{
+    status_t result = NO_ERROR;
+    unsigned char buf[kLFHLen];
+
+    assert(mFileName == NULL);
+    assert(mExtraField == NULL);
+
+    if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
+        LOGD("whoops: didn't find expected signature\n");
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
+    mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
+    mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
+    mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
+    mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
+    mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
+    mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
+    mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
+    mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
+    mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
+
+    // TODO: validate sizes
+
+    /* grab filename */
+    if (mFileNameLength != 0) {
+        mFileName = new unsigned char[mFileNameLength+1];
+        if (mFileName == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mFileName[mFileNameLength] = '\0';
+    }
+
+    /* grab extra field */
+    if (mExtraFieldLength != 0) {
+        mExtraField = new unsigned char[mExtraFieldLength+1];
+        if (mExtraField == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mExtraField[mExtraFieldLength] = '\0';
+    }
+
+bail:
+    return result;
+}
+
+/*
+ * Write a local file header.
+ */
+status_t ZipEntry::LocalFileHeader::write(FILE* fp)
+{
+    unsigned char buf[kLFHLen];
+
+    ZipEntry::putLongLE(&buf[0x00], kSignature);
+    ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
+    ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
+    ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
+    ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
+    ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
+    ZipEntry::putLongLE(&buf[0x0e], mCRC32);
+    ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
+    ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
+    ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
+    ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
+
+    if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
+        return UNKNOWN_ERROR;
+
+    /* write filename */
+    if (mFileNameLength != 0) {
+        if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
+            return UNKNOWN_ERROR;
+    }
+
+    /* write "extra field" */
+    if (mExtraFieldLength != 0) {
+        if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
+            return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+
+/*
+ * Dump the contents of a LocalFileHeader object.
+ */
+void ZipEntry::LocalFileHeader::dump(void) const
+{
+    LOGD(" LocalFileHeader contents:\n");
+    LOGD("  versToExt=%u gpBits=0x%04x compression=%u\n",
+        mVersionToExtract, mGPBitFlag, mCompressionMethod);
+    LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+        mLastModFileTime, mLastModFileDate, mCRC32);
+    LOGD("  compressedSize=%lu uncompressedSize=%lu\n",
+        mCompressedSize, mUncompressedSize);
+    LOGD("  filenameLen=%u extraLen=%u\n",
+        mFileNameLength, mExtraFieldLength);
+    if (mFileName != NULL)
+        LOGD("  filename: '%s'\n", mFileName);
+}
+
+
+/*
+ * ===========================================================================
+ *      ZipEntry::CentralDirEntry
+ * ===========================================================================
+ */
+
+/*
+ * Read the central dir entry that appears next in the file.
+ *
+ * On entry, "fp" should be positioned on the signature bytes for the
+ * entry.  On exit, "fp" will point at the signature word for the next
+ * entry or for the EOCD.
+ */
+status_t ZipEntry::CentralDirEntry::read(FILE* fp)
+{
+    status_t result = NO_ERROR;
+    unsigned char buf[kCDELen];
+
+    /* no re-use */
+    assert(mFileName == NULL);
+    assert(mExtraField == NULL);
+    assert(mFileComment == NULL);
+
+    if (fread(buf, 1, kCDELen, fp) != kCDELen) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
+        LOGD("Whoops: didn't find expected signature\n");
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
+    mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
+    mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
+    mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
+    mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
+    mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
+    mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
+    mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
+    mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
+    mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
+    mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
+    mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
+    mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
+    mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
+    mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
+    mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
+
+    // TODO: validate sizes and offsets
+
+    /* grab filename */
+    if (mFileNameLength != 0) {
+        mFileName = new unsigned char[mFileNameLength+1];
+        if (mFileName == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mFileName[mFileNameLength] = '\0';
+    }
+
+    /* read "extra field" */
+    if (mExtraFieldLength != 0) {
+        mExtraField = new unsigned char[mExtraFieldLength+1];
+        if (mExtraField == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mExtraField[mExtraFieldLength] = '\0';
+    }
+
+
+    /* grab comment, if any */
+    if (mFileCommentLength != 0) {
+        mFileComment = new unsigned char[mFileCommentLength+1];
+        if (mFileComment == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
+        {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mFileComment[mFileCommentLength] = '\0';
+    }
+
+bail:
+    return result;
+}
+
+/*
+ * Write a central dir entry.
+ */
+status_t ZipEntry::CentralDirEntry::write(FILE* fp)
+{
+    unsigned char buf[kCDELen];
+
+    ZipEntry::putLongLE(&buf[0x00], kSignature);
+    ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
+    ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
+    ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
+    ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
+    ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
+    ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
+    ZipEntry::putLongLE(&buf[0x10], mCRC32);
+    ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
+    ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
+    ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
+    ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
+    ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
+    ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
+    ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
+    ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
+    ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
+
+    if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
+        return UNKNOWN_ERROR;
+
+    /* write filename */
+    if (mFileNameLength != 0) {
+        if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
+            return UNKNOWN_ERROR;
+    }
+
+    /* write "extra field" */
+    if (mExtraFieldLength != 0) {
+        if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
+            return UNKNOWN_ERROR;
+    }
+
+    /* write comment */
+    if (mFileCommentLength != 0) {
+        if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
+            return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Dump the contents of a CentralDirEntry object.
+ */
+void ZipEntry::CentralDirEntry::dump(void) const
+{
+    LOGD(" CentralDirEntry contents:\n");
+    LOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
+        mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
+    LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+        mLastModFileTime, mLastModFileDate, mCRC32);
+    LOGD("  compressedSize=%lu uncompressedSize=%lu\n",
+        mCompressedSize, mUncompressedSize);
+    LOGD("  filenameLen=%u extraLen=%u commentLen=%u\n",
+        mFileNameLength, mExtraFieldLength, mFileCommentLength);
+    LOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
+        mDiskNumberStart, mInternalAttrs, mExternalAttrs,
+        mLocalHeaderRelOffset);
+
+    if (mFileName != NULL)
+        LOGD("  filename: '%s'\n", mFileName);
+    if (mFileComment != NULL)
+        LOGD("  comment: '%s'\n", mFileComment);
+}
+
diff --git a/tools/zipalign/ZipEntry.h b/tools/zipalign/ZipEntry.h
new file mode 100644
index 0000000..7f721b4
--- /dev/null
+++ b/tools/zipalign/ZipEntry.h
@@ -0,0 +1,345 @@
+/*
+ * 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.
+ */
+
+//
+// Zip archive entries.
+//
+// The ZipEntry class is tightly meshed with the ZipFile class.
+//
+#ifndef __LIBS_ZIPENTRY_H
+#define __LIBS_ZIPENTRY_H
+
+#include <utils/Errors.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace android {
+
+class ZipFile;
+
+/*
+ * ZipEntry objects represent a single entry in a Zip archive.
+ *
+ * You can use one of these to get or set information about an entry, but
+ * there are no functions here for accessing the data itself.  (We could
+ * tuck a pointer to the ZipFile in here for convenience, but that raises
+ * the likelihood of using ZipEntry objects after discarding the ZipFile.)
+ *
+ * File information is stored in two places: next to the file data (the Local
+ * File Header, and possibly a Data Descriptor), and at the end of the file
+ * (the Central Directory Entry).  The two must be kept in sync.
+ */
+class ZipEntry {
+public:
+    friend class ZipFile;
+
+    ZipEntry(void)
+        : mDeleted(false), mMarked(false)
+        {}
+    ~ZipEntry(void) {}
+
+    /*
+     * Returns "true" if the data is compressed.
+     */
+    bool isCompressed(void) const {
+        return mCDE.mCompressionMethod != kCompressStored;
+    }
+    int getCompressionMethod(void) const { return mCDE.mCompressionMethod; }
+
+    /*
+     * Return the uncompressed length.
+     */
+    off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; }
+
+    /*
+     * Return the compressed length.  For uncompressed data, this returns
+     * the same thing as getUncompresesdLen().
+     */
+    off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }
+
+    /*
+     * Return the absolute file offset of the start of the compressed or
+     * uncompressed data.
+     */
+    off_t getFileOffset(void) const {
+        return mCDE.mLocalHeaderRelOffset +
+                LocalFileHeader::kLFHLen +
+                mLFH.mFileNameLength +
+                mLFH.mExtraFieldLength;
+    }
+
+    /*
+     * Return the data CRC.
+     */
+    unsigned long getCRC32(void) const { return mCDE.mCRC32; }
+
+    /*
+     * Return file modification time in UNIX seconds-since-epoch.
+     */
+    time_t getModWhen(void) const;
+
+    /*
+     * Return the archived file name.
+     */
+    const char* getFileName(void) const { return (const char*) mCDE.mFileName; }
+
+    /*
+     * Application-defined "mark".  Can be useful when synchronizing the
+     * contents of an archive with contents on disk.
+     */
+    bool getMarked(void) const { return mMarked; }
+    void setMarked(bool val) { mMarked = val; }
+
+    /*
+     * Some basic functions for raw data manipulation.  "LE" means
+     * Little Endian.
+     */
+    static inline unsigned short getShortLE(const unsigned char* buf) {
+        return buf[0] | (buf[1] << 8);
+    }
+    static inline unsigned long getLongLE(const unsigned char* buf) {
+        return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+    }
+    static inline void putShortLE(unsigned char* buf, short val) {
+        buf[0] = (unsigned char) val;
+        buf[1] = (unsigned char) (val >> 8);
+    }
+    static inline void putLongLE(unsigned char* buf, long val) {
+        buf[0] = (unsigned char) val;
+        buf[1] = (unsigned char) (val >> 8);
+        buf[2] = (unsigned char) (val >> 16);
+        buf[3] = (unsigned char) (val >> 24);
+    }
+
+    /* defined for Zip archives */
+    enum {
+        kCompressStored     = 0,        // no compression
+        // shrunk           = 1,
+        // reduced 1        = 2,
+        // reduced 2        = 3,
+        // reduced 3        = 4,
+        // reduced 4        = 5,
+        // imploded         = 6,
+        // tokenized        = 7,
+        kCompressDeflated   = 8,        // standard deflate
+        // Deflate64        = 9,
+        // lib imploded     = 10,
+        // reserved         = 11,
+        // bzip2            = 12,
+    };
+
+    /*
+     * Deletion flag.  If set, the entry will be removed on the next
+     * call to "flush".
+     */
+    bool getDeleted(void) const { return mDeleted; }
+
+protected:
+    /*
+     * Initialize the structure from the file, which is pointing at
+     * our Central Directory entry.
+     */
+    status_t initFromCDE(FILE* fp);
+
+    /*
+     * Initialize the structure for a new file.  We need the filename
+     * and comment so that we can properly size the LFH area.  The
+     * filename is mandatory, the comment is optional.
+     */
+    void initNew(const char* fileName, const char* comment);
+
+    /*
+     * Initialize the structure with the contents of a ZipEntry from
+     * another file.
+     */
+    status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry);
+
+    /*
+     * Add some pad bytes to the LFH.  We do this by adding or resizing
+     * the "extra" field.
+     */
+    status_t addPadding(int padding);
+
+    /*
+     * Set information about the data for this entry.
+     */
+    void setDataInfo(long uncompLen, long compLen, unsigned long crc32,
+        int compressionMethod);
+
+    /*
+     * Set the modification date.
+     */
+    void setModWhen(time_t when);
+
+    /*
+     * Return the offset of the local file header.
+     */
+    off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
+
+    /*
+     * Set the offset of the local file header, relative to the start of
+     * the current file.
+     */
+    void setLFHOffset(off_t offset) {
+        mCDE.mLocalHeaderRelOffset = (long) offset;
+    }
+
+    /* mark for deletion; used by ZipFile::remove() */
+    void setDeleted(void) { mDeleted = true; }
+
+private:
+    /* these are private and not defined */
+    ZipEntry(const ZipEntry& src);
+    ZipEntry& operator=(const ZipEntry& src);
+
+    /* returns "true" if the CDE and the LFH agree */
+    bool compareHeaders(void) const;
+    void copyCDEtoLFH(void);
+
+    bool        mDeleted;       // set if entry is pending deletion
+    bool        mMarked;        // app-defined marker
+
+    /*
+     * Every entry in the Zip archive starts off with one of these.
+     */
+    class LocalFileHeader {
+    public:
+        LocalFileHeader(void) :
+            mVersionToExtract(0),
+            mGPBitFlag(0),
+            mCompressionMethod(0),
+            mLastModFileTime(0),
+            mLastModFileDate(0),
+            mCRC32(0),
+            mCompressedSize(0),
+            mUncompressedSize(0),
+            mFileNameLength(0),
+            mExtraFieldLength(0),
+            mFileName(NULL),
+            mExtraField(NULL)
+        {}
+        virtual ~LocalFileHeader(void) {
+            delete[] mFileName;
+            delete[] mExtraField;
+        }
+
+        status_t read(FILE* fp);
+        status_t write(FILE* fp);
+
+        // unsigned long mSignature;
+        unsigned short  mVersionToExtract;
+        unsigned short  mGPBitFlag;
+        unsigned short  mCompressionMethod;
+        unsigned short  mLastModFileTime;
+        unsigned short  mLastModFileDate;
+        unsigned long   mCRC32;
+        unsigned long   mCompressedSize;
+        unsigned long   mUncompressedSize;
+        unsigned short  mFileNameLength;
+        unsigned short  mExtraFieldLength;
+        unsigned char*  mFileName;
+        unsigned char*  mExtraField;
+
+        enum {
+            kSignature      = 0x04034b50,
+            kLFHLen         = 30,       // LocalFileHdr len, excl. var fields
+        };
+
+        void dump(void) const;
+    };
+
+    /*
+     * Every entry in the Zip archive has one of these in the "central
+     * directory" at the end of the file.
+     */
+    class CentralDirEntry {
+    public:
+        CentralDirEntry(void) :
+            mVersionMadeBy(0),
+            mVersionToExtract(0),
+            mGPBitFlag(0),
+            mCompressionMethod(0),
+            mLastModFileTime(0),
+            mLastModFileDate(0),
+            mCRC32(0),
+            mCompressedSize(0),
+            mUncompressedSize(0),
+            mFileNameLength(0),
+            mExtraFieldLength(0),
+            mFileCommentLength(0),
+            mDiskNumberStart(0),
+            mInternalAttrs(0),
+            mExternalAttrs(0),
+            mLocalHeaderRelOffset(0),
+            mFileName(NULL),
+            mExtraField(NULL),
+            mFileComment(NULL)
+        {}
+        virtual ~CentralDirEntry(void) {
+            delete[] mFileName;
+            delete[] mExtraField;
+            delete[] mFileComment;
+        }
+
+        status_t read(FILE* fp);
+        status_t write(FILE* fp);
+
+        // unsigned long mSignature;
+        unsigned short  mVersionMadeBy;
+        unsigned short  mVersionToExtract;
+        unsigned short  mGPBitFlag;
+        unsigned short  mCompressionMethod;
+        unsigned short  mLastModFileTime;
+        unsigned short  mLastModFileDate;
+        unsigned long   mCRC32;
+        unsigned long   mCompressedSize;
+        unsigned long   mUncompressedSize;
+        unsigned short  mFileNameLength;
+        unsigned short  mExtraFieldLength;
+        unsigned short  mFileCommentLength;
+        unsigned short  mDiskNumberStart;
+        unsigned short  mInternalAttrs;
+        unsigned long   mExternalAttrs;
+        unsigned long   mLocalHeaderRelOffset;
+        unsigned char*  mFileName;
+        unsigned char*  mExtraField;
+        unsigned char*  mFileComment;
+
+        void dump(void) const;
+
+        enum {
+            kSignature      = 0x02014b50,
+            kCDELen         = 46,       // CentralDirEnt len, excl. var fields
+        };
+    };
+
+    enum {
+        //kDataDescriptorSignature  = 0x08074b50,   // currently unused
+        kDataDescriptorLen  = 16,           // four 32-bit fields
+
+        kDefaultVersion     = 20,           // need deflate, nothing much else
+        kDefaultMadeBy      = 0x0317,       // 03=UNIX, 17=spec v2.3
+        kUsesDataDescr      = 0x0008,       // GPBitFlag bit 3
+    };
+
+    LocalFileHeader     mLFH;
+    CentralDirEntry     mCDE;
+};
+
+}; // namespace android
+
+#endif // __LIBS_ZIPENTRY_H
diff --git a/tools/zipalign/ZipFile.cpp b/tools/zipalign/ZipFile.cpp
new file mode 100644
index 0000000..62c9383
--- /dev/null
+++ b/tools/zipalign/ZipFile.cpp
@@ -0,0 +1,1297 @@
+/*
+ * 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.
+ */
+
+//
+// Access to Zip archives.
+//
+
+#define LOG_TAG "zip"
+
+#include <utils/ZipUtils.h>
+#include <utils/Log.h>
+
+#include "ZipFile.h"
+
+#include <zlib.h>
+#define DEF_MEM_LEVEL 8                // normally in zutil.h?
+
+#include <memory.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <assert.h>
+
+using namespace android;
+
+/*
+ * Some environments require the "b", some choke on it.
+ */
+#define FILE_OPEN_RO        "rb"
+#define FILE_OPEN_RW        "r+b"
+#define FILE_OPEN_RW_CREATE "w+b"
+
+/* should live somewhere else? */
+static status_t errnoToStatus(int err)
+{
+    if (err == ENOENT)
+        return NAME_NOT_FOUND;
+    else if (err == EACCES)
+        return PERMISSION_DENIED;
+    else
+        return UNKNOWN_ERROR;
+}
+
+/*
+ * Open a file and parse its guts.
+ */
+status_t ZipFile::open(const char* zipFileName, int flags)
+{
+    bool newArchive = false;
+
+    assert(mZipFp == NULL);     // no reopen
+
+    if ((flags & kOpenTruncate))
+        flags |= kOpenCreate;           // trunc implies create
+
+    if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
+        return INVALID_OPERATION;       // not both
+    if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
+        return INVALID_OPERATION;       // not neither
+    if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
+        return INVALID_OPERATION;       // create requires write
+
+    if (flags & kOpenTruncate) {
+        newArchive = true;
+    } else {
+        newArchive = (access(zipFileName, F_OK) != 0);
+        if (!(flags & kOpenCreate) && newArchive) {
+            /* not creating, must already exist */
+            LOGD("File %s does not exist", zipFileName);
+            return NAME_NOT_FOUND;
+        }
+    }
+
+    /* open the file */
+    const char* openflags;
+    if (flags & kOpenReadWrite) {
+        if (newArchive)
+            openflags = FILE_OPEN_RW_CREATE;
+        else
+            openflags = FILE_OPEN_RW;
+    } else {
+        openflags = FILE_OPEN_RO;
+    }
+    mZipFp = fopen(zipFileName, openflags);
+    if (mZipFp == NULL) {
+        int err = errno;
+        LOGD("fopen failed: %d\n", err);
+        return errnoToStatus(err);
+    }
+
+    status_t result;
+    if (!newArchive) {
+        /*
+         * Load the central directory.  If that fails, then this probably
+         * isn't a Zip archive.
+         */
+        result = readCentralDir();
+    } else {
+        /*
+         * Newly-created.  The EndOfCentralDir constructor actually
+         * sets everything to be the way we want it (all zeroes).  We
+         * set mNeedCDRewrite so that we create *something* if the
+         * caller doesn't add any files.  (We could also just unlink
+         * the file if it's brand new and nothing was added, but that's
+         * probably doing more than we really should -- the user might
+         * have a need for empty zip files.)
+         */
+        mNeedCDRewrite = true;
+        result = NO_ERROR;
+    }
+
+    if (flags & kOpenReadOnly)
+        mReadOnly = true;
+    else
+        assert(!mReadOnly);
+
+    return result;
+}
+
+/*
+ * Return the Nth entry in the archive.
+ */
+ZipEntry* ZipFile::getEntryByIndex(int idx) const
+{
+    if (idx < 0 || idx >= (int) mEntries.size())
+        return NULL;
+
+    return mEntries[idx];
+}
+
+/*
+ * Find an entry by name.
+ */
+ZipEntry* ZipFile::getEntryByName(const char* fileName) const
+{
+    /*
+     * Do a stupid linear string-compare search.
+     *
+     * There are various ways to speed this up, especially since it's rare
+     * to intermingle changes to the archive with "get by name" calls.  We
+     * don't want to sort the mEntries vector itself, however, because
+     * it's used to recreate the Central Directory.
+     *
+     * (Hash table works, parallel list of pointers in sorted order is good.)
+     */
+    int idx;
+
+    for (idx = mEntries.size()-1; idx >= 0; idx--) {
+        ZipEntry* pEntry = mEntries[idx];
+        if (!pEntry->getDeleted() &&
+            strcmp(fileName, pEntry->getFileName()) == 0)
+        {
+            return pEntry;
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Empty the mEntries vector.
+ */
+void ZipFile::discardEntries(void)
+{
+    int count = mEntries.size();
+
+    while (--count >= 0)
+        delete mEntries[count];
+
+    mEntries.clear();
+}
+
+
+/*
+ * Find the central directory and read the contents.
+ *
+ * The fun thing about ZIP archives is that they may or may not be
+ * readable from start to end.  In some cases, notably for archives
+ * that were written to stdout, the only length information is in the
+ * central directory at the end of the file.
+ *
+ * Of course, the central directory can be followed by a variable-length
+ * comment field, so we have to scan through it backwards.  The comment
+ * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
+ * itself, plus apparently sometimes people throw random junk on the end
+ * just for the fun of it.
+ *
+ * This is all a little wobbly.  If the wrong value ends up in the EOCD
+ * area, we're hosed.  This appears to be the way that everbody handles
+ * it though, so we're in pretty good company if this fails.
+ */
+status_t ZipFile::readCentralDir(void)
+{
+    status_t result = NO_ERROR;
+    unsigned char* buf = NULL;
+    off_t fileLength, seekStart;
+    long readAmount;
+    int i;
+
+    fseek(mZipFp, 0, SEEK_END);
+    fileLength = ftell(mZipFp);
+    rewind(mZipFp);
+
+    /* too small to be a ZIP archive? */
+    if (fileLength < EndOfCentralDir::kEOCDLen) {
+        LOGD("Length is %ld -- too small\n", (long)fileLength);
+        result = INVALID_OPERATION;
+        goto bail;
+    }
+
+    buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
+    if (buf == NULL) {
+        LOGD("Failure allocating %d bytes for EOCD search",
+             EndOfCentralDir::kMaxEOCDSearch);
+        result = NO_MEMORY;
+        goto bail;
+    }
+
+    if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
+        seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
+        readAmount = EndOfCentralDir::kMaxEOCDSearch;
+    } else {
+        seekStart = 0;
+        readAmount = (long) fileLength;
+    }
+    if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
+        LOGD("Failure seeking to end of zip at %ld", (long) seekStart);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /* read the last part of the file into the buffer */
+    if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
+        LOGD("short file? wanted %ld\n", readAmount);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /* find the end-of-central-dir magic */
+    for (i = readAmount - 4; i >= 0; i--) {
+        if (buf[i] == 0x50 &&
+            ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
+        {
+            LOGV("+++ Found EOCD at buf+%d\n", i);
+            break;
+        }
+    }
+    if (i < 0) {
+        LOGD("EOCD not found, not Zip\n");
+        result = INVALID_OPERATION;
+        goto bail;
+    }
+
+    /* extract eocd values */
+    result = mEOCD.readBuf(buf + i, readAmount - i);
+    if (result != NO_ERROR) {
+        LOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
+        goto bail;
+    }
+    //mEOCD.dump();
+
+    if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
+        mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
+    {
+        LOGD("Archive spanning not supported\n");
+        result = INVALID_OPERATION;
+        goto bail;
+    }
+
+    /*
+     * So far so good.  "mCentralDirSize" is the size in bytes of the
+     * central directory, so we can just seek back that far to find it.
+     * We can also seek forward mCentralDirOffset bytes from the
+     * start of the file.
+     *
+     * We're not guaranteed to have the rest of the central dir in the
+     * buffer, nor are we guaranteed that the central dir will have any
+     * sort of convenient size.  We need to skip to the start of it and
+     * read the header, then the other goodies.
+     *
+     * The only thing we really need right now is the file comment, which
+     * we're hoping to preserve.
+     */
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+        LOGD("Failure seeking to central dir offset %ld\n",
+             mEOCD.mCentralDirOffset);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /*
+     * Loop through and read the central dir entries.
+     */
+    LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
+    int entry;
+    for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
+        ZipEntry* pEntry = new ZipEntry;
+
+        result = pEntry->initFromCDE(mZipFp);
+        if (result != NO_ERROR) {
+            LOGD("initFromCDE failed\n");
+            delete pEntry;
+            goto bail;
+        }
+
+        mEntries.add(pEntry);
+    }
+
+
+    /*
+     * If all went well, we should now be back at the EOCD.
+     */
+    {
+        unsigned char checkBuf[4];
+        if (fread(checkBuf, 1, 4, mZipFp) != 4) {
+            LOGD("EOCD check read failed\n");
+            result = INVALID_OPERATION;
+            goto bail;
+        }
+        if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
+            LOGD("EOCD read check failed\n");
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        LOGV("+++ EOCD read check passed\n");
+    }
+
+bail:
+    delete[] buf;
+    return result;
+}
+
+
+/*
+ * Add a new file to the archive.
+ *
+ * This requires creating and populating a ZipEntry structure, and copying
+ * the data into the file at the appropriate position.  The "appropriate
+ * position" is the current location of the central directory, which we
+ * casually overwrite (we can put it back later).
+ *
+ * If we were concerned about safety, we would want to make all changes
+ * in a temp file and then overwrite the original after everything was
+ * safely written.  Not really a concern for us.
+ */
+status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
+    const char* storageName, int sourceType, int compressionMethod,
+    ZipEntry** ppEntry)
+{
+    ZipEntry* pEntry = NULL;
+    status_t result = NO_ERROR;
+    long lfhPosn, startPosn, endPosn, uncompressedLen;
+    FILE* inputFp = NULL;
+    unsigned long crc;
+    time_t modWhen;
+
+    if (mReadOnly)
+        return INVALID_OPERATION;
+
+    assert(compressionMethod == ZipEntry::kCompressDeflated ||
+           compressionMethod == ZipEntry::kCompressStored);
+
+    /* make sure we're in a reasonable state */
+    assert(mZipFp != NULL);
+    assert(mEntries.size() == mEOCD.mTotalNumEntries);
+
+    /* make sure it doesn't already exist */
+    if (getEntryByName(storageName) != NULL)
+        return ALREADY_EXISTS;
+
+    if (!data) {
+        inputFp = fopen(fileName, FILE_OPEN_RO);
+        if (inputFp == NULL)
+            return errnoToStatus(errno);
+    }
+
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    pEntry = new ZipEntry;
+    pEntry->initNew(storageName, NULL);
+
+    /*
+     * From here on out, failures are more interesting.
+     */
+    mNeedCDRewrite = true;
+
+    /*
+     * Write the LFH, even though it's still mostly blank.  We need it
+     * as a place-holder.  In theory the LFH isn't necessary, but in
+     * practice some utilities demand it.
+     */
+    lfhPosn = ftell(mZipFp);
+    pEntry->mLFH.write(mZipFp);
+    startPosn = ftell(mZipFp);
+
+    /*
+     * Copy the data in, possibly compressing it as we go.
+     */
+    if (sourceType == ZipEntry::kCompressStored) {
+        if (compressionMethod == ZipEntry::kCompressDeflated) {
+            bool failed = false;
+            result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
+            if (result != NO_ERROR) {
+                LOGD("compression failed, storing\n");
+                failed = true;
+            } else {
+                /*
+                 * Make sure it has compressed "enough".  This probably ought
+                 * to be set through an API call, but I don't expect our
+                 * criteria to change over time.
+                 */
+                long src = inputFp ? ftell(inputFp) : size;
+                long dst = ftell(mZipFp) - startPosn;
+                if (dst + (dst / 10) > src) {
+                    LOGD("insufficient compression (src=%ld dst=%ld), storing\n",
+                        src, dst);
+                    failed = true;
+                }
+            }
+
+            if (failed) {
+                compressionMethod = ZipEntry::kCompressStored;
+                if (inputFp) rewind(inputFp);
+                fseek(mZipFp, startPosn, SEEK_SET);
+                /* fall through to kCompressStored case */
+            }
+        }
+        /* handle "no compression" request, or failed compression from above */
+        if (compressionMethod == ZipEntry::kCompressStored) {
+            if (inputFp) {
+                result = copyFpToFp(mZipFp, inputFp, &crc);
+            } else {
+                result = copyDataToFp(mZipFp, data, size, &crc);
+            }
+            if (result != NO_ERROR) {
+                // don't need to truncate; happens in CDE rewrite
+                LOGD("failed copying data in\n");
+                goto bail;
+            }
+        }
+
+        // currently seeked to end of file
+        uncompressedLen = inputFp ? ftell(inputFp) : size;
+    } else if (sourceType == ZipEntry::kCompressDeflated) {
+        /* we should support uncompressed-from-compressed, but it's not
+         * important right now */
+        assert(compressionMethod == ZipEntry::kCompressDeflated);
+
+        bool scanResult;
+        int method;
+        long compressedLen;
+
+        scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
+                        &compressedLen, &crc);
+        if (!scanResult || method != ZipEntry::kCompressDeflated) {
+            LOGD("this isn't a deflated gzip file?");
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+
+        result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
+        if (result != NO_ERROR) {
+            LOGD("failed copying gzip data in\n");
+            goto bail;
+        }
+    } else {
+        assert(false);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /*
+     * We could write the "Data Descriptor", but there doesn't seem to
+     * be any point since we're going to go back and write the LFH.
+     *
+     * Update file offsets.
+     */
+    endPosn = ftell(mZipFp);            // seeked to end of compressed data
+
+    /*
+     * Success!  Fill out new values.
+     */
+    pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
+        compressionMethod);
+    modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
+    pEntry->setModWhen(modWhen);
+    pEntry->setLFHOffset(lfhPosn);
+    mEOCD.mNumEntries++;
+    mEOCD.mTotalNumEntries++;
+    mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
+    mEOCD.mCentralDirOffset = endPosn;
+
+    /*
+     * Go back and write the LFH.
+     */
+    if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+    pEntry->mLFH.write(mZipFp);
+
+    /*
+     * Add pEntry to the list.
+     */
+    mEntries.add(pEntry);
+    if (ppEntry != NULL)
+        *ppEntry = pEntry;
+    pEntry = NULL;
+
+bail:
+    if (inputFp != NULL)
+        fclose(inputFp);
+    delete pEntry;
+    return result;
+}
+
+/*
+ * Add an entry by copying it from another zip file.  If "padding" is
+ * nonzero, the specified number of bytes will be added to the "extra"
+ * field in the header.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
+    int padding, ZipEntry** ppEntry)
+{
+    ZipEntry* pEntry = NULL;
+    status_t result;
+    long lfhPosn, endPosn;
+
+    if (mReadOnly)
+        return INVALID_OPERATION;
+
+    /* make sure we're in a reasonable state */
+    assert(mZipFp != NULL);
+    assert(mEntries.size() == mEOCD.mTotalNumEntries);
+
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    pEntry = new ZipEntry;
+    if (pEntry == NULL) {
+        result = NO_MEMORY;
+        goto bail;
+    }
+
+    result = pEntry->initFromExternal(pSourceZip, pSourceEntry);
+    if (result != NO_ERROR)
+        goto bail;
+    if (padding != 0) {
+        result = pEntry->addPadding(padding);
+        if (result != NO_ERROR)
+            goto bail;
+    }
+
+    /*
+     * From here on out, failures are more interesting.
+     */
+    mNeedCDRewrite = true;
+
+    /*
+     * Write the LFH.  Since we're not recompressing the data, we already
+     * have all of the fields filled out.
+     */
+    lfhPosn = ftell(mZipFp);
+    pEntry->mLFH.write(mZipFp);
+
+    /*
+     * Copy the data over.
+     *
+     * If the "has data descriptor" flag is set, we want to copy the DD
+     * fields as well.  This is a fixed-size area immediately following
+     * the data.
+     */
+    if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
+    {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    off_t copyLen;
+    copyLen = pSourceEntry->getCompressedLen();
+    if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
+        copyLen += ZipEntry::kDataDescriptorLen;
+
+    if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
+        != NO_ERROR)
+    {
+        LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /*
+     * Update file offsets.
+     */
+    endPosn = ftell(mZipFp);
+
+    /*
+     * Success!  Fill out new values.
+     */
+    pEntry->setLFHOffset(lfhPosn);      // sets mCDE.mLocalHeaderRelOffset
+    mEOCD.mNumEntries++;
+    mEOCD.mTotalNumEntries++;
+    mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
+    mEOCD.mCentralDirOffset = endPosn;
+
+    /*
+     * Add pEntry to the list.
+     */
+    mEntries.add(pEntry);
+    if (ppEntry != NULL)
+        *ppEntry = pEntry;
+    pEntry = NULL;
+
+    result = NO_ERROR;
+
+bail:
+    delete pEntry;
+    return result;
+}
+
+/*
+ * Copy all of the bytes in "src" to "dst".
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the data.
+ */
+status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32)
+{
+    unsigned char tmpBuf[32768];
+    size_t count;
+
+    *pCRC32 = crc32(0L, Z_NULL, 0);
+
+    while (1) {
+        count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
+        if (ferror(srcFp) || ferror(dstFp))
+            return errnoToStatus(errno);
+        if (count == 0)
+            break;
+
+        *pCRC32 = crc32(*pCRC32, tmpBuf, count);
+
+        if (fwrite(tmpBuf, 1, count, dstFp) != count) {
+            LOGD("fwrite %d bytes failed\n", (int) count);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Copy all of the bytes in "src" to "dst".
+ *
+ * On exit, "dstFp" will be seeked immediately past the data.
+ */
+status_t ZipFile::copyDataToFp(FILE* dstFp,
+    const void* data, size_t size, unsigned long* pCRC32)
+{
+    size_t count;
+
+    *pCRC32 = crc32(0L, Z_NULL, 0);
+    if (size > 0) {
+        *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
+        if (fwrite(data, 1, size, dstFp) != size) {
+            LOGD("fwrite %d bytes failed\n", (int) size);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Copy some of the bytes in "src" to "dst".
+ *
+ * If "pCRC32" is NULL, the CRC will not be computed.
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the data just written.
+ */
+status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
+    unsigned long* pCRC32)
+{
+    unsigned char tmpBuf[32768];
+    size_t count;
+
+    if (pCRC32 != NULL)
+        *pCRC32 = crc32(0L, Z_NULL, 0);
+
+    while (length) {
+        long readSize;
+        
+        readSize = sizeof(tmpBuf);
+        if (readSize > length)
+            readSize = length;
+
+        count = fread(tmpBuf, 1, readSize, srcFp);
+        if ((long) count != readSize) {     // error or unexpected EOF
+            LOGD("fread %d bytes failed\n", (int) readSize);
+            return UNKNOWN_ERROR;
+        }
+
+        if (pCRC32 != NULL)
+            *pCRC32 = crc32(*pCRC32, tmpBuf, count);
+
+        if (fwrite(tmpBuf, 1, count, dstFp) != count) {
+            LOGD("fwrite %d bytes failed\n", (int) count);
+            return UNKNOWN_ERROR;
+        }
+
+        length -= readSize;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Compress all of the data in "srcFp" and write it to "dstFp".
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the compressed data.
+ */
+status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
+    const void* data, size_t size, unsigned long* pCRC32)
+{
+    status_t result = NO_ERROR;
+    const size_t kBufSize = 32768;
+    unsigned char* inBuf = NULL;
+    unsigned char* outBuf = NULL;
+    z_stream zstream;
+    bool atEof = false;     // no feof() aviailable yet
+    unsigned long crc;
+    int zerr;
+
+    /*
+     * Create an input buffer and an output buffer.
+     */
+    inBuf = new unsigned char[kBufSize];
+    outBuf = new unsigned char[kBufSize];
+    if (inBuf == NULL || outBuf == NULL) {
+        result = NO_MEMORY;
+        goto bail;
+    }
+
+    /*
+     * Initialize the zlib stream.
+     */
+    memset(&zstream, 0, sizeof(zstream));
+    zstream.zalloc = Z_NULL;
+    zstream.zfree = Z_NULL;
+    zstream.opaque = Z_NULL;
+    zstream.next_in = NULL;
+    zstream.avail_in = 0;
+    zstream.next_out = outBuf;
+    zstream.avail_out = kBufSize;
+    zstream.data_type = Z_UNKNOWN;
+
+    zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION,
+        Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+    if (zerr != Z_OK) {
+        result = UNKNOWN_ERROR;
+        if (zerr == Z_VERSION_ERROR) {
+            LOGE("Installed zlib is not compatible with linked version (%s)\n",
+                ZLIB_VERSION);
+        } else {
+            LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
+        }
+        goto bail;
+    }
+
+    crc = crc32(0L, Z_NULL, 0);
+
+    /*
+     * Loop while we have data.
+     */
+    do {
+        size_t getSize;
+        int flush;
+
+        /* only read if the input buffer is empty */
+        if (zstream.avail_in == 0 && !atEof) {
+            LOGV("+++ reading %d bytes\n", (int)kBufSize);
+            if (data) {
+                getSize = size > kBufSize ? kBufSize : size;
+                memcpy(inBuf, data, getSize);
+                data = ((const char*)data) + getSize;
+                size -= getSize;
+            } else {
+                getSize = fread(inBuf, 1, kBufSize, srcFp);
+                if (ferror(srcFp)) {
+                    LOGD("deflate read failed (errno=%d)\n", errno);
+                    goto z_bail;
+                }
+            }
+            if (getSize < kBufSize) {
+                LOGV("+++  got %d bytes, EOF reached\n",
+                    (int)getSize);
+                atEof = true;
+            }
+
+            crc = crc32(crc, inBuf, getSize);
+
+            zstream.next_in = inBuf;
+            zstream.avail_in = getSize;
+        }
+
+        if (atEof)
+            flush = Z_FINISH;       /* tell zlib that we're done */
+        else
+            flush = Z_NO_FLUSH;     /* more to come! */
+
+        zerr = deflate(&zstream, flush);
+        if (zerr != Z_OK && zerr != Z_STREAM_END) {
+            LOGD("zlib deflate call failed (zerr=%d)\n", zerr);
+            result = UNKNOWN_ERROR;
+            goto z_bail;
+        }
+
+        /* write when we're full or when we're done */
+        if (zstream.avail_out == 0 ||
+            (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
+        {
+            LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
+            if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
+                (size_t)(zstream.next_out - outBuf))
+            {
+                LOGD("write %d failed in deflate\n",
+                    (int) (zstream.next_out - outBuf));
+                goto z_bail;
+            }
+
+            zstream.next_out = outBuf;
+            zstream.avail_out = kBufSize;
+        }
+    } while (zerr == Z_OK);
+
+    assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
+
+    *pCRC32 = crc;
+
+z_bail:
+    deflateEnd(&zstream);        /* free up any allocated structures */
+
+bail:
+    delete[] inBuf;
+    delete[] outBuf;
+
+    return result;
+}
+
+/*
+ * Mark an entry as deleted.
+ *
+ * We will eventually need to crunch the file down, but if several files
+ * are being removed (perhaps as part of an "update" process) we can make
+ * things considerably faster by deferring the removal to "flush" time.
+ */
+status_t ZipFile::remove(ZipEntry* pEntry)
+{
+    /*
+     * Should verify that pEntry is actually part of this archive, and
+     * not some stray ZipEntry from a different file.
+     */
+
+    /* mark entry as deleted, and mark archive as dirty */
+    pEntry->setDeleted();
+    mNeedCDRewrite = true;
+    return NO_ERROR;
+}
+
+/*
+ * Flush any pending writes.
+ *
+ * In particular, this will crunch out deleted entries, and write the
+ * Central Directory and EOCD if we have stomped on them.
+ */
+status_t ZipFile::flush(void)
+{
+    status_t result = NO_ERROR;
+    long eocdPosn;
+    int i, count;
+
+    if (mReadOnly)
+        return INVALID_OPERATION;
+    if (!mNeedCDRewrite)
+        return NO_ERROR;
+
+    assert(mZipFp != NULL);
+
+    result = crunchArchive();
+    if (result != NO_ERROR)
+        return result;
+
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0)
+        return UNKNOWN_ERROR;
+
+    count = mEntries.size();
+    for (i = 0; i < count; i++) {
+        ZipEntry* pEntry = mEntries[i];
+        pEntry->mCDE.write(mZipFp);
+    }
+
+    eocdPosn = ftell(mZipFp);
+    mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
+
+    mEOCD.write(mZipFp);
+
+    /*
+     * If we had some stuff bloat up during compression and get replaced
+     * with plain files, or if we deleted some entries, there's a lot
+     * of wasted space at the end of the file.  Remove it now.
+     */
+    if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
+        LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
+        // not fatal
+    }
+
+    /* should we clear the "newly added" flag in all entries now? */
+
+    mNeedCDRewrite = false;
+    return NO_ERROR;
+}
+
+/*
+ * Crunch deleted files out of an archive by shifting the later files down.
+ *
+ * Because we're not using a temp file, we do the operation inside the
+ * current file.
+ */
+status_t ZipFile::crunchArchive(void)
+{
+    status_t result = NO_ERROR;
+    int i, count;
+    long delCount, adjust;
+
+#if 0
+    printf("CONTENTS:\n");
+    for (i = 0; i < (int) mEntries.size(); i++) {
+        printf(" %d: lfhOff=%ld del=%d\n",
+            i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
+    }
+    printf("  END is %ld\n", (long) mEOCD.mCentralDirOffset);
+#endif
+
+    /*
+     * Roll through the set of files, shifting them as appropriate.  We
+     * could probably get a slight performance improvement by sliding
+     * multiple files down at once (because we could use larger reads
+     * when operating on batches of small files), but it's not that useful.
+     */
+    count = mEntries.size();
+    delCount = adjust = 0;
+    for (i = 0; i < count; i++) {
+        ZipEntry* pEntry = mEntries[i];
+        long span;
+
+        if (pEntry->getLFHOffset() != 0) {
+            long nextOffset;
+
+            /* Get the length of this entry by finding the offset
+             * of the next entry.  Directory entries don't have
+             * file offsets, so we need to find the next non-directory
+             * entry.
+             */
+            nextOffset = 0;
+            for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
+                nextOffset = mEntries[ii]->getLFHOffset();
+            if (nextOffset == 0)
+                nextOffset = mEOCD.mCentralDirOffset;
+            span = nextOffset - pEntry->getLFHOffset();
+
+            assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
+        } else {
+            /* This is a directory entry.  It doesn't have
+             * any actual file contents, so there's no need to
+             * move anything.
+             */
+            span = 0;
+        }
+
+        //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
+        //    i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
+
+        if (pEntry->getDeleted()) {
+            adjust += span;
+            delCount++;
+
+            delete pEntry;
+            mEntries.removeAt(i);
+
+            /* adjust loop control */
+            count--;
+            i--;
+        } else if (span != 0 && adjust > 0) {
+            /* shuffle this entry back */
+            //printf("+++ Shuffling '%s' back %ld\n",
+            //    pEntry->getFileName(), adjust);
+            result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
+                        pEntry->getLFHOffset(), span);
+            if (result != NO_ERROR) {
+                /* this is why you use a temp file */
+                LOGE("error during crunch - archive is toast\n");
+                return result;
+            }
+
+            pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
+        }
+    }
+
+    /*
+     * Fix EOCD info.  We have to wait until the end to do some of this
+     * because we use mCentralDirOffset to determine "span" for the
+     * last entry.
+     */
+    mEOCD.mCentralDirOffset -= adjust;
+    mEOCD.mNumEntries -= delCount;
+    mEOCD.mTotalNumEntries -= delCount;
+    mEOCD.mCentralDirSize = 0;  // mark invalid; set by flush()
+
+    assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
+    assert(mEOCD.mNumEntries == count);
+
+    return result;
+}
+
+/*
+ * Works like memmove(), but on pieces of a file.
+ */
+status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
+{
+    if (dst == src || n <= 0)
+        return NO_ERROR;
+
+    unsigned char readBuf[32768];
+
+    if (dst < src) {
+        /* shift stuff toward start of file; must read from start */
+        while (n != 0) {
+            size_t getSize = sizeof(readBuf);
+            if (getSize > n)
+                getSize = n;
+
+            if (fseek(fp, (long) src, SEEK_SET) != 0) {
+                LOGD("filemove src seek %ld failed\n", (long) src);
+                return UNKNOWN_ERROR;
+            }
+
+            if (fread(readBuf, 1, getSize, fp) != getSize) {
+                LOGD("filemove read %ld off=%ld failed\n",
+                    (long) getSize, (long) src);
+                return UNKNOWN_ERROR;
+            }
+
+            if (fseek(fp, (long) dst, SEEK_SET) != 0) {
+                LOGD("filemove dst seek %ld failed\n", (long) dst);
+                return UNKNOWN_ERROR;
+            }
+
+            if (fwrite(readBuf, 1, getSize, fp) != getSize) {
+                LOGD("filemove write %ld off=%ld failed\n",
+                    (long) getSize, (long) dst);
+                return UNKNOWN_ERROR;
+            }
+
+            src += getSize;
+            dst += getSize;
+            n -= getSize;
+        }
+    } else {
+        /* shift stuff toward end of file; must read from end */
+        assert(false);      // write this someday, maybe
+        return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+
+/*
+ * Get the modification time from a file descriptor.
+ */
+time_t ZipFile::getModTime(int fd)
+{
+    struct stat sb;
+
+    if (fstat(fd, &sb) < 0) {
+        LOGD("HEY: fstat on fd %d failed\n", fd);
+        return (time_t) -1;
+    }
+
+    return sb.st_mtime;
+}
+
+
+#if 0       /* this is a bad idea */
+/*
+ * Get a copy of the Zip file descriptor.
+ *
+ * We don't allow this if the file was opened read-write because we tend
+ * to leave the file contents in an uncertain state between calls to
+ * flush().  The duplicated file descriptor should only be valid for reads.
+ */
+int ZipFile::getZipFd(void) const
+{
+    if (!mReadOnly)
+        return INVALID_OPERATION;
+    assert(mZipFp != NULL);
+
+    int fd;
+    fd = dup(fileno(mZipFp));
+    if (fd < 0) {
+        LOGD("didn't work, errno=%d\n", errno);
+    }
+
+    return fd;
+}
+#endif
+
+
+#if 0
+/*
+ * Expand data.
+ */
+bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
+{
+    return false;
+}
+#endif
+
+// free the memory when you're done
+void* ZipFile::uncompress(const ZipEntry* entry)
+{
+    size_t unlen = entry->getUncompressedLen();
+    size_t clen = entry->getCompressedLen();
+
+    void* buf = malloc(unlen);
+    if (buf == NULL) {
+        return NULL;
+    }
+
+    fseek(mZipFp, 0, SEEK_SET);
+
+    off_t offset = entry->getFileOffset();
+    if (fseek(mZipFp, offset, SEEK_SET) != 0) {
+        goto bail;
+    }
+
+    switch (entry->getCompressionMethod())
+    {
+        case ZipEntry::kCompressStored: {
+            ssize_t amt = fread(buf, 1, unlen, mZipFp);
+            if (amt != (ssize_t)unlen) {
+                goto bail;
+            }
+#if 0
+            printf("data...\n");
+            const unsigned char* p = (unsigned char*)buf;
+            const unsigned char* end = p+unlen;
+            for (int i=0; i<32 && p < end; i++) {
+                printf("0x%08x ", (int)(offset+(i*0x10)));
+                for (int j=0; j<0x10 && p < end; j++) {
+                    printf(" %02x", *p);
+                    p++;
+                }
+                printf("\n");
+            }
+#endif
+
+            }
+            break;
+        case ZipEntry::kCompressDeflated: {
+            if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) {
+                goto bail;
+            }
+            }
+            break;
+        default:
+            goto bail;
+    }
+    return buf;
+
+bail:
+    free(buf);
+    return NULL;
+}
+
+
+/*
+ * ===========================================================================
+ *      ZipFile::EndOfCentralDir
+ * ===========================================================================
+ */
+
+/*
+ * Read the end-of-central-dir fields.
+ *
+ * "buf" should be positioned at the EOCD signature, and should contain
+ * the entire EOCD area including the comment.
+ */
+status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len)
+{
+    /* don't allow re-use */
+    assert(mComment == NULL);
+
+    if (len < kEOCDLen) {
+        /* looks like ZIP file got truncated */
+        LOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
+            kEOCDLen, len);
+        return INVALID_OPERATION;
+    }
+
+    /* this should probably be an assert() */
+    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
+        return UNKNOWN_ERROR;
+
+    mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
+    mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
+    mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
+    mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
+    mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
+    mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
+    mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
+
+    // TODO: validate mCentralDirOffset
+
+    if (mCommentLen > 0) {
+        if (kEOCDLen + mCommentLen > len) {
+            LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
+                kEOCDLen, mCommentLen, len);
+            return UNKNOWN_ERROR;
+        }
+        mComment = new unsigned char[mCommentLen];
+        memcpy(mComment, buf + kEOCDLen, mCommentLen);
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Write an end-of-central-directory section.
+ */
+status_t ZipFile::EndOfCentralDir::write(FILE* fp)
+{
+    unsigned char buf[kEOCDLen];
+
+    ZipEntry::putLongLE(&buf[0x00], kSignature);
+    ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
+    ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
+    ZipEntry::putShortLE(&buf[0x08], mNumEntries);
+    ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
+    ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
+    ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
+    ZipEntry::putShortLE(&buf[0x14], mCommentLen);
+
+    if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen)
+        return UNKNOWN_ERROR;
+    if (mCommentLen > 0) {
+        assert(mComment != NULL);
+        if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen)
+            return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Dump the contents of an EndOfCentralDir object.
+ */
+void ZipFile::EndOfCentralDir::dump(void) const
+{
+    LOGD(" EndOfCentralDir contents:\n");
+    LOGD("  diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
+        mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
+    LOGD("  centDirSize=%lu centDirOff=%lu commentLen=%u\n",
+        mCentralDirSize, mCentralDirOffset, mCommentLen);
+}
+
diff --git a/tools/zipalign/ZipFile.h b/tools/zipalign/ZipFile.h
new file mode 100644
index 0000000..dbbd072
--- /dev/null
+++ b/tools/zipalign/ZipFile.h
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+//
+// General-purpose Zip archive access.  This class allows both reading and
+// writing to Zip archives, including deletion of existing entries.
+//
+#ifndef __LIBS_ZIPFILE_H
+#define __LIBS_ZIPFILE_H
+
+#include <utils/Vector.h>
+#include <utils/Errors.h>
+#include <stdio.h>
+
+#include "ZipEntry.h"
+
+namespace android {
+
+/*
+ * Manipulate a Zip archive.
+ *
+ * Some changes will not be visible in the until until "flush" is called.
+ *
+ * The correct way to update a file archive is to make all changes to a
+ * copy of the archive in a temporary file, and then unlink/rename over
+ * the original after everything completes.  Because we're only interested
+ * in using this for packaging, we don't worry about such things.  Crashing
+ * after making changes and before flush() completes could leave us with
+ * an unusable Zip archive.
+ */
+class ZipFile {
+public:
+    ZipFile(void)
+      : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
+      {}
+    ~ZipFile(void) {
+        if (!mReadOnly)
+            flush();
+        if (mZipFp != NULL)
+            fclose(mZipFp);
+        discardEntries();
+    }
+
+    /*
+     * Open a new or existing archive.
+     */
+    typedef enum {
+        kOpenReadOnly   = 0x01,
+        kOpenReadWrite  = 0x02,
+        kOpenCreate     = 0x04,     // create if it doesn't exist
+        kOpenTruncate   = 0x08,     // if it exists, empty it
+    };
+    status_t open(const char* zipFileName, int flags);
+
+    /*
+     * Add a file to the end of the archive.  Specify whether you want the
+     * library to try to store it compressed.
+     *
+     * If "storageName" is specified, the archive will use that instead
+     * of "fileName".
+     *
+     * If there is already an entry with the same name, the call fails.
+     * Existing entries with the same name must be removed first.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t add(const char* fileName, int compressionMethod,
+        ZipEntry** ppEntry)
+    {
+        return add(fileName, fileName, compressionMethod, ppEntry);
+    }
+    status_t add(const char* fileName, const char* storageName,
+        int compressionMethod, ZipEntry** ppEntry)
+    {
+        return addCommon(fileName, NULL, 0, storageName,
+                         ZipEntry::kCompressStored,
+                         compressionMethod, ppEntry);
+    }
+
+    /*
+     * Add a file that is already compressed with gzip.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t addGzip(const char* fileName, const char* storageName,
+        ZipEntry** ppEntry)
+    {
+        return addCommon(fileName, NULL, 0, storageName,
+                         ZipEntry::kCompressDeflated,
+                         ZipEntry::kCompressDeflated, ppEntry);
+    }
+
+    /*
+     * Add a file from an in-memory data buffer.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t add(const void* data, size_t size, const char* storageName,
+        int compressionMethod, ZipEntry** ppEntry)
+    {
+        return addCommon(NULL, data, size, storageName,
+                         ZipEntry::kCompressStored,
+                         compressionMethod, ppEntry);
+    }
+
+    /*
+     * Add an entry by copying it from another zip file.  If "padding" is
+     * nonzero, the specified number of bytes will be added to the "extra"
+     * field in the header.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
+        int padding, ZipEntry** ppEntry);
+
+    /*
+     * Mark an entry as having been removed.  It is not actually deleted
+     * from the archive or our internal data structures until flush() is
+     * called.
+     */
+    status_t remove(ZipEntry* pEntry);
+
+    /*
+     * Flush changes.  If mNeedCDRewrite is set, this writes the central dir.
+     */
+    status_t flush(void);
+
+    /*
+     * Expand the data into the buffer provided.  The buffer must hold
+     * at least <uncompressed len> bytes.  Variation expands directly
+     * to a file.
+     *
+     * Returns "false" if an error was encountered in the compressed data.
+     */
+    //bool uncompress(const ZipEntry* pEntry, void* buf) const;
+    //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
+    void* uncompress(const ZipEntry* pEntry);
+
+    /*
+     * Get an entry, by name.  Returns NULL if not found.
+     *
+     * Does not return entries pending deletion.
+     */
+    ZipEntry* getEntryByName(const char* fileName) const;
+
+    /*
+     * Get the Nth entry in the archive.
+     *
+     * This will return an entry that is pending deletion.
+     */
+    int getNumEntries(void) const { return mEntries.size(); }
+    ZipEntry* getEntryByIndex(int idx) const;
+
+private:
+    /* these are private and not defined */
+    ZipFile(const ZipFile& src);
+    ZipFile& operator=(const ZipFile& src);
+
+    class EndOfCentralDir {
+    public:
+        EndOfCentralDir(void) :
+            mDiskNumber(0),
+            mDiskWithCentralDir(0),
+            mNumEntries(0),
+            mTotalNumEntries(0),
+            mCentralDirSize(0),
+            mCentralDirOffset(0),
+            mCommentLen(0),
+            mComment(NULL)
+            {}
+        virtual ~EndOfCentralDir(void) {
+            delete[] mComment;
+        }
+
+        status_t readBuf(const unsigned char* buf, int len);
+        status_t write(FILE* fp);
+
+        //unsigned long   mSignature;
+        unsigned short  mDiskNumber;
+        unsigned short  mDiskWithCentralDir;
+        unsigned short  mNumEntries;
+        unsigned short  mTotalNumEntries;
+        unsigned long   mCentralDirSize;
+        unsigned long   mCentralDirOffset;      // offset from first disk
+        unsigned short  mCommentLen;
+        unsigned char*  mComment;
+
+        enum {
+            kSignature      = 0x06054b50,
+            kEOCDLen        = 22,       // EndOfCentralDir len, excl. comment
+
+            kMaxCommentLen  = 65535,    // longest possible in ushort
+            kMaxEOCDSearch  = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
+
+        };
+
+        void dump(void) const;
+    };
+
+
+    /* read all entries in the central dir */
+    status_t readCentralDir(void);
+
+    /* crunch deleted entries out */
+    status_t crunchArchive(void);
+
+    /* clean up mEntries */
+    void discardEntries(void);
+
+    /* common handler for all "add" functions */
+    status_t addCommon(const char* fileName, const void* data, size_t size,
+        const char* storageName, int sourceType, int compressionMethod,
+        ZipEntry** ppEntry);
+
+    /* copy all of "srcFp" into "dstFp" */
+    status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32);
+    /* copy all of "data" into "dstFp" */
+    status_t copyDataToFp(FILE* dstFp,
+        const void* data, size_t size, unsigned long* pCRC32);
+    /* copy some of "srcFp" into "dstFp" */
+    status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
+        unsigned long* pCRC32);
+    /* like memmove(), but on parts of a single file */
+    status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
+    /* compress all of "srcFp" into "dstFp", using Deflate */
+    status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
+        const void* data, size_t size, unsigned long* pCRC32);
+
+    /* get modification date from a file descriptor */
+    time_t getModTime(int fd);
+
+    /*
+     * We use stdio FILE*, which gives us buffering but makes dealing
+     * with files >2GB awkward.  Until we support Zip64, we're fine.
+     */
+    FILE*           mZipFp;             // Zip file pointer
+
+    /* one of these per file */
+    EndOfCentralDir mEOCD;
+
+    /* did we open this read-only? */
+    bool            mReadOnly;
+
+    /* set this when we trash the central dir */
+    bool            mNeedCDRewrite;
+
+    /*
+     * One ZipEntry per entry in the zip file.  I'm using pointers instead
+     * of objects because it's easier than making operator= work for the
+     * classes and sub-classes.
+     */
+    Vector<ZipEntry*>   mEntries;
+};
+
+}; // namespace android
+
+#endif // __LIBS_ZIPFILE_H