merge from open-source master
diff --git a/buildspec.mk.default b/buildspec.mk.default
index 861bb0d..6fd93da 100644
--- a/buildspec.mk.default
+++ b/buildspec.mk.default
@@ -88,10 +88,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..26f8e0c 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,25 @@
 #$(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 -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 $(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)/obj/SHARED_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.google.android.datamessaging_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sholes/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/pvasflocal.cfg)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sholes)
+$(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 $(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)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/passion/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.google.android.datamessaging_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libcrypto_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libssl_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/openssl_intermediates)
+
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/core/Makefile b/core/Makefile
index 9247c75..307a217 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)" \
@@ -842,6 +843,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 +860,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)
@@ -904,9 +909,24 @@
 
 $(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
 
+ifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),)
+# default to common dir for device vendor
+$(INTERNAL_OTA_PACKAGE_TARGET): extensions := $(TARGET_DEVICE_DIR)/../common
+else
+$(INTERNAL_OTA_PACKAGE_TARGET): extensions := $(TARGET_RELEASETOOLS_EXTENSIONS)
+endif
+
+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 \
+	   -s $(extensions) -m $(scriptmode) \
 	   -p $(HOST_OUT) \
            -k $(KEY_CERT_PAIR) \
            $(BUILT_TARGET_FILES_PACKAGE) $@
@@ -1030,9 +1050,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..82c7dec 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -350,7 +350,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/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..ec4c3ac 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 -Wctor-dtor-privacy 
+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
@@ -259,10 +291,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 c84cbd8..b4e4b50 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -1575,6 +1575,25 @@
  )
 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..0ee7cfc 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -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/main.mk b/core/main.mk
index 71bbc08..482fefd 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)
@@ -245,6 +255,16 @@
 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.
 ifeq (,$(filter %:system/etc/apns-conf.xml, $(PRODUCT_COPY_FILES)))
   PRODUCT_COPY_FILES += \
@@ -253,18 +273,10 @@
     $(warning implicitly installing apns-conf_sdk.xml)
   endif
 endif
-# Install a vold.conf file is one's not already being installed.
-ifeq (,$(filter %:system/etc/vold.conf, $(PRODUCT_COPY_FILES)))
-  PRODUCT_COPY_FILES += \
-	development/data/etc/vold.conf:system/etc/vold.conf
-  ifeq ($(filter eng tests,$(TARGET_BUILD_VARIANT)),)
-    $(warning implicitly installing vold.conf)
-  endif
-endif
 # 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 := \
@@ -325,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
@@ -398,8 +408,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 \
@@ -418,7 +426,6 @@
 #
 # Typical build; include any Android.mk files we can find.
 #
-INTERNAL_DEFAULT_DOCS_TARGETS := offline-sdk-docs
 subdirs := $(TOP)
 
 FULL_BUILD := true
@@ -427,41 +434,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="./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.
@@ -473,14 +445,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.sh --prune="./out" $(subdirs) Android.mk)
+
 include $(subdir_makefiles)
-endif
+endif # ONE_SHOT_MAKEFILE
+
 # -------------------------------------------------------------------
 # All module makefiles have been included at this point.
 # -------------------------------------------------------------------
@@ -681,7 +664,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/pathmap.mk b/core/pathmap.mk
index e281b9d..992e3ef 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 \
diff --git a/core/prelink-linux-arm.map b/core/prelink-linux-arm.map
index c8e0db5..bd86397 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,8 @@
 
 # graphics
 libpixelflinger.so      0xACF00000
-libcorecg.so            0xACE00000
 libsurfaceflinger.so    0xACD00000
+libGLES_android.so      0xACC80000
 libagl.so               0xACC00000
 
 libGLESv1_CM.so         0xACB00000
@@ -64,13 +64,18 @@
 
 libexif.so              0xAC500000
 libui.so                0xAC400000
-libsgl.so               0xAC000000
+libskia.so              0xAC000000
+librs_jni.so            0xABF00000
+libRS.so                0xAB900000
+
 
 # audio
-libspeech.so            0xAB800000
+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
@@ -82,6 +87,7 @@
 libsqlite.so            0xAAC00000
 libexpat.so             0xAAB00000
 libwebcore.so           0xAA000000
+libbinder.so            0xA9D80000
 libutils.so             0xA9D00000
 libcameraservice.so     0xA9C80000
 libhardware.so          0xA9C70000
@@ -91,13 +97,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
@@ -136,3 +158,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..3237801 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -214,6 +214,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/tasks/product-graph.mk b/core/tasks/product-graph.mk
index ead1406..5dc13a6 100644
--- a/core/tasks/product-graph.mk
+++ b/core/tasks/product-graph.mk
@@ -25,6 +25,7 @@
 $(products_pdf):
 	$(hide) ( \
 		echo 'digraph {'; \
+		echo 'graph [ ratio = .6, pack=false];'; \
 		$(foreach p,$(ALL_PRODUCTS), \
 			$(foreach d,$(PRODUCTS.$(strip $(p)).INHERITS_FROM), \
 			echo \"$(d)\" -\> \"$(p)\";)) \
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index ca8487f..bbcbb51 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 := Donut
+  PLATFORM_VERSION := Eclair
 endif
 
 ifeq "" "$(PLATFORM_SDK_VERSION)"
@@ -57,9 +57,9 @@
 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 := Donut
+  # 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 := Eclair
 endif
 
 ifeq "" "$(DEFAULT_APP_TARGET_SDK)"
diff --git a/envsetup.sh b/envsetup.sh
index 6325c3d..9eeab02 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"
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 204345e..79372a1 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=F1_New_SMS.ogg \
+    ro.config.alarm_alert=Alarm_Classic.ogg
 
 PRODUCT_PACKAGES := \
     framework-res \
@@ -26,4 +27,3 @@
     PackageInstaller \
     WebSearchProvider \
     Bugreport
-
diff --git a/target/product/generic.mk b/target/product/generic.mk
index b9bc070..dc48702 100644
--- a/target/product/generic.mk
+++ b/target/product/generic.mk
@@ -5,9 +5,12 @@
 PRODUCT_PACKAGES := \
     AlarmClock \
     AlarmProvider \
+    Bluetooth \
+    Calculator \
     Calendar \
     Camera \
     DrmProvider \
+    Email \
     LatinIME \
     Mms \
     Music \
diff --git a/target/product/generic_with_google.mk b/target/product/generic_with_google.mk
index dddbbb7..d691e99 100644
--- a/target/product/generic_with_google.mk
+++ b/target/product/generic_with_google.mk
@@ -5,12 +5,15 @@
 
 PRODUCT_PACKAGES := \
     GoogleContactsProvider \
+    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 005af70..056c2b8 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=F1_New_SMS.ogg \
+    ro.config.alarm_alert=Alarm_Classic.ogg
 PRODUCT_BRAND := generic
 PRODUCT_NAME := min_dev
 PRODUCT_DEVICE := generic
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/droiddoc/src/ClassInfo.java b/tools/droiddoc/src/ClassInfo.java
index 869142e..5c61941 100644
--- a/tools/droiddoc/src/ClassInfo.java
+++ b/tools/droiddoc/src/ClassInfo.java
@@ -863,6 +863,7 @@
         data.setValue(base + ".kind", this.kind());
         TagInfo.makeHDF(data, base + ".shortDescr", this.firstSentenceTags());
         TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
+        data.setValue(base + ".since", getSince());
     }
 
     /**
diff --git a/tools/droiddoc/src/DroidDoc.java b/tools/droiddoc/src/DroidDoc.java
index a8b9c73..39f9393 100644
--- a/tools/droiddoc/src/DroidDoc.java
+++ b/tools/droiddoc/src/DroidDoc.java
@@ -56,6 +56,7 @@
     public static ArrayList<String[]> mHDFData = new ArrayList<String[]>();
     public static Map<Character,String> escapeChars = new HashMap<Character,String>();
     public static String title = "";
+    public static SinceTagger sinceTagger = new SinceTagger();
 
     public static boolean checkLevel(int level)
     {
@@ -94,10 +95,10 @@
         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;
-        SinceTagger sinceTagger = new SinceTagger();
 
         root = r;
 
@@ -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;
         }
@@ -518,6 +533,7 @@
             i++;
         }
 
+        sinceTagger.writeVersionNames(data);
         return data;
     }
 
diff --git a/tools/droiddoc/src/NavTree.java b/tools/droiddoc/src/NavTree.java
index 9eef0ce..0469fdc 100644
--- a/tools/droiddoc/src/NavTree.java
+++ b/tools/droiddoc/src/NavTree.java
@@ -25,7 +25,7 @@
         for (PackageInfo pkg: DroidDoc.choosePackages()) {
             children.add(makePackageNode(pkg));
         }
-        Node node = new Node("Reference", dir + "packages.html", children);
+        Node node = new Node("Reference", dir + "packages.html", children, null);
 
         StringBuilder buf = new StringBuilder();
         if (false) {
@@ -46,7 +46,7 @@
     private static Node makePackageNode(PackageInfo pkg) {
         ArrayList<Node> children = new ArrayList();
 
-        children.add(new Node("Description", pkg.fullDescriptionHtmlPage(), null));
+        children.add(new Node("Description", pkg.fullDescriptionHtmlPage(), null, null));
 
         addClassNodes(children, "Interfaces", pkg.interfaces());
         addClassNodes(children, "Classes", pkg.ordinaryClasses());
@@ -54,7 +54,7 @@
         addClassNodes(children, "Exceptions", pkg.exceptions());
         addClassNodes(children, "Errors", pkg.errors());
 
-        return new Node(pkg.name(), pkg.htmlPage(), children);
+        return new Node(pkg.name(), pkg.htmlPage(), children, pkg.getSince());
     }
 
     private static void addClassNodes(ArrayList<Node> parent, String label, ClassInfo[] classes) {
@@ -62,12 +62,12 @@
 
         for (ClassInfo cl: classes) {
             if (cl.checkLevel()) {
-                children.add(new Node(cl.name(), cl.htmlPage(), null));
+                children.add(new Node(cl.name(), cl.htmlPage(), null, cl.getSince()));
             }
         }
 
         if (children.size() > 0) {
-            parent.add(new Node(label, null, children));
+            parent.add(new Node(label, null, children, null));
         }
     }
 
@@ -75,11 +75,13 @@
         private String mLabel;
         private String mLink;
         ArrayList<Node> mChildren;
+        private String mSince;
 
-        Node(String label, String link, ArrayList<Node> children) {
+        Node(String label, String link, ArrayList<Node> children, String since) {
             mLabel = label;
             mLink = link;
             mChildren = children;
+            mSince = since;
         }
 
         static void renderString(StringBuilder buf, String s) {
@@ -136,6 +138,8 @@
             renderString(buf, mLink);
             buf.append(", ");
             renderChildren(buf);
+            buf.append(", ");
+            renderString(buf, mSince);
             buf.append(" ]");
         }
     }
diff --git a/tools/droiddoc/src/PackageInfo.java b/tools/droiddoc/src/PackageInfo.java
index bcf3cf3..18c636e 100644
--- a/tools/droiddoc/src/PackageInfo.java
+++ b/tools/droiddoc/src/PackageInfo.java
@@ -123,6 +123,7 @@
         ClassInfo.makeLinkListHDF(data, base + ".enums", enums());
         ClassInfo.makeLinkListHDF(data, base + ".exceptions", exceptions());
         ClassInfo.makeLinkListHDF(data, base + ".errors", errors());
+        data.setValue(base + ".since", getSince());
     }
 
     public ClassInfo[] interfaces()
diff --git a/tools/droiddoc/src/SinceTagger.java b/tools/droiddoc/src/SinceTagger.java
index a34814c..fb69c04 100644
--- a/tools/droiddoc/src/SinceTagger.java
+++ b/tools/droiddoc/src/SinceTagger.java
@@ -4,6 +4,8 @@
 
 import java.util.*;
 
+import org.clearsilver.HDF;
+
 /**
  * Applies version information to the DroidDoc class model from apicheck XML
  * files. Sample usage:
@@ -46,6 +48,17 @@
     }
 
     /**
+     * Writes an index of the version names to {@code data}. 
+     */
+    public void writeVersionNames(HDF data) {
+        int index = 1;
+        for (String version : xmlToName.values()) {
+            data.setValue("since." + index + ".name", version);
+            index++;
+        }
+    }
+
+    /**
      * Applies the version information to {@code classDocs} where not already
      * present.
      *
diff --git a/tools/droiddoc/templates-sdk/customization.cs b/tools/droiddoc/templates-sdk/customization.cs
index 79fc282..6bdb992 100644
--- a/tools/droiddoc/templates-sdk/customization.cs
+++ b/tools/droiddoc/templates-sdk/customization.cs
@@ -47,34 +47,51 @@
       <div id="headerRight">
           <div id="headerLinks">
           <?cs if:template.showLanguageMenu ?>
-              <img src="<?cs var:toroot ?>assets/images/icon_world.jpg" alt="" /> 
-              <span id="language">
+            <img src="<?cs var:toroot ?>assets/images/icon_world.jpg" alt="Language:" /> 
+            <span id="language">
              	<select name="language" onChange="changeLangPref(this.value, true)">
-    			<option value="en">English&nbsp;&nbsp;&nbsp;</option>
-    			<option value="ja">日本語</option>
-    			<?cs # 
-			<option value="de">Deutsch</option> 
-    			<option value="es">Español</option>
-    			<option value="fr">Français</option>
-    			<option value="it">Italiano</option>
-    			<option value="zh-CN">中文 (简体)</option>
-    			<option value="zh-TW">中文 (繁體)</option>
-			?>
+          			<option value="en">English&nbsp;&nbsp;&nbsp;</option>
+          			<option value="ja">日本語</option>
+          			<?cs # 
+      			    <option value="de">Deutsch</option> 
+          			<option value="es">Español</option>
+          			<option value="fr">Français</option>
+          			<option value="it">Italiano</option>
+          			<option value="zh-CN">中文 (简体)</option>
+          			<option value="zh-TW">中文 (繁體)</option>
+      			    ?>
              	</select>	
              	<script type="text/javascript">
              	  <!--  
                   loadLangPref();  
              	   //-->
              	</script>
-             </span>
+            </span>
           <?cs /if ?>
           <a href="http://www.android.com">Android.com</a>
           </div><?cs 
-          call:default_search_box() ?>
+          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() -->
+    			    </select>
+    			  </div>
+    	 	    <script>
+              var SINCE_DATA = [ <?cs 
+                each:since = since ?>'<?cs 
+                  var:since.name ?>'<?cs 
+                  if:!last(since) ?>, <?cs /if ?><?cs
+                /each 
+              ?> ];
+              buildApiLevelToggle();
+            </script><?cs 
+    			/if ?>
       </div><!-- headerRight -->
       <script type="text/javascript">
         <!--  
-        changeTabLang(getLangPref());  
+        changeTabLang(getLangPref());
         //-->
       </script>
   </div><!-- header --><?cs 
diff --git a/tools/droiddoc/templates-sdk/header_tabs.cs b/tools/droiddoc/templates-sdk/header_tabs.cs
index 02fc8bf..2a897cb 100644
--- a/tools/droiddoc/templates-sdk/header_tabs.cs
+++ b/tools/droiddoc/templates-sdk/header_tabs.cs
@@ -58,8 +58,16 @@
 	<?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="ja">ビデオ</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>
+	<?cs /if ?>
 	</a></li>
 	<li id="community-link"><a href="<?cs var:toroot ?>community/index.html">
 	<?cs if:!sdk.redirect ?>
diff --git a/tools/droiddoc/templates-sdk/sdkpage.cs b/tools/droiddoc/templates-sdk/sdkpage.cs
index d5c9ffc..0f3a30a 100644
--- a/tools/droiddoc/templates-sdk/sdkpage.cs
+++ b/tools/droiddoc/templates-sdk/sdkpage.cs
@@ -6,7 +6,7 @@
     <title>Redirecting...</title>
     <meta http-equiv="refresh" content="0;url=<?cs var:toroot ?>sdk/<?cs 
       if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?>"<?cs 
-      else ?><?cs var:sdk.current ?>/index.html<?cs /if ?>"
+      else ?><?cs var:sdk.current ?>/index.html<?cs /if ?>">
     <link href="<?cs var:toroot ?>assets/android-developer-docs.css" rel="stylesheet" type="text/css" />
   </head>
 <?cs else ?>
@@ -24,8 +24,10 @@
   <div id="jd-content">
     <p>Redirecting to 
     <a href="<?cs var:toroot ?>sdk/<?cs 
-      if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?>">/sdk/<?cs var:sdk.redirect.path ?><?cs 
-      else ?><?cs var:sdk.current ?>/index.html">/sdk/<?cs var:sdk.current ?>/index.html<?cs /if ?>
+      if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?><?cs 
+      else ?><?cs var:sdk.current ?>/index.html<?cs /if ?>">sdk/<?cs 
+      if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?><?cs 
+      else ?><?cs var:sdk.current ?>/index.html<?cs /if ?>
     </a> ...</p>
 <?cs else ?>
 <div class="g-unit" id="doc-content" >
@@ -50,7 +52,7 @@
   </div>
 <?cs /if ?>
 
-<?cs if:android.whichdoc != "online" ?>
+<?cs if:android.whichdoc != "online" && !android.preview ?>
 
 <p>The sections below provide an overview of the SDK package. </p>
 
@@ -101,7 +103,20 @@
   </tr>
   </table>
 
-  <?cs else ?>
+  <?cs else ?><?cs if:android.whichdoc == "online" ?>
+
+  <?cs if: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>
+
+  <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>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>
@@ -137,9 +152,32 @@
     <td><?cs var:sdk.linux_bytes ?> bytes</td>
     <td><?cs var:sdk.linux_checksum ?></td>
   </tr>
+  <?cs if:adt.zip_download ?>
+  <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>
+    </td>
+    <td><?cs var:adt.zip_bytes ?> bytes</td>
+    <td><?cs var:adt.zip_checksum ?></td>
+  </tr>
+  <?cs /if ?>
   </table>
 
-<?cs /if ?>
+  <?cs /if ?>
+ <?cs /if ?>
+<?cs /if ?> 
+
+<?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>
+
+  <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>
 <?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 3bd7060..447eace 100644
--- a/tools/droiddoc/templates/assets/android-developer-core.css
+++ b/tools/droiddoc/templates/assets/android-developer-core.css
@@ -49,6 +49,8 @@
 
 input, select,
 textarea, option {
+  font-family:inherit;
+  font-size:inherit;
   padding:0;
   margin:0;
 }
@@ -177,6 +179,8 @@
 
 /* LAYOUT */
 #body-content {
+  /* "Preliminary" watermark for preview releases and interim builds. */
+  background:transparent url(images/preliminary.png) repeat scroll 0 0;
   margin:0;
   position:relative;
   width:100%;
@@ -777,7 +781,7 @@
 }
 
 #carouselMain {
-	background: url('/assets/images/home/bg_home_carousel_board.png') 0 0 no-repeat;
+	background: url(images/home/bg_home_carousel_board.png) 0 0 no-repeat;
 	height:auto;
   padding: 25px 21px 0;
   overflow:hidden;
@@ -800,7 +804,7 @@
 }
 
 #carouselWheel {
-	background: url('/assets/images/home/bg_home_carousel_wheel.png') 0 0 no-repeat;
+	background: url(images/home/bg_home_carousel_wheel.png) 0 0 no-repeat;
 	padding-top:40px;
 	height:150px;
 }
@@ -1154,12 +1158,12 @@
 
 ul.videoPreviews span.more {
   padding:0 0 0 12px;
-  background:url('/assets/images/arrow_bluelink_down.png') 0 2px no-repeat;
+  background:url(images/arrow_bluelink_down.png) 0 2px no-repeat;
 }
 
 ul.videoPreviews span.less {
   padding:0 0 0 12px;
-  background:url('/assets/images/arrow_bluelink_up.png') 0 2px no-repeat;
+  background:url(images/arrow_bluelink_up.png) 0 2px no-repeat;
   display:none;
 }
 
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.css b/tools/droiddoc/templates/assets/android-developer-docs.css
index e5489e6..726694d 100644
--- a/tools/droiddoc/templates/assets/android-developer-docs.css
+++ b/tools/droiddoc/templates/assets/android-developer-docs.css
@@ -276,9 +276,9 @@
 
 /* summary tables for reference pages */
 .jd-sumtable {
-margin: .5em 1em 1em 1em;
-width:99%;
-font-size:.9em;
+  margin: .5em 1em 1em 1em;
+  width:95%; /* consistent table widths; within IE's quirks */
+  font-size:.9em;
 }
 
 .jd-sumtable a {
@@ -330,8 +330,7 @@
 links to summary tables) */
 #api-info-block {
   font-size:.8em;
-  margin:0;
-  padding:6px;
+  padding:6px 10px;
   font-weight:normal;
   float:right;
   text-align:right;
@@ -346,20 +345,56 @@
   color:#999;
 }
 
-h4.jd-details-title .api-level,
-div#jd-header .api-level { 
-  font-size:12px;
+div.api-level { 
+  font-size:.8em;
   font-weight:normal;
   color:#999;
-  position:absolute;
-  top:5px;
-  right:5px;
+  float:right;
+  padding:0 7px 0;
+  margin-top:-25px;
 }
 
-div#jd-header .api-level { 
-  position:relative;
-  float:right;
-  margin-top:-1.7em;
+#api-info-block div.api-level { 
+  font-size:1.3em;
+  font-weight:bold;
+  float:none;
+  color:#444;
+  padding:0;
+  margin:0;
+}
+
+/* Force link colors for IE6 */
+div.api-level a {
+  color:#999;
+}
+#api-info-block div.api-level a:link {
+  color:#444;
+}
+#api-level-toggle a {
+  color:#999;
+}
+
+div#naMessage {
+  display:none;
+  width:555px;
+  height:0;
+  margin:0 auto;
+}
+
+div#naMessage div {
+  width:450px;
+  position:fixed;
+  margin:50px 0;
+  padding:4em 4em 3em;
+  background:#FFF;
+  background:rgba(255,255,255,0.7);
+  border:1px solid #dddd00;
+}
+/* IE6 can't position fixed */
+* html div#naMessage div { position:absolute; }
+
+div#naMessage strong {
+  font-size:1.1em;
 }
 
 .absent,
@@ -367,25 +402,53 @@
 .absent a:visited,
 .absent a:hover,
 .absent * {
-  color:#aaa !important;
-  background-color:#f6f6f6 !important;
+  color:#bbb !important;
   cursor:default !important;
   text-decoration:none !important;
 }
 
-#side-nav li.absent,
-#side-nav li.absent * {
-  background-color:#fff !important;
+#api-level-toggle a,
+.api-level a {
+  color:inherit;
+  text-decoration:none;
+}
+
+#api-level-toggle a:hover,
+.api-level a:hover {
+  color:inherit;
+  text-decoration:underline !important;
+  cursor:pointer !important;
 }
 
 #side-nav li.absent.selected,
-#side-nav li.absent.selected * {
-  background-color:#eee !important;
+#side-nav li.absent.selected *,
+#side-nav div.label.absent.selected,
+#side-nav div.label.absent.selected * {
+  background-color:#eaeaea !important;
 }
+/* IE6 quirk (won't chain classes, so just keep background blue) */
+* html #side-nav li.selected,
+* html #side-nav li.selected *,
+* html #side-nav div.label.selected,
+* html #side-nav div.label.selected * {
+  background-color: #435a6e !important;
+}
+
+
+.absent h4.jd-details-title,
+.absent h4.jd-details-title * {
+  background-color:#f6f6f6 !important;
+}
+
+.absent img {
+  opacity: .3;
+  filter: alpha(opacity=30);
+  -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";
+}
+
 
 /* applies to a div containing links to summary tables */
 .sum-details-links {
-  margin:0 .5em;
   padding:0;
   font-weight:normal;
 }
@@ -554,8 +617,7 @@
   font-size:1.15em;
   background-color: #E2E2E2;
   margin:1.5em 0 .6em;
-  padding:3px;
-  position:relative; /* so the api level can be absolute */
+  padding:3px 95px 3px 3px; /* room for api-level */
 }
 
 h4.jd-tagtitle {
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.js b/tools/droiddoc/templates/assets/android-developer-docs.js
index 2edf39d..b16ed0d 100644
--- a/tools/droiddoc/templates/assets/android-developer-docs.js
+++ b/tools/droiddoc/templates/assets/android-developer-docs.js
@@ -31,16 +31,9 @@
   addLoadEvent(mobileSetup);
 }
 
-/* loads the lists.js file to the page.
-Loading this in the head was slowing page load time */
-addLoadEvent( function() {
-  var lists = document.createElement("script");
-  lists.setAttribute("type","text/javascript");
-  lists.setAttribute("src", toRoot+"reference/lists.js");
-  $("head").append($(lists));
-} );
-
+addLoadEvent(function() {
 window.onresize = resizeAll;
+});
 
 function mobileSetup() {
   $("body").css({'overflow':'auto'});
@@ -51,6 +44,15 @@
   $("#nav-tree").css({'overflow-y': 'auto'});
 }
 
+/* loads the lists.js file to the page.
+Loading this in the head was slowing page load time */
+addLoadEvent( function() {
+  var lists = document.createElement("script");
+  lists.setAttribute("type","text/javascript");
+  lists.setAttribute("src", toRoot+"reference/lists.js");
+  $("head").append($(lists));
+} );
+
 function setToRoot(root) {
   toRoot = root;
   // note: toRoot also used by carousel.js
diff --git a/tools/droiddoc/templates/assets/navtree.js b/tools/droiddoc/templates/assets/android-developer-reference.js
similarity index 64%
rename from tools/droiddoc/templates/assets/navtree.js
rename to tools/droiddoc/templates/assets/android-developer-reference.js
index 33a4f81..44fa903 100644
--- a/tools/droiddoc/templates/assets/navtree.js
+++ b/tools/droiddoc/templates/assets/android-developer-reference.js
@@ -1,5 +1,65 @@
 
-function new_node(me, mom, text, link, children_data)
+/* API LEVEL TOGGLE */
+addLoadEvent(changeApiLevel);
+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;
+	}
+	
+  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);
+	}
+	
+  // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
+	var selectedLevelItem = $("#apiLevelControl option[value='"+selectedLevel+"']").get(0); 
+  selectedLevelItem.setAttribute('selected',true); 
+}
+
+function changeApiLevel() {
+	var selectedLevel = $("#apiLevelControl option:selected").val();
+	var apis = $(".api");
+	apis.each(function(i) {
+		var obj = $(this);
+		var className = obj.attr("class");
+		var apiLevelIndex = className.lastIndexOf("-")+1;
+		var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
+		apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
+		var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
+		if (apiLevel > selectedLevel) obj.addClass("absent").attr("title","Requires API Level "+apiLevel+" or higher");
+		else obj.removeClass("absent").removeAttr("title");
+	});
+
+  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>"
+                              + "<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 {
+    $("#naMessage").hide();
+  }
+}
+
+/* NAVTREE */
+
+function new_node(me, mom, text, link, children_data, api_level)
 {
   var node = new Object();
   node.children = Array();
@@ -10,9 +70,13 @@
   mom.get_children_ul().appendChild(node.li);
 
   node.label_div = document.createElement("div");
+  node.label_div.className = "label";
+  if (api_level != null) {
+    $(node.label_div).addClass("api");
+    $(node.label_div).addClass("api-level-"+api_level);
+  }
   node.li.appendChild(node.label_div);
   node.label_div.style.paddingLeft = 10*node.depth + "px";
-  node.label_div.className = "label";
 
   if (children_data == null) {
     // 12 is the width of the triangle and padding extra space
@@ -81,6 +145,7 @@
       $(node.get_children_ul()).slideDown("fast");
     } else {
       get_node(me, node);
+      if ($(node.label_div).hasClass("absent")) $(node.get_children_ul()).addClass("absent");
       $(node.get_children_ul()).slideDown("fast");
     }
     node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
@@ -94,7 +159,7 @@
   for (var i in mom.children_data) {
     var node_data = mom.children_data[i];
     mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
-        node_data[2]);
+        node_data[2], node_data[3]);
   }
 }
 
@@ -104,8 +169,7 @@
   var file = "";
   if (toroot.substr(0, 1) == "/") {
     if (full.substr(0, toroot.length) == toroot) {
-      var basePath = getBaseUri(full);
-      return basePath.substring(toroot.length);
+      return full.substr(toroot.length);
     } else {
       // the file isn't under toroot.  Fail.
       return null;
@@ -150,15 +214,14 @@
   navtreeData.setAttribute("type","text/javascript");
   navtreeData.setAttribute("src", toroot+"navtree_data.js");
   $("head").append($(navtreeData));
-} 
+}
 
 function init_default_navtree(toroot) {
-  load_navtree_data(toroot);
   init_navtree("nav-tree", toroot, NAVTREE_DATA);
 }
 
 function init_navtree(navtree_id, toroot, root_nodes)
-{  
+{
   var me = new Object();
   me.toroot = toroot;
   me.node = new Object();
@@ -189,4 +252,3 @@
       });
   }
 }
-
diff --git a/tools/droiddoc/templates/assets/images/preliminary.png b/tools/droiddoc/templates/assets/images/preliminary.png
old mode 100755
new mode 100644
index aad1644..fe0da3d
--- a/tools/droiddoc/templates/assets/images/preliminary.png
+++ b/tools/droiddoc/templates/assets/images/preliminary.png
Binary files differ
diff --git a/tools/droiddoc/templates/class.cs b/tools/droiddoc/templates/class.cs
index 90e1472..9bc2ba0 100644
--- a/tools/droiddoc/templates/class.cs
+++ b/tools/droiddoc/templates/class.cs
@@ -2,7 +2,7 @@
 <?cs include:"macros.cs" ?>
 <html>
 <?cs include:"head_tag.cs" ?>
-<body>
+<body class="<?cs var:class.since ?>">
 <script type="text/javascript">
 function toggleInherited(linkObj, expand) {
     var base = linkObj.getAttribute("id");
@@ -26,7 +26,6 @@
     return false;
 }
 </script>
-
 <?cs include:"header.cs" ?>
 
 <div class="g-unit" id="doc-content">
@@ -106,7 +105,9 @@
 &#124; <a href="#" onclick="return toggleAllSummaryInherited(this)">[Expand All]</a>
 <?cs /if ?>
 </div><!-- end sum-details-links -->
-
+<div class="api-level">
+  <?cs call:since_tags(class) ?>
+</div>
 </div><!-- end api-info-block -->
 
 <?cs # this next line must be exactly like this to be parsed by eclipse ?>
@@ -134,12 +135,11 @@
   <?cs set:colspan = colspan-1 ?>
 <?cs /each ?>
 
-<div class="api-level"><?cs call:since_tags(class) ?></div>
-
 </div><!-- end header -->
 
+<div id="naMessage"></div>
 
-<div id="jd-content" class="apilevel-<?cs var:class.since ?>">
+<div id="jd-content" class="api apilevel-<?cs var:class.since ?>">
 <table class="jd-inheritance-table">
 <?cs set:colspan = subcount(class.inheritance) ?>
 <?cs each:supr = class.inheritance ?>
@@ -215,7 +215,7 @@
 <?cs def:write_field_summary(fields) ?>
 <?cs set:count = #1 ?>
     <?cs each:field=fields ?>
-      <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+      <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:field.since ?>" >
           <td class="jd-typecol"><nobr>
           <?cs var:field.scope ?>
           <?cs var:field.static ?>
@@ -231,7 +231,7 @@
 <?cs def:write_constant_summary(fields) ?>
 <?cs set:count = #1 ?>
     <?cs each:field=fields ?>
-    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+    <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-descrcol" width="100%"><?cs call:short_descr(field) ?></td>
@@ -248,7 +248,7 @@
         <td><nobr><em>Description</em></nobr></td>
     </tr>
     <?cs each:attr=attrs ?>
-    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+    <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 each:m=attr.methods ?>
             <a href="<?cs var:toroot ?><?cs var:m.href ?>"><?cs var:m.name ?></a>
@@ -263,13 +263,13 @@
 <?cs def:write_inners_summary(classes) ?>
 <?cs set:count = #1 ?>
   <?cs each:cl=class.inners ?>
-    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+    <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:cl.since ?>" >
       <td class="jd-typecol"><nobr>
-        <?cs var:class.scope ?>
-        <?cs var:class.static ?> 
-        <?cs var:class.final ?> 
-        <?cs var:class.abstract ?>
-        <?cs var:class.kind ?></nobr></td>
+        <?cs var:cl.scope ?>
+        <?cs var:cl.static ?> 
+        <?cs var:cl.final ?> 
+        <?cs var:cl.abstract ?>
+        <?cs var:cl.kind ?></nobr></td>
       <td class="jd-linkcol"><?cs call:type_link(cl.type) ?></td>
       <td class="jd-descrcol" width="100%"><?cs call:short_descr(cl) ?>&nbsp;</td>
     </tr>
@@ -305,7 +305,8 @@
   <div style="clear:left;">Inherited XML Attributes</div></th></tr>
 <?cs each:cl=class.inherited ?>
 <?cs if:subcount(cl.attrs) ?>
-<tr><td colspan="12">
+<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>
 <div id="inherited-attrs-<?cs var:cl.qualified ?>">
@@ -329,7 +330,7 @@
 <table id="enumconstants" class="jd-sumtable"><tr><th colspan="12">Enum Values</th></tr>
 <?cs set:count = #1 ?>
     <?cs each:field=class.enumConstants ?>
-    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+    <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-descrcol" width="100%"><?cs call:short_descr(field) ?>&nbsp;</td>
@@ -355,7 +356,8 @@
   <div style="clear:left;">Inherited Constants</div></th></tr>
 <?cs each:cl=class.inherited ?>
 <?cs if:subcount(cl.constants) ?>
-<tr><td colspan="12">
+<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>
 <div id="inherited-constants-<?cs var:cl.qualified ?>">
@@ -390,7 +392,8 @@
   <div style="clear:left;">Inherited Fields</div></th></tr>
 <?cs each:cl=class.inherited ?>
 <?cs if:subcount(cl.fields) ?>
-<tr><td colspan="12">
+<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>
 <div id="inherited-fields-<?cs var:cl.qualified ?>">
@@ -449,7 +452,8 @@
   <div style="clear:left;">Inherited Methods</div></th></tr>
 <?cs each:cl=class.inherited ?>
 <?cs if:subcount(cl.methods) ?>
-<tr><td colspan="12"><?cs call:expando_trigger("inherited-methods-"+cl.qualified, "closed") ?>
+<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>
 <div id="inherited-methods-<?cs var:cl.qualified ?>">
   <div id="inherited-methods-<?cs var:cl.qualified ?>-list"
@@ -485,11 +489,12 @@
         <?cs call:type_link(field.type) ?>
       </span>
         <?cs var:field.name ?>
-      <span class="api-level">
-        <?cs call:since_tags(field) ?>
-      </span>
     </h4>
-    <div class="jd-details-descr"><?cs call:description(field) ?>
+      <div class="api-level">
+        <?cs call:since_tags(field) ?>
+      </div>
+    <div class="jd-details-descr">
+      <?cs call:description(field) ?>
     <?cs if:subcount(field.constantValue) ?>
         <div class="jd-tagdata">
         <span class="jd-tagtitle">Constant Value: </span>
@@ -525,11 +530,13 @@
       </span>
       <span class="sympad"><?cs var:method.name ?></span>
       <span class="normal">(<?cs call:parameter_list(method.params) ?>)</span>
-      <span class="api-level">
-        <?cs call:since_tags(method) ?>
-      </span>
     </h4>
-    <div class="jd-details-descr"><?cs call:description(method) ?></div>
+      <div class="api-level">
+        <?cs call:since_tags(method) ?>
+      </div>
+    <div class="jd-details-descr">
+      <?cs call:description(method) ?>
+    </div>
 </div>
 <?cs /each ?>
 <?cs /def ?>
@@ -541,10 +548,10 @@
 <?cs # The apilevel-N class MUST BE LAST in the sequence of class names ?>
 <div class="jd-details api apilevel-<?cs var:attr.since ?>"> 
     <h4 class="jd-details-title"><?cs var:attr.name ?>
-      <span class="api-level">
-        <?cs call:since_tags(attr) ?>
-      </span>
     </h4>
+      <div class="api-level">
+        <?cs call:since_tags(attr) ?>
+      </div>
     <div class="jd-details-descr">
         <?cs call:description(attr) ?>
 
diff --git a/tools/droiddoc/templates/classes.cs b/tools/droiddoc/templates/classes.cs
index abe8e4e..5a8315f 100644
--- a/tools/droiddoc/templates/classes.cs
+++ b/tools/droiddoc/templates/classes.cs
@@ -22,7 +22,7 @@
 <table class="jd-sumtable">
     <?cs set:cur_row = #0 ?>
     <?cs each:cl = letter ?>
-        <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+        <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:cl.since ?>" >
             <td class="jd-linkcol"><?cs call:type_link(cl.type) ?></td>
             <td class="jd-descrcol" width="100%"><?cs call:short_descr(cl) ?>&nbsp;</td>
         </tr>
diff --git a/tools/droiddoc/templates/head_tag.cs b/tools/droiddoc/templates/head_tag.cs
index f721403..a2ea30a 100644
--- a/tools/droiddoc/templates/head_tag.cs
+++ b/tools/droiddoc/templates/head_tag.cs
@@ -17,7 +17,8 @@
   setToRoot("<?cs var:toroot ?>");
 </script><?cs 
 if:reference ?>
-<script src="<?cs var:toroot ?>assets/navtree.js" type="text/javascript"></script><?cs 
+<script src="<?cs var:toroot ?>assets/android-developer-reference.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>navtree_data.js" type="text/javascript"></script><?cs 
 /if ?>
 <noscript>
   <style type="text/css">
diff --git a/tools/droiddoc/templates/macros.cs b/tools/droiddoc/templates/macros.cs
index 9f0e5ac..14dc90e 100644
--- a/tools/droiddoc/templates/macros.cs
+++ b/tools/droiddoc/templates/macros.cs
@@ -117,7 +117,7 @@
 
 <?cs # print the API Level ?><?cs
 def:since_tags(obj) ?>
-  Since: API Level <?cs var:obj.since ?>
+  Since: <a href="<?cs var:toroot ?>guide/appendix/api-levels.html#level<?cs var:obj.since ?>">API Level <?cs var:obj.since ?></a>
 <?cs /def ?>
 
 <?cs # Print the long-form description for something.
@@ -203,7 +203,7 @@
     <li><h2><?cs var:label ?></h2>
       <ul><?cs 
       each:cl=classes ?>
-          <li class="<?cs if:class.name == cl.label?>selected<?cs /if ?> api apilevel-<?cs var:cl.since ?>"><?cs call:type_link(cl) ?></li><?cs 
+          <li class="<?cs if:class.name == cl.label?>selected <?cs /if ?>api apilevel-<?cs var:cl.since ?>"><?cs call:type_link(cl) ?></li><?cs 
       /each ?>
       </ul>
     </li><?cs 
@@ -213,7 +213,7 @@
 <?cs # A list of links to packages, for use in the side navigation of packages (panel nav) ?><?cs 
 def:package_link_list(packages) ?><?cs 
   each:pkg=packages ?>
-    <li class="<?cs if:(class.package.name == pkg.name) || (package.name == pkg.name)?>selected<?cs /if ?> api apilevel-<?cs var:pkg.since ?>"><?cs call:package_link(pkg) ?></li><?cs 
+    <li class="<?cs if:(class.package.name == pkg.name) || (package.name == pkg.name)?>selected <?cs /if ?>api apilevel-<?cs var:pkg.since ?>"><?cs call:package_link(pkg) ?></li><?cs 
   /each ?><?cs
 /def ?>
 
diff --git a/tools/droiddoc/templates/package-descr.cs b/tools/droiddoc/templates/package-descr.cs
index 112db4b..08fee18 100644
--- a/tools/droiddoc/templates/package-descr.cs
+++ b/tools/droiddoc/templates/package-descr.cs
@@ -2,26 +2,31 @@
 <?cs include:"macros.cs" ?>
 <html>
 <?cs include:"head_tag.cs" ?>
+<body class="<?cs var:package.since ?>">
 <?cs include:"header.cs" ?>
 
 <div class="g-unit" id="doc-content">
 
+<div id="api-info-block">
+<div class="api-level">
+  <?cs call:since_tags(package) ?>
+</div>
+</div>
+
 <div id="jd-header">
-  <strong>
-    <div class="jd-page_title-prefix">package</div>
-  </strong>
+  package
   <h1><?cs var:package.name ?></b></h1>
   <div class="jd-nav">
-      <a class="jd-navlink" href="package-summary.html">Classes</a> |
-      Description
+      <a class="jd-navlink" href="package-summary.html">Classes</a> | Description
   </div>
 </div><!-- end header -->
 
-<div id="jd-content">
+<div id="naMessage"></div>
+
+<div id="jd-content" class="api apilevel-<?cs var:package.since ?>">
 <div class="jd-descr">
 <p><?cs call:tag_list(package.descr) ?></p>
 </div>
-<?cs call:since_tags(package) ?>
 
 <?cs include:"footer.cs" ?>
 </div><!-- end jd-content -->
diff --git a/tools/droiddoc/templates/package.cs b/tools/droiddoc/templates/package.cs
index 5ef3e0c..b29bc77 100644
--- a/tools/droiddoc/templates/package.cs
+++ b/tools/droiddoc/templates/package.cs
@@ -2,25 +2,30 @@
 <?cs include:"macros.cs" ?>
 <html>
 <?cs include:"head_tag.cs" ?>
+<body class="<?cs var:package.since ?>">
 <?cs include:"header.cs" ?>
 
 <div class="g-unit" id="doc-content">
 
+<div id="api-info-block">
+<div class="api-level">
+  <?cs call:since_tags(package) ?>
+</div>
+</div>
+
 <div id="jd-header">
   package
   <h1><?cs var:package.name ?></h1>
   <div class="jd-nav">
       <?cs if:subcount(package.shortDescr) ?>
-      Classes |
-      <a class="jd-navlink" href="package-descr.html">Description</a>
+        Classes | <a class="jd-navlink" href="package-descr.html">Description</a>
       <?cs /if ?>
   </div>
-  <span class="api-level">
-    <?cs call:since_tags(package) ?>
-  </span>
-</div>
+</div><!-- end header -->
 
-<div id="jd-content">
+<div id="naMessage"></div>
+
+<div id="jd-content" class="api apilevel-<?cs var:package.since ?>">
 
 <?cs if:subcount(package.shortDescr) ?>
   <div class="jd-descr">
diff --git a/tools/droiddoc/templates/packages.cs b/tools/droiddoc/templates/packages.cs
index a358dca..c2d8c75 100644
--- a/tools/droiddoc/templates/packages.cs
+++ b/tools/droiddoc/templates/packages.cs
@@ -20,7 +20,7 @@
 <?cs set:count = #1 ?>
 <table class="jd-sumtable">
 <?cs each:pkg = docs.packages ?>
-    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+    <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:pkg.since ?>" >
         <td class="jd-linkcol"><?cs call:package_link(pkg) ?></td>
         <td class="jd-descrcol" width="100%"><?cs call:tag_list(pkg.shortDescr) ?>&nbsp;</td>
     </tr>
diff --git a/tools/releasetools/amend_generator.py b/tools/releasetools/amend_generator.py
index 3e8af13..70e71d4 100644
--- a/tools/releasetools/amend_generator.py
+++ b/tools/releasetools/amend_generator.py
@@ -180,6 +180,7 @@
 
   def MakeSymlinks(self, symlink_list):
     """Create symlinks, given a list of (dest, link) pairs."""
+    self.DeleteFiles([i[1] for i in symlink_list])
     self.script.extend(["symlink %s %s" % (i[0], self._FileRoot(i[1]))
                         for i in sorted(symlink_list)])
 
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index a07ff7c..8ca7781 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
 
@@ -46,18 +47,20 @@
   return subprocess.Popen(args, **kwargs)
 
 
-def LoadBoardConfig(fn):
-  """Parse a board_config.mk file looking for lines that specify the
-  maximum size of various images, and parse them into the
-  OPTIONS.max_image_size dict."""
+def LoadMaxSizes():
+  """Load the maximum allowable images sizes from the input
+  target_files size."""
   OPTIONS.max_image_size = {}
-  for line in open(fn):
-    line = line.strip()
-    m = re.match(r"BOARD_(BOOT|RECOVERY|SYSTEM|USERDATA)IMAGE_MAX_SIZE"
-                 r"\s*:=\s*(\d+)", line)
-    if not m: continue
-
-    OPTIONS.max_image_size[m.group(1).lower() + ".img"] = int(m.group(2))
+  try:
+    for line in open(os.path.join(OPTIONS.input_tmp, "META", "imagesizes.txt")):
+      pieces = line.split()
+      if len(pieces) != 2: continue
+      image = pieces[0]
+      size = int(pieces[1])
+      OPTIONS.max_image_size[image + ".img"] = size
+  except IOError, e:
+    if e.errno == errno.ENOENT:
+      pass
 
 
 def BuildAndAddBootableImage(sourcedir, targetname, output_zip):
@@ -245,6 +248,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.
 
@@ -269,8 +276,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), "**"
@@ -286,6 +294,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,)
@@ -410,3 +420,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 path is None: 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/img_from_target_files b/tools/releasetools/img_from_target_files
index 1d154b9..00abde4 100755
--- a/tools/releasetools/img_from_target_files
+++ b/tools/releasetools/img_from_target_files
@@ -21,8 +21,7 @@
 Usage:  img_from_target_files [flags] input_target_files output_image_zip
 
   -b  (--board_config)  <file>
-      Specifies a BoardConfig.mk file containing image max sizes
-      against which the generated image files are checked.
+      Deprecated.
 
 """
 
@@ -109,10 +108,10 @@
 
   def option_handler(o, a):
     if o in ("-b", "--board_config"):
-      common.LoadBoardConfig(a)
-      return True
+      pass       # deprecated
     else:
       return False
+    return True
 
   args = common.ParseOptions(argv, __doc__,
                              extra_opts="b:",
@@ -123,15 +122,15 @@
     common.Usage(__doc__)
     sys.exit(1)
 
+  OPTIONS.input_tmp = common.UnzipTemp(args[0])
+
+  common.LoadMaxSizes()
   if not OPTIONS.max_image_size:
     print
-    print "  WARNING:  No board config specified; will not check image"
-    print "  sizes against limits.  Use -b to make sure the generated"
-    print "  images don't exceed partition sizes."
+    print "  WARNING:  Failed to load max image sizes; will not enforce"
+    print "  image size limits."
     print
 
-  OPTIONS.input_tmp = common.UnzipTemp(args[0])
-
   output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
 
   common.AddBoot(output_zip)
diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files
index 4cda44a..864c35b 100755
--- a/tools/releasetools/ota_from_target_files
+++ b/tools/releasetools/ota_from_target_files
@@ -22,8 +22,7 @@
 Usage:  ota_from_target_files [flags] input_target_files output_ota_package
 
   -b  (--board_config)  <file>
-      Specifies a BoardConfig.mk file containing image max sizes
-      against which the generated image files are checked.
+      Deprecated.
 
   -k  (--package_key)  <key>
       Key to use to sign the package (default is
@@ -285,7 +284,8 @@
   m = re.search(r"require\s+version-bootloader\s*=\s*(\S+)", info)
   if m:
     bootloaders = m.group(1).split("|")
-    script.AssertSomeBootloader(*bootloaders)
+    if "*" not in bootloaders:
+      script.AssertSomeBootloader(*bootloaders)
 
 
 def MakeRecoveryPatch(output_zip, recovery_img, boot_img):
@@ -340,19 +340,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)
 
@@ -386,8 +385,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)
@@ -503,6 +505,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..."
@@ -567,19 +575,15 @@
       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
+  # We reserve the last 0.3 of the progress bar for the
+  # device-specific IncrementalOTA_InstallEnd() call at the end, which
+  # will typically install a radio image.
+  progress_bar_total = 0.7
   if updating_boot:
     progress_bar_total -= 0.1
-  if updating_radio:
-    progress_bar_total -= 0.3
 
   AppendAssertions(script, target_zip)
+  device_specific.IncrementalOTA_Assertions()
 
   script.Print("Verifying current system...")
 
@@ -610,6 +614,8 @@
     script.Print("Unpacking patches...")
     script.UnpackPackageDir("patch", "/tmp/patchtmp")
 
+  device_specific.IncrementalOTA_VerifyEnd()
+
   script.Comment("---- start making changes here ----")
 
   if OPTIONS.wipe_user_data:
@@ -654,15 +660,6 @@
   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 /
@@ -707,7 +704,7 @@
     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 +723,10 @@
   # permissions.
   script.AppendScript(temp_script)
 
+  # Write the radio image, if necessary.
+  script.ShowProgress(0.3, 10)
+  device_specific.IncrementalOTA_InstallEnd()
+
   if OPTIONS.extra_script is not None:
     scirpt.AppendExtra(OPTIONS.extra_script)
 
@@ -736,7 +737,7 @@
 
   def option_handler(o, a):
     if o in ("-b", "--board_config"):
-      common.LoadBoardConfig(a)
+      pass   # deprecated
     elif o in ("-k", "--package_key"):
       OPTIONS.package_key = a
     elif o in ("-i", "--incremental_from"):
@@ -768,13 +769,6 @@
     common.Usage(__doc__)
     sys.exit(1)
 
-  if not OPTIONS.max_image_size:
-    print
-    print "  WARNING:  No board config specified; will not check image"
-    print "  sizes against limits.  Use -b to make sure the generated"
-    print "  images don't exceed partition sizes."
-    print
-
   if OPTIONS.script_mode not in ("amend", "edify", "auto"):
     raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
 
@@ -783,6 +777,14 @@
 
   print "unzipping target target-files..."
   OPTIONS.input_tmp = common.UnzipTemp(args[0])
+
+  common.LoadMaxSizes()
+  if not OPTIONS.max_image_size:
+    print
+    print "  WARNING:  Failed to load max image sizes; will not enforce"
+    print "  image size limits."
+    print
+
   OPTIONS.target_tmp = OPTIONS.input_tmp
   input_zip = zipfile.ZipFile(args[0], "r")
   if OPTIONS.package_key:
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index 6dd8ede..5153398 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).
@@ -302,9 +298,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:
@@ -334,9 +328,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/warn.py b/tools/warn.py
new file mode 100755
index 0000000..b379849
--- /dev/null
+++ b/tools/warn.py
@@ -0,0 +1,521 @@
+#!/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 pat in i['patterns']:
+            if re.match(pat, 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
+
+
+
+infile = open(sys.argv[1], 'r')
+warnings = []
+
+platformversion = 'unknown'
+targetproduct = 'unknown'
+targetvariant = 'unknown'
+linecounter = 0
+
+warningpattern = re.compile('.* warning:.*')
+
+# 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/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp
index 058f9ed..eab2f04 100644
--- a/tools/zipalign/ZipAlign.cpp
+++ b/tools/zipalign/ZipAlign.cpp
@@ -16,7 +16,7 @@
 /*
  * Zip alignment tool
  */
-#include "utils/ZipFile.h"
+#include "ZipFile.h"
 
 #include <stdlib.h>
 #include <stdio.h>
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