Merge branch 'master' of git://android.git.kernel.org/platform/build into merge_korg_master

Conflicts:
	core/apicheck_msg_current.txt
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 22d9fe1..38403ee 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,27 +54,12 @@
 #$(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 $(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 $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/core/Makefile b/core/Makefile
index 58a9695..b20c524 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -62,9 +62,6 @@
 # Apps are always signed with test keys, and may be re-signed in a post-build
 # step.  If that happens, the "test-keys" tag will be removed by that step.
 BUILD_VERSION_TAGS += test-keys
-ifndef INCLUDE_TEST_OTA_KEYS
-  BUILD_VERSION_TAGS += ota-rel-keys
-endif
 BUILD_VERSION_TAGS := $(subst $(space),$(comma),$(sort $(BUILD_VERSION_TAGS)))
 
 # A human-readable string that descibes this build in detail.
@@ -121,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)" \
@@ -129,10 +127,12 @@
 			BUILD_NUMBER="$(BUILD_NUMBER)" \
 			PLATFORM_VERSION="$(PLATFORM_VERSION)" \
 			PLATFORM_SDK_VERSION="$(PLATFORM_SDK_VERSION)" \
+			PLATFORM_VERSION_CODENAME="$(PLATFORM_VERSION_CODENAME)" \
 			BUILD_VERSION_TAGS="$(BUILD_VERSION_TAGS)" \
 			TARGET_BOOTLOADER_BOARD_NAME="$(TARGET_BOOTLOADER_BOARD_NAME)" \
 			BUILD_FINGERPRINT="$(BUILD_FINGERPRINT)" \
 			TARGET_BOARD_PLATFORM="$(TARGET_BOARD_PLATFORM)" \
+			TARGET_CPU_ABI="$(TARGET_CPU_ABI)" \
 	        bash $(BUILDINFO_SH) > $@
 	$(hide) if [ -f $(TARGET_DEVICE_DIR)/system.prop ]; then \
 	          cat $(TARGET_DEVICE_DIR)/system.prop >> $@; \
@@ -227,6 +227,15 @@
 	"INSTALLED=\"$(strip $(ALL_MODULES.$(m).INSTALLED))\"" >> $(MODULE_INFO_FILE)))
 endif
 
+# -----------------------------------------------------------------
+
+# The test key is used to sign this package, and as the key required
+# for future OTA packages installed by this system.  Actual product
+# deliverables will be re-signed by hand.  We expect this file to
+# exist with the suffixes ".x509.pem" and ".pk8".
+DEFAULT_KEY_CERT_PAIR := $(SRC_TARGET_DIR)/product/security/testkey
+
+
 # Rules that need to be present for the simulator, even
 # if they don't do anything.
 .PHONY: systemimage
@@ -250,9 +259,9 @@
 
 # We just build this directly to the install location.
 INSTALLED_RAMDISK_TARGET := $(BUILT_RAMDISK_TARGET)
-$(INSTALLED_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_RAMDISK_FILES)
+$(INSTALLED_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_RAMDISK_FILES) | $(MINIGZIP)
 	$(call pretty,"Target ram disk: $@")
-	$(hide) $(MKBOOTFS) $(TARGET_ROOT_OUT) | gzip > $@
+	$(hide) $(MKBOOTFS) $(TARGET_ROOT_OUT) | $(MINIGZIP) > $@
 
 
 ifneq ($(strip $(TARGET_NO_KERNEL)),true)
@@ -271,6 +280,11 @@
   INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)"
 endif
 
+BOARD_KERNEL_BASE := $(strip $(BOARD_KERNEL_BASE))
+ifdef BOARD_KERNEL_BASE
+  INTERNAL_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)
+endif
+
 INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
 
 ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true)
@@ -434,12 +448,16 @@
 # the module processing has already been done -- in fact, we used the
 # fact that all that has been done to get the list of modules that we
 # need notice files for.
-$(target_notice_file_html_gz): $(target_notice_file_html)
-	gzip -c $< > $@
+$(target_notice_file_html_gz): $(target_notice_file_html) | $(MINIGZIP)
+	$(hide) $(MINIGZIP) -9 < $< > $@
 installed_notice_html_gz := $(TARGET_OUT)/etc/NOTICE.html.gz
 $(installed_notice_html_gz): $(target_notice_file_html_gz) | $(ACP)
 	$(copy-file-to-target)
+
+# if we've been run my mm, mmm, etc, don't reinstall this every time
+ifeq ($(ONE_SHOT_MAKEFILE),)
 ALL_DEFAULT_INSTALLED_MODULES += $(installed_notice_html_gz)
+endif
 
 # The kernel isn't really a module, so to get its module file in there, we
 # make the target NOTICE files depend on this particular file too, which will
@@ -452,6 +470,23 @@
 	$(hide) $(ACP) $< $@
 
 
+# -----------------------------------------------------------------
+# Build a keystore with the authorized keys in it, used to verify the
+# authenticity of downloaded OTA packages.
+#
+# This rule adds to ALL_DEFAULT_INSTALLED_MODULES, so it needs to come
+# before the rules that use that variable to build the image.
+ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_ETC)/security/otacerts.zip
+$(TARGET_OUT_ETC)/security/otacerts.zip: KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
+$(TARGET_OUT_ETC)/security/otacerts.zip: $(addsuffix .x509.pem,$(DEFAULT_KEY_CERT_PAIR))
+	$(hide) rm -f $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) zip -qj $@ $<
+
+.PHONY: otacerts
+otacerts: $(TARGET_OUT_ETC)/security/otacerts.zip
+
+
 # #################################################################
 # Targets for user images
 # #################################################################
@@ -644,14 +679,35 @@
 ifdef BOARD_KERNEL_CMDLINE
   INTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)"
 endif
+ifdef BOARD_KERNEL_BASE
+  INTERNAL_RECOVERYIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)
+endif
 
-$(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) \
+# Keys authorized to sign OTA packages this build will accept.  The
+# build always uses test-keys for this; release packaging tools will
+# substitute other keys for this one.
+OTA_PUBLIC_KEYS := $(SRC_TARGET_DIR)/product/security/testkey.x509.pem
+
+# Generate a file containing the keys that will be read by the
+# recovery binary.
+RECOVERY_INSTALL_OTA_KEYS := \
+	$(call intermediates-dir-for,PACKAGING,ota_keys)/keys
+DUMPKEY_JAR := $(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar
+$(RECOVERY_INSTALL_OTA_KEYS): PRIVATE_OTA_PUBLIC_KEYS := $(OTA_PUBLIC_KEYS)
+$(RECOVERY_INSTALL_OTA_KEYS): $(OTA_PUBLIC_KEYS) $(DUMPKEY_JAR)
+	@echo "DumpPublicKey: $@ <= $(PRIVATE_OTA_PUBLIC_KEYS)"
+	@rm -rf $@
+	@mkdir -p $(dir $@)
+	java -jar $(DUMPKEY_JAR) $(PRIVATE_OTA_PUBLIC_KEYS) > $@
+
+$(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $(MINIGZIP) \
 		$(INSTALLED_RAMDISK_TARGET) \
 		$(INSTALLED_BOOTIMAGE_TARGET) \
 		$(recovery_binary) \
 		$(recovery_initrc) $(recovery_kernel) \
 		$(INSTALLED_2NDBOOTLOADER_TARGET) \
-		$(recovery_build_prop) $(recovery_resource_deps)
+		$(recovery_build_prop) $(recovery_resource_deps) \
+		$(RECOVERY_INSTALL_OTA_KEYS)
 	@echo ----- Making recovery image ------
 	rm -rf $(TARGET_RECOVERY_OUT)
 	mkdir -p $(TARGET_RECOVERY_OUT)
@@ -666,9 +722,10 @@
 	cp -rf $(recovery_resources_common) $(TARGET_RECOVERY_ROOT_OUT)/
 	$(foreach item,$(recovery_resources_private), \
 	  cp -rf $(item) $(TARGET_RECOVERY_ROOT_OUT)/)
+	cp $(RECOVERY_INSTALL_OTA_KEYS) $(TARGET_RECOVERY_ROOT_OUT)/res/keys
 	cat $(INSTALLED_DEFAULT_PROP_TARGET) $(recovery_build_prop) \
 	        > $(TARGET_RECOVERY_ROOT_OUT)/default.prop
-	$(MKBOOTFS) $(TARGET_RECOVERY_ROOT_OUT) | gzip > $(recovery_ramdisk)
+	$(MKBOOTFS) $(TARGET_RECOVERY_ROOT_OUT) | $(MINIGZIP) > $(recovery_ramdisk)
 	$(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) --output $@
 	@echo ----- Made recovery image -------- $@
 	$(hide) $(call assert-max-file-size,$@,$(BOARD_RECOVERYIMAGE_MAX_SIZE))
@@ -687,123 +744,20 @@
 endif
 
 # -----------------------------------------------------------------
-# OTA update package
-name := $(TARGET_PRODUCT)
-ifeq ($(TARGET_BUILD_TYPE),debug)
-  name := $(name)_debug
-endif
-name := $(name)-ota-$(FILE_NAME_TAG)
+# host tools needed to build OTA packages
 
-INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
-INTERNAL_OTA_INTERMEDIATES_DIR := $(call intermediates-dir-for,PACKAGING,ota)
-
-# If neither TARGET_NO_KERNEL nor TARGET_NO_RECOVERY are true
-ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))
-INTERNAL_OTA_RECOVERYIMAGE_TARGET := $(INTERNAL_OTA_INTERMEDIATES_DIR)/system/recovery.img
-else
-INTERNAL_OTA_RECOVERYIMAGE_TARGET :=
-endif
-INTERNAL_OTA_SCRIPT_TARGET := $(INTERNAL_OTA_INTERMEDIATES_DIR)/META-INF/com/google/android/update-script
-
-# Sign OTA packages with the test key by default.
-# Actual product deliverables will be re-signed by hand.
-private_key := $(SRC_TARGET_DIR)/product/security/testkey.pk8
-certificate := $(SRC_TARGET_DIR)/product/security/testkey.x509.pem
-$(INTERNAL_OTA_PACKAGE_TARGET): $(private_key) $(certificate) $(SIGNAPK_JAR)
-$(INTERNAL_OTA_PACKAGE_TARGET): PRIVATE_PRIVATE_KEY := $(private_key)
-$(INTERNAL_OTA_PACKAGE_TARGET): PRIVATE_CERTIFICATE := $(certificate)
-
-# Depending on INSTALLED_SYSTEMIMAGE guarantees that SYSTEMIMAGE_SOURCE_DIR
-# is up-to-date.  We use jar instead of zip so that we can use the -C
-# switch to avoid cd-ing all over the place.
-# TODO: Make our own jar-creation tool to avoid all these shenanigans.
-$(INTERNAL_OTA_PACKAGE_TARGET): \
-		$(INTERNAL_OTA_SCRIPT_TARGET) \
-		$(INTERNAL_OTA_RECOVERYIMAGE_TARGET) \
-		$(INSTALLED_BOOTIMAGE_TARGET) \
-		$(INSTALLED_RADIOIMAGE_TARGET) \
-		$(INSTALLED_ANDROID_INFO_TXT_TARGET) \
-		$(INSTALLED_SYSTEMIMAGE)
-	@echo "Package OTA: $@"
-	$(hide) rm -rf $@
-	$(hide) jar cf $@ \
-	        $(foreach item, \
-	                $(INSTALLED_BOOTIMAGE_TARGET) \
-	                $(INSTALLED_RADIOIMAGE_TARGET) \
-	                $(INSTALLED_ANDROID_INFO_TXT_TARGET), \
-	            -C $(dir $(item)) $(notdir $(item))) \
-	            -C $(INTERNAL_OTA_INTERMEDIATES_DIR) .
-	$(hide) find $(SYSTEMIMAGE_SOURCE_DIR) -type f -print | \
-	        sed 's|^$(dir $(SYSTEMIMAGE_SOURCE_DIR))|-C & |' | \
-	        xargs jar uf $@
-	$(hide) if jar tf $@ | egrep '.{65}' >&2; then \
-	            echo "Path too long (>64 chars) for OTA update" >&2; \
-	            exit 1; \
-	        fi
-	$(sign-package)
-
-$(INTERNAL_OTA_SCRIPT_TARGET): \
-		$(HOST_OUT_EXECUTABLES)/make-update-script \
-		$(INSTALLED_ANDROID_INFO_TXT_TARGET) \
-		$(INSTALLED_SYSTEMIMAGE)
-	@mkdir -p $(dir $@)
-	@rm -rf $@
-	@echo "Update script: $@"
-	$(hide) TARGET_DEVICE=$(TARGET_DEVICE) \
-	        $< $(SYSTEMIMAGE_SOURCE_DIR) \
-	           $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
-	        > $@
-
-ifneq (,$(INTERNAL_OTA_RECOVERYIMAGE_TARGET))
-# This copy is so recovery.img can be in /system within the OTA package.
-# That way it gets installed into the system image, which in turn installs it.
-$(INTERNAL_OTA_RECOVERYIMAGE_TARGET): $(INSTALLED_RECOVERYIMAGE_TARGET) | $(ACP)
-	@mkdir -p $(dir $@)
-	$(hide) $(ACP) $< $@
-endif
-
-.PHONY: otapackage
-otapackage: $(INTERNAL_OTA_PACKAGE_TARGET)
-
-# Keys authorized to sign OTA packages this build will accept.
-ifeq ($(INCLUDE_TEST_OTA_KEYS),true)
-  OTA_PUBLIC_KEYS := \
-  	$(sort $(SRC_TARGET_DIR)/product/security/testkey.x509.pem $(OTA_PUBLIC_KEYS))
-endif
-
-ifeq ($(OTA_PUBLIC_KEYS),)
-  $(error No OTA_PUBLIC_KEYS defined)
-endif
-
-# Build a keystore with the authorized keys in it.
-# java/android/android/server/checkin/UpdateVerifier.java uses this.
-ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_ETC)/security/otacerts.zip
-$(TARGET_OUT_ETC)/security/otacerts.zip: $(OTA_PUBLIC_KEYS)
-	$(hide) rm -f $@
-	$(hide) mkdir -p $(dir $@)
-	zip -qj $@ $(OTA_PUBLIC_KEYS)
-
-# The device does not support JKS.
-# $(hide) for f in $(OTA_PUBLIC_KEYS); do \
-#   echo "keytool: $@ <= $$f" && \
-#   keytool -keystore $@ -storepass $(notdir $@) -noprompt \
-#           -import -file $$f -alias $(notdir $$f) || exit 1; \
-# done
-
-ifdef RECOVERY_INSTALL_OTA_KEYS_INC
-# Generate a C-includable file containing the keys.
-# RECOVERY_INSTALL_OTA_KEYS_INC is defined by recovery/Android.mk.
-# *** THIS IS A TOTAL HACK; EXECUTABLES MUST NOT CHANGE BETWEEN DIFFERENT
-#     PRODUCTS/BUILD TYPES. ***
-# TODO: make recovery read the keys from an external file.
-DUMPKEY_JAR := $(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar
-$(RECOVERY_INSTALL_OTA_KEYS_INC): PRIVATE_OTA_PUBLIC_KEYS := $(OTA_PUBLIC_KEYS)
-$(RECOVERY_INSTALL_OTA_KEYS_INC): $(OTA_PUBLIC_KEYS) $(DUMPKEY_JAR)
-	@echo "DumpPublicKey: $@ <= $(PRIVATE_OTA_PUBLIC_KEYS)"
-	@rm -rf $@
-	@mkdir -p $(dir $@)
-	$(hide) java -jar $(DUMPKEY_JAR) $(PRIVATE_OTA_PUBLIC_KEYS) > $@
-endif
+.PHONY: otatools
+otatools: $(HOST_OUT_EXECUTABLES)/minigzip \
+	  $(HOST_OUT_EXECUTABLES)/mkbootfs \
+	  $(HOST_OUT_EXECUTABLES)/mkbootimg \
+	  $(HOST_OUT_EXECUTABLES)/fs_config \
+	  $(HOST_OUT_EXECUTABLES)/mkyaffs2image \
+	  $(HOST_OUT_EXECUTABLES)/zipalign \
+	  $(HOST_OUT_EXECUTABLES)/aapt \
+	  $(HOST_OUT_EXECUTABLES)/bsdiff \
+	  $(HOST_OUT_EXECUTABLES)/imgdiff \
+	  $(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar \
+	  $(HOST_OUT_JAVA_LIBRARIES)/signapk.jar
 
 # -----------------------------------------------------------------
 # A zip of the directories that map to the target filesystem.
@@ -833,21 +787,22 @@
 endef
 
 built_ota_tools := \
- 	$(call intermediates-dir-for,EXECUTABLES,applypatch)/applypatch \
-	$(call intermediates-dir-for,EXECUTABLES,check_prereq)/check_prereq
+	$(call intermediates-dir-for,EXECUTABLES,applypatch)/applypatch \
+	$(call intermediates-dir-for,EXECUTABLES,check_prereq)/check_prereq \
+	$(call intermediates-dir-for,EXECUTABLES,updater)/updater
 $(BUILT_TARGET_FILES_PACKAGE): PRIVATE_OTA_TOOLS := $(built_ota_tools)
 
+$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_API_VERSION := $(RECOVERY_API_VERSION)
+
 # Depending on the various images guarantees that the underlying
 # directories are up-to-date.
 $(BUILT_TARGET_FILES_PACKAGE): \
-		$(INTERNAL_OTA_SCRIPT_TARGET) \
 		$(INSTALLED_BOOTIMAGE_TARGET) \
 		$(INSTALLED_RADIOIMAGE_TARGET) \
 		$(INSTALLED_RECOVERYIMAGE_TARGET) \
 		$(BUILT_SYSTEMIMAGE) \
 		$(INSTALLED_USERDATAIMAGE_TARGET) \
 		$(INSTALLED_ANDROID_INFO_TXT_TARGET) \
-		$(INTERNAL_OTA_SCRIPT_TARGET) \
 		$(built_ota_tools) \
 		$(APKCERTS_FILE) \
 		| $(ACP)
@@ -868,6 +823,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, \
@@ -882,11 +840,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)
@@ -895,20 +854,65 @@
 		$(TARGET_OUT_DATA),$(zip_root)/DATA)
 	@# Extra contents of the OTA package
 	$(hide) mkdir -p $(zip_root)/OTA/bin
-	$(hide) $(call package_files-copy-root, \
-		$(INTERNAL_OTA_INTERMEDIATES_DIR),$(zip_root)/OTA)
 	$(hide) $(ACP) $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/
 	$(hide) $(ACP) $(PRIVATE_OTA_TOOLS) $(zip_root)/OTA/bin/
-	@# Files that don't end up in any images, but are necessary to
+	@# Files that do not end up in any images, but are necessary to
 	@# build them.
 	$(hide) mkdir -p $(zip_root)/META
 	$(hide) $(ACP) $(APKCERTS_FILE) $(zip_root)/META/apkcerts.txt
+	$(hide)	echo "$(PRODUCT_OTA_PUBLIC_KEYS)" > $(zip_root)/META/otakeys.txt
+	$(hide) echo "$(PRIVATE_RECOVERY_API_VERSION)" > $(zip_root)/META/recovery-api-version.txt
 	@# Zip everything up, preserving symlinks
 	$(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
 
 target-files-package: $(BUILT_TARGET_FILES_PACKAGE)
 
 # -----------------------------------------------------------------
+# OTA update package
+
+ifneq ($(TARGET_SIMULATOR),true)
+ifneq ($(TARGET_PRODUCT),sdk)
+
+name := $(TARGET_PRODUCT)
+ifeq ($(TARGET_BUILD_TYPE),debug)
+  name := $(name)_debug
+endif
+name := $(name)-ota-$(FILE_NAME_TAG)
+
+INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
+
+$(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) \
+           -b $(TARGET_DEVICE_DIR)/BoardConfig.mk \
+           -k $(KEY_CERT_PAIR) \
+           $(BUILT_TARGET_FILES_PACKAGE) $@
+
+.PHONY: otapackage
+otapackage: $(INTERNAL_OTA_PACKAGE_TARGET)
+
+endif    # TARGET_PRODUCT != sdk
+endif    # TARGET_SIMULATOR != true
+
+# -----------------------------------------------------------------
 # installed file list
 # Depending on $(INSTALLED_SYSTEMIMAGE) ensures that it
 # gets the DexOpt one if we're doing that.
@@ -1009,14 +1013,8 @@
 # -----------------------------------------------------------------
 # The update package
 
-INTERNAL_UPDATE_PACKAGE_FILES += \
-		$(INSTALLED_BOOTIMAGE_TARGET) \
-		$(INSTALLED_RECOVERYIMAGE_TARGET) \
-		$(INSTALLED_SYSTEMIMAGE) \
-		$(INSTALLED_USERDATAIMAGE_TARGET) \
-		$(INSTALLED_ANDROID_INFO_TXT_TARGET)
-
-ifneq ($(strip $(INTERNAL_UPDATE_PACKAGE_FILES)),)
+ifneq ($(TARGET_SIMULATOR),true)
+ifneq ($(TARGET_PRODUCT),sdk)
 
 name := $(TARGET_PRODUCT)
 ifeq ($(TARGET_BUILD_TYPE),debug)
@@ -1026,14 +1024,27 @@
 
 INTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 
-$(INTERNAL_UPDATE_PACKAGE_TARGET): $(INTERNAL_UPDATE_PACKAGE_FILES)
-	@echo "Package: $@"
-	$(hide) zip -qj $@ $(INTERNAL_UPDATE_PACKAGE_FILES)
-
+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 :=
+$(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) \
+	   -b $(TARGET_DEVICE_DIR)/BoardConfig.mk \
+	   $(BUILT_TARGET_FILES_PACKAGE) $@
+
+.PHONY: updatepackage
+updatepackage: $(INTERNAL_UPDATE_PACKAGE_TARGET)
+
+endif    # TARGET_PRODUCT != sdk
+endif    # TARGET_SIMULATOR != true
+
 # -----------------------------------------------------------------
 # The emulator package
 
@@ -1116,7 +1127,9 @@
 	$(target_notice_file_txt) \
 	$(tools_notice_file_txt) \
 	$(OUT_DOCS)/offline-sdk-timestamp \
-	$(INTERNAL_UPDATE_PACKAGE_TARGET) \
+	$(INSTALLED_SYSTEMIMAGE) \
+	$(INSTALLED_USERDATAIMAGE_TARGET) \
+	$(INSTALLED_RAMDISK_TARGET) \
 	$(INSTALLED_SDK_BUILD_PROP_TARGET) \
 	$(ATREE_FILES) \
 	$(atree_dir)/sdk.atree \
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 4ee2985..1a55320 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -267,7 +267,6 @@
         JAVA_LIBRARIES,$(lib),$(LOCAL_IS_HOST_MODULE))/javalib.jar)
 
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_INSTALL_DIR := $(dir $(LOCAL_INSTALLED_MODULE))
-$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_INTERMEDIATES_DIR := $(intermediates)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CLASS_INTERMEDIATES_DIR := $(intermediates)/classes
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SOURCE_INTERMEDIATES_DIR := $(intermediates)/src
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_SOURCES := $(all_java_sources)
@@ -351,12 +350,13 @@
 $(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)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_HOST:= $(my_host)
 
+$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_INTERMEDIATES_DIR:= $(intermediates)
+
 # Tell the module and all of its sub-modules who it is.
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_MODULE:= $(LOCAL_MODULE)
 
diff --git a/core/binary.mk b/core/binary.mk
index 0f35d3f..4413d47 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -47,11 +47,11 @@
 arm_objects_mode := $(if $(LOCAL_ARM_MODE),$(LOCAL_ARM_MODE),arm)
 normal_objects_mode := $(if $(LOCAL_ARM_MODE),$(LOCAL_ARM_MODE),thumb)
 
-# Read the values from something like TARGET_arm_release_CFLAGS or
-# TARGET_thumb_debug_CFLAGS.  HOST_(arm|thumb)_(release|debug)_CFLAGS
-# values aren't actually used (although they are usually empty).
-arm_objects_cflags := $($(my_prefix)$(arm_objects_mode)_$($(my_prefix)BUILD_TYPE)_CFLAGS)
-normal_objects_cflags := $($(my_prefix)$(normal_objects_mode)_$($(my_prefix)BUILD_TYPE)_CFLAGS)
+# Read the values from something like TARGET_arm_CFLAGS or
+# TARGET_thumb_CFLAGS.  HOST_(arm|thumb)_CFLAGS values aren't
+# actually used (although they are usually empty).
+arm_objects_cflags := $($(my_prefix)$(arm_objects_mode)_CFLAGS)
+normal_objects_cflags := $($(my_prefix)$(normal_objects_mode)_CFLAGS)
 
 ###########################################################
 ## Define per-module debugging flags.  Users can turn on
@@ -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,42 @@
 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
+###########################################################
+
+objc_sources := $(filter %.m,$(LOCAL_SRC_FILES))
+objc_objects := $(addprefix $(intermediates)/,$(objc_sources:.m=.o))
+
+ifneq ($(strip $(objc_objects)),)
+$(objc_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.m $(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+	$(transform-$(PRIVATE_HOST)m-to-o)
+-include $(objc_objects:%.o=%.P)
+endif
+
+###########################################################
 ## AS: Compile .S files to .o.
 ###########################################################
 
@@ -219,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
@@ -228,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
@@ -248,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 cb18bc4..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 := CUPCAKE
+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..a1cca79
--- /dev/null
+++ b/core/combo/arch/arm/armv7-a.mk
@@ -0,0 +1,17 @@
+# 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
+
+# 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=vfp
diff --git a/core/combo/linux-arm.mk b/core/combo/linux-arm.mk
index d2e0672..ec786c0 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)
@@ -73,40 +49,39 @@
 
 $(combo_target)NO_UNDEFINED_LDFLAGS := -Wl,--no-undefined
 
-TARGET_arm_release_CFLAGS :=    -O2 \
-                                -fomit-frame-pointer \
-                                -fstrict-aliasing    \
-                                -funswitch-loops     \
-                                -finline-limit=300
+TARGET_arm_CFLAGS :=    -O2 \
+                        -fomit-frame-pointer \
+                        -fstrict-aliasing    \
+                        -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.
 ifeq ($(ARCH_ARM_HAVE_THUMB_SUPPORT),true)
-TARGET_thumb_release_CFLAGS :=  -mthumb \
-                                -Os \
-                                -fomit-frame-pointer \
-                                -fno-strict-aliasing \
-                                -finline-limit=64
+TARGET_thumb_CFLAGS :=  -mthumb \
+                        -Os \
+                        -fomit-frame-pointer \
+                        -fno-strict-aliasing \
+                        -finline-limit=64
 else
-TARGET_thumb_release_CFLAGS := $(TARGET_arm_release_CFLAGS)
+TARGET_thumb_CFLAGS := $(TARGET_arm_CFLAGS)
 endif
 
-# When building for debug, compile everything as arm.
-TARGET_arm_debug_CFLAGS := $(TARGET_arm_release_CFLAGS) -fno-omit-frame-pointer -fno-strict-aliasing
-TARGET_thumb_debug_CFLAGS := $(TARGET_thumb_release_CFLAGS) -marm -fno-omit-frame-pointer
-
-# NOTE: if you try to build a debug build with thumb, several
+# Set FORCE_ARM_DEBUGGING to "true" in your buildspec.mk
+# or in your environment to force a full arm build, even for
+# files that are normally built as thumb; this can make
+# gdb debugging easier.  Don't forget to do a clean build.
+#
+# NOTE: if you try to build a -O0 build with thumb, several
 # of the libraries (libpv, libwebcore, libkjs) need to be built
 # 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.
-
-## As hopefully a temporary hack,
-## use this to force a full ARM build (for easier debugging in gdb)
-## (don't forget to do a clean build)
-##TARGET_arm_release_CFLAGS := $(TARGET_arm_release_CFLAGS) -fno-omit-frame-pointer
-##TARGET_thumb_release_CFLAGS := $(TARGET_thumb_release_CFLAGS) -marm -fno-omit-frame-pointer
+ifeq ($(FORCE_ARM_DEBUGGING),true)
+  TARGET_arm_CFLAGS += -fno-omit-frame-pointer -fno-strict-aliasing
+  TARGET_thumb_CFLAGS += -marm -fno-omit-frame-pointer
+endif
 
 android_config_h := $(call select-android-config-h,linux-arm)
 arch_include_dir := $(dir $(android_config_h))
@@ -117,7 +92,7 @@
 			-funwind-tables \
 			-fstack-protector \
 			-fno-short-enums \
-			$(arch_version_cflags) \
+			$(arch_variant_cflags) \
 			-include $(android_config_h) \
 			-I $(arch_include_dir)
 
@@ -151,7 +126,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)
diff --git a/core/combo/select.mk b/core/combo/select.mk
index c54da22..273b660 100644
--- a/core/combo/select.mk
+++ b/core/combo/select.mk
@@ -7,7 +7,6 @@
 #   $(combo_target)OS -- standard name for this host (LINUX, DARWIN, etc.)
 #   $(combo_target)ARCH -- standard name for process architecture (powerpc, x86, etc.)
 #   $(combo_target)GLOBAL_CFLAGS -- C compiler flags to use for everything
-#   $(combo_target)DEBUG_CFLAGS -- additional C compiler flags for debug builds
 #   $(combo_target)RELEASE_CFLAGS -- additional C compiler flags for release builds
 #   $(combo_target)GLOBAL_ARFLAGS -- flags to use for static linking everything
 #   $(combo_target)SHLIB_SUFFIX -- suffix of shared libraries
@@ -39,7 +38,6 @@
 
 # These flags might (will) be overridden by the target makefiles
 $(combo_target)GLOBAL_CFLAGS := -fno-exceptions -Wno-multichar
-$(combo_target)DEBUG_CFLAGS := -O0 -g
 $(combo_target)RELEASE_CFLAGS := -O2 -g -fno-strict-aliasing
 $(combo_target)GLOBAL_ARFLAGS := crs
 
diff --git a/core/config.mk b/core/config.mk
index 90a40a7..6b2f08d 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -76,11 +76,9 @@
 
 # These can be changed to modify both host and device modules.
 COMMON_GLOBAL_CFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused
-COMMON_DEBUG_CFLAGS:=
 COMMON_RELEASE_CFLAGS:= -DNDEBUG -UDEBUG
 
 COMMON_GLOBAL_CPPFLAGS:=
-COMMON_DEBUG_CPPFLAGS:=
 COMMON_RELEASE_CPPFLAGS:=
 
 # Set the extensions used for various packages
@@ -91,6 +89,12 @@
 # list of flags to turn specific warnings in to errors
 TARGET_ERROR_FLAGS := -Werror=return-type
 
+# TODO: do symbol compression
+TARGET_COMPRESS_MODULE_SYMBOLS := false
+
+# Default is to prelink modules.
+TARGET_PRELINK_MODULE := true
+
 # ###############################################################
 # Include sub-configuration files
 # ###############################################################
@@ -107,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
@@ -158,6 +188,7 @@
 ICUDATA := $(HOST_OUT_EXECUTABLES)/icudata$(HOST_EXECUTABLE_SUFFIX)
 SIGNAPK_JAR := $(HOST_OUT_JAVA_LIBRARIES)/signapk$(COMMON_JAVA_PACKAGE_SUFFIX)
 MKBOOTFS := $(HOST_OUT_EXECUTABLES)/mkbootfs$(HOST_EXECUTABLE_SUFFIX)
+MINIGZIP := $(HOST_OUT_EXECUTABLES)/minigzip$(HOST_EXECUTABLE_SUFFIX)
 MKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX)
 MKYAFFS2 := $(HOST_OUT_EXECUTABLES)/mkyaffs2image$(HOST_EXECUTABLE_SUFFIX)
 APICHECK := $(HOST_OUT_EXECUTABLES)/apicheck$(HOST_EXECUTABLE_SUFFIX)
@@ -227,19 +258,15 @@
 # ###############################################################
 
 HOST_GLOBAL_CFLAGS += $(COMMON_GLOBAL_CFLAGS)
-HOST_DEBUG_CFLAGS += $(COMMON_DEBUG_CFLAGS)
 HOST_RELEASE_CFLAGS += $(COMMON_RELEASE_CFLAGS)
 
 HOST_GLOBAL_CPPFLAGS += $(COMMON_GLOBAL_CPPFLAGS)
-HOST_DEBUG_CPPFLAGS += $(COMMON_DEBUG_CPPFLAGS)
 HOST_RELEASE_CPPFLAGS += $(COMMON_RELEASE_CPPFLAGS)
 
 TARGET_GLOBAL_CFLAGS += $(COMMON_GLOBAL_CFLAGS)
-TARGET_DEBUG_CFLAGS += $(COMMON_DEBUG_CFLAGS)
 TARGET_RELEASE_CFLAGS += $(COMMON_RELEASE_CFLAGS)
 
 TARGET_GLOBAL_CPPFLAGS += $(COMMON_GLOBAL_CPPFLAGS)
-TARGET_DEBUG_CPPFLAGS += $(COMMON_DEBUG_CPPFLAGS)
 TARGET_RELEASE_CPPFLAGS += $(COMMON_RELEASE_CPPFLAGS)
 
 HOST_GLOBAL_LD_DIRS += -L$(HOST_OUT_INTERMEDIATE_LIBRARIES)
@@ -250,7 +277,7 @@
 
 # Many host compilers don't support these flags, so we have to make
 # sure to only specify them for the target compilers checked in to
-# the source tree. The simulator uses the target flags but the
+# the source tree. The simulator passes the target flags to the
 # host compiler, so only set them for the target when the target
 # is not the simulator.
 ifneq ($(TARGET_SIMULATOR),true)
@@ -258,25 +285,11 @@
 TARGET_GLOBAL_CPPFLAGS += $(TARGET_ERROR_FLAGS)
 endif
 
-ifeq ($(HOST_BUILD_TYPE),release)
-HOST_GLOBAL_CFLAGS+= $(HOST_RELEASE_CFLAGS)
-HOST_GLOBAL_CPPFLAGS+= $(HOST_RELEASE_CPPFLAGS)
-else
-HOST_GLOBAL_CFLAGS+= $(HOST_DEBUG_CFLAGS)
-HOST_GLOBAL_CPPFLAGS+= $(HOST_DEBUG_CPPFLAGS)
-endif
+HOST_GLOBAL_CFLAGS += $(HOST_RELEASE_CFLAGS)
+HOST_GLOBAL_CPPFLAGS += $(HOST_RELEASE_CPPFLAGS)
 
-ifeq ($(TARGET_BUILD_TYPE),release)
-TARGET_GLOBAL_CFLAGS+= $(TARGET_RELEASE_CFLAGS)
-TARGET_GLOBAL_CPPFLAGS+= $(TARGET_RELEASE_CPPFLAGS)
-else
-TARGET_GLOBAL_CFLAGS+= $(TARGET_DEBUG_CFLAGS)
-TARGET_GLOBAL_CPPFLAGS+= $(TARGET_DEBUG_CPPFLAGS)
-endif
-
-# TODO: do symbol compression
-TARGET_COMPRESS_MODULE_SYMBOLS := false
-TARGET_PRELINK_MODULE := true
+TARGET_GLOBAL_CFLAGS += $(TARGET_RELEASE_CFLAGS)
+TARGET_GLOBAL_CPPFLAGS += $(TARGET_RELEASE_CPPFLAGS)
 
 PREBUILT_IS_PRESENT := $(if $(wildcard prebuilt/Android.mk),true)
 
@@ -290,7 +303,7 @@
 # The 'current' version is whatever this source tree is.  Once the apicheck
 # tool can generate the stubs from the xml files, we'll use that to be
 # able to build back-versions.  In the meantime, 'current' is the only
-# one supported.  
+# one supported.
 #
 # sgrax     is the opposite of xargs.  It takes the list of args and puts them
 #           on each line for sort to process.
diff --git a/core/definitions.mk b/core/definitions.mk
index 17ec646..69c7aae 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -794,6 +794,22 @@
 endef
 
 ###########################################################
+## Commands for running gcc to compile an Objective-C file
+## This should never happen for target builds but this
+## will error at build time.
+###########################################################
+
+define transform-m-to-o-no-deps
+@echo "target ObjC: $(PRIVATE_MODULE) <= $<"
+$(call transform-c-or-s-to-o-no-deps)
+endef
+
+define transform-m-to-o
+$(transform-m-to-o-no-deps)
+$(hide) $(transform-d-to-p)
+endef
+
+###########################################################
 ## Commands for running gcc to compile a host C++ file
 ###########################################################
 
@@ -871,15 +887,45 @@
 endef
 
 ###########################################################
+## Commands for running gcc to compile a host Objective-C file
+###########################################################
+
+define transform-host-m-to-o-no-deps
+@echo "host ObjC: $(PRIVATE_MODULE) <= $<"
+$(call transform-host-c-or-s-to-o-no-deps)
+endef
+
+define tranform-host-m-to-o
+$(transform-host-m-to-o-no-deps)
+$(transform-d-to-p)
+endef
+
+###########################################################
 ## Commands for running ar
 ###########################################################
 
+define extract-and-include-whole-static-libs
+$(foreach lib,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES), \
+	@echo "preparing StaticLib: $(PRIVATE_MODULE) [including $(lib)]"; \
+	ldir=$(PRIVATE_INTERMEDIATES_DIR)/WHOLE/$(basename $(notdir $(lib)))_objs;\
+	rm -rf $$ldir; \
+	mkdir -p $$ldir; \
+	filelist=; \
+	for f in `$(TARGET_AR) t $(lib)`; do \
+	    $(TARGET_AR) p $(lib) $$f > $$ldir/$$f; \
+	    filelist="$$filelist $$ldir/$$f"; \
+	done ; \
+	$(TARGET_AR) $(TARGET_GLOBAL_ARFLAGS) $(PRIVATE_ARFLAGS) $@ $$filelist;\
+)
+endef
+
 # Explicitly delete the archive first so that ar doesn't
 # try to add to an existing archive.
 define transform-o-to-static-lib
 @mkdir -p $(dir $@)
-@echo "target StaticLib: $(PRIVATE_MODULE) ($@)"
 @rm -f $@
+$(extract-and-include-whole-static-libs)
+@echo "target StaticLib: $(PRIVATE_MODULE) ($@)"
 $(hide) $(TARGET_AR) $(TARGET_GLOBAL_ARFLAGS) $(PRIVATE_ARFLAGS) $@ $^
 endef
 
@@ -1122,7 +1168,11 @@
     $(addprefix -P , $(PRIVATE_RESOURCE_PUBLICS_OUTPUT)) \
     $(addprefix -S , $(PRIVATE_RESOURCE_DIR)) \
     $(addprefix -A , $(PRIVATE_ASSET_DIR)) \
-    $(addprefix -I , $(PRIVATE_AAPT_INCLUDES))
+    $(addprefix -I , $(PRIVATE_AAPT_INCLUDES)) \
+    $(addprefix --min-sdk-version , $(DEFAULT_APP_TARGET_SDK)) \
+    $(addprefix --target-sdk-version , $(DEFAULT_APP_TARGET_SDK)) \
+    $(addprefix --version-code , $(PLATFORM_SDK_VERSION)) \
+    $(addprefix --version-name , $(PLATFORM_VERSION))
 endef
 
 ifeq ($(HOST_OS),windows)
@@ -1174,7 +1224,7 @@
       echo Missing file $$f; \
       exit 1; \
     fi; \
-    unzip -q $$f -d $(2); \
+    unzip -qo $$f -d $(2); \
     (cd $(2) && rm -rf META-INF); \
   done
 endef
@@ -1189,21 +1239,21 @@
 $(hide) mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR)
 $(call unzip-jar-files,$(PRIVATE_STATIC_JAVA_LIBRARIES), \
     $(PRIVATE_CLASS_INTERMEDIATES_DIR))
-$(call dump-words-to-file,$(PRIVATE_JAVA_SOURCES),$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list)
+$(call dump-words-to-file,$(PRIVATE_JAVA_SOURCES),$(PRIVATE_INTERMEDIATES_DIR)/java-source-list)
 $(hide) if [ -d "$(PRIVATE_SOURCE_INTERMEDIATES_DIR)" ]; then \
-	    find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list; \
+	    find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(PRIVATE_INTERMEDIATES_DIR)/java-source-list; \
 fi
-$(hide) tr ' ' '\n' < $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list \
-    | sort -u > $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq
+$(hide) tr ' ' '\n' < $(PRIVATE_INTERMEDIATES_DIR)/java-source-list \
+    | sort -u > $(PRIVATE_INTERMEDIATES_DIR)/java-source-list-uniq
 $(hide) $(TARGET_JAVAC) -encoding ascii $(PRIVATE_BOOTCLASSPATH) \
     $(addprefix -classpath ,$(strip \
         $(call normalize-path-list,$(PRIVATE_ALL_JAVA_LIBRARIES)))) \
     $(strip $(PRIVATE_JAVAC_DEBUG_FLAGS)) $(xlint_unchecked) \
     -extdirs "" -d $(PRIVATE_CLASS_INTERMEDIATES_DIR) \
-    \@$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq \
+    \@$(PRIVATE_INTERMEDIATES_DIR)/java-source-list-uniq \
     || ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 )
-$(hide) rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list
-$(hide) rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq
+$(hide) rm -f $(PRIVATE_INTERMEDIATES_DIR)/java-source-list
+$(hide) rm -f $(PRIVATE_INTERMEDIATES_DIR)/java-source-list-uniq
 $(hide) mkdir -p $(dir $@)
 $(hide) jar $(if $(strip $(PRIVATE_JAR_MANIFEST)),-cfm,-cf) \
     $@ $(PRIVATE_JAR_MANIFEST) -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .
@@ -1250,6 +1300,9 @@
 #      A list of dynamic and static parameters;  build layers for
 #      dynamic params that lay over the static ones.
 #TODO: update the manifest to point to the package file
+#Note that the version numbers are given to aapt as simple default
+#values; applications can override these by explicitly stating
+#them in their manifest.
 define add-assets-to-package
 $(hide) $(AAPT) package -z -u $(PRIVATE_AAPT_FLAGS) \
     $(addprefix -c , $(PRODUCT_AAPT_CONFIG)) \
@@ -1257,6 +1310,10 @@
     $(addprefix -S , $(PRIVATE_RESOURCE_DIR)) \
     $(addprefix -A , $(PRIVATE_ASSET_DIR)) \
     $(addprefix -I , $(PRIVATE_AAPT_INCLUDES)) \
+    $(addprefix --min-sdk-version , $(DEFAULT_APP_TARGET_SDK)) \
+    $(addprefix --target-sdk-version , $(DEFAULT_APP_TARGET_SDK)) \
+    $(addprefix --version-code , $(PLATFORM_SDK_VERSION)) \
+    $(addprefix --version-name , $(PLATFORM_VERSION)) \
     -F $@
 endef
 
@@ -1328,14 +1385,16 @@
     $(PRIVATE_CLASS_INTERMEDIATES_DIR))
 $(call dump-words-to-file,$(sort\
 	$(PRIVATE_JAVA_SOURCES)),\
-	$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq)
+	$(PRIVATE_INTERMEDIATES_DIR)/java-source-list-uniq)
 $(hide) $(HOST_JAVAC) -encoding ascii -g \
 	$(xlint_unchecked) \
 	$(addprefix -classpath ,$(strip \
 		$(call normalize-path-list,$(PRIVATE_ALL_JAVA_LIBRARIES)))) \
 	-extdirs "" -d $(PRIVATE_CLASS_INTERMEDIATES_DIR)\
-        \@$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq || \
+        \@$(PRIVATE_INTERMEDIATES_DIR)/java-source-list-uniq || \
 	( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 )
+$(hide) rm -f $(PRIVATE_INTERMEDIATES_DIR)/java-source-list
+$(hide) rm -f $(PRIVATE_INTERMEDIATES_DIR)/java-source-list-uniq
 $(hide) jar $(if $(strip $(PRIVATE_JAR_MANIFEST)),-cfm,-cf) \
     $@ $(PRIVATE_JAR_MANIFEST) $(PRIVATE_EXTRA_JAR_ARGS) \
     -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .
@@ -1510,6 +1569,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 ba93549..0ee7cfc 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -7,6 +7,9 @@
 #     OUT_DIR is also set to "out" if it's not already set.
 #         this allows you to set it to somewhere else if you like
 
+# Set up version information.
+include $(BUILD_SYSTEM)/version_defaults.mk
+
 # ---------------------------------------------------------------
 # If you update the build system such that the environment setup
 # or buildspec.mk need to be updated, increment this number, and
@@ -267,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)
@@ -319,6 +322,8 @@
 
 ifneq ($(PRINT_BUILD_CONFIG),)
 $(info ============================================)
+$(info   PLATFORM_VERSION_CODENAME=$(PLATFORM_VERSION_CODENAME))
+$(info   PLATFORM_VERSION=$(PLATFORM_VERSION))
 $(info   TARGET_PRODUCT=$(TARGET_PRODUCT))
 $(info   TARGET_BUILD_VARIANT=$(TARGET_BUILD_VARIANT))
 $(info   TARGET_SIMULATOR=$(TARGET_SIMULATOR))
@@ -330,5 +335,3 @@
 $(info   BUILD_ID=$(BUILD_ID))
 $(info ============================================)
 endif
-
-
diff --git a/core/java.mk b/core/java.mk
index 9150a5c..5a434c4 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -188,9 +188,8 @@
 $(LOCAL_MODULE)-findbugs : $(findbugs_html)
 $(findbugs_html) : $(findbugs_xml)
 	@mkdir -p $(dir $@)
-	@echo UnionBugs: $@
-	$(hide) prebuilt/common/findbugs/bin/unionBugs $(PRIVATE_XML_FILE) \
-	| prebuilt/common/findbugs/bin/convertXmlToText -html:fancy.xsl \
+	@echo ConvertXmlToText: $@
+	$(hide) prebuilt/common/findbugs/bin/convertXmlToText -html:fancy.xsl $(PRIVATE_XML_FILE) \
 	> $@
 
 $(LOCAL_MODULE)-findbugs : $(findbugs_html)
diff --git a/core/main.mk b/core/main.mk
index e846290..aa7fcdb 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -85,8 +85,43 @@
 $(error Directory names containing spaces not supported)
 endif
 
-# Set up version information.
-include $(BUILD_SYSTEM)/version_defaults.mk
+
+# The windows build server currently uses 1.6.  This will be fixed.
+ifneq ($(HOST_OS),windows)
+
+# Check for the correct version of java
+java_version := $(shell java -version 2>&1 | head -n 1 | grep '[ "]1\.5[\. "$$]')
+ifeq ($(strip $(java_version)),)
+$(info ************************************************************)
+$(info You are attempting to build with the incorrect version)
+$(info of java.)
+$(info $(space))
+$(info Your version is: $(shell java -version 2>&1 | head -n 1).)
+$(info The correct version is: 1.5.)
+$(info $(space))
+$(info Please follow the machine setup instructions at)
+$(info $(space)$(space)$(space)$(space)http://source.android.com/download)
+$(info ************************************************************)
+$(error stop)
+endif
+
+# Check for the correct version of javac
+javac_version := $(shell javac -version 2>&1 | head -n 1 | grep '[ "]1\.5[\. "$$]')
+ifeq ($(strip $(javac_version)),)
+$(info ************************************************************)
+$(info You are attempting to build with the incorrect version)
+$(info of javac.)
+$(info $(space))
+$(info Your version is: $(shell javac -version 2>&1 | head -n 1).)
+$(info The correct version is: 1.5.)
+$(info $(space))
+$(info Please follow the machine setup instructions at)
+$(info $(space)$(space)$(space)$(space)http://source.android.com/download)
+$(info ************************************************************)
+$(error stop)
+endif
+
+endif # windows
 
 # These are the modifier targets that don't do anything themselves, but
 # change the behavior of the build.
@@ -210,6 +245,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 += \
@@ -229,7 +274,7 @@
 # If we're on an eng or tests build, but not on the sdk, and we have
 # a better one, use that instead.
 ifneq ($(filter eng tests,$(TARGET_BUILD_VARIANT)),)
-  ifdef is_sdk_build
+  ifndef is_sdk_build
     apns_to_use := $(wildcard vendor/google/etc/apns-conf.xml)
     ifneq ($(strip $(apns_to_use)),)
       PRODUCT_COPY_FILES := \
@@ -309,7 +354,6 @@
 	dalvik/tools/dmtracedump \
 	dalvik/tools/hprof-conv \
 	development/emulator/mksdcard \
-	development/tools/activitycreator \
 	development/tools/line_endings \
 	development/host \
 	external/expat \
@@ -336,6 +380,7 @@
 	dalvik/dx \
 	dalvik/libcore \
 	development/apps \
+	development/tools/archquery \
 	development/tools/androidprefs \
 	development/tools/apkbuilder \
 	development/tools/jarutils \
@@ -397,28 +442,6 @@
 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))
-
 #
 # Include all of the makefiles in the system
 #
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 13cb80d..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 \
@@ -83,6 +83,8 @@
 	    sax \
 	    telephony \
 	    wifi \
+	    vpn \
+	    keystore \
 	 )
 
 #
diff --git a/core/prelink-linux-arm.map b/core/prelink-linux-arm.map
index c32566f..44e5d06 100644
--- a/core/prelink-linux-arm.map
+++ b/core/prelink-linux-arm.map
@@ -21,12 +21,13 @@
 libevent.so             0xAF800000
 libssl.so               0xAF700000
 libcrypto.so            0xAF500000
+libsysutils.so          0xAF400000
 
 # bluetooth
 liba2dp.so              0xAEE00000
 audio.so                0xAED00000
 input.so                0xAEC00000
-libhcid.so              0xAEB00000
+libbluetoothd.so        0xAEB00000
 libbluedroid.so         0xAEA00000
 libbluetooth.so         0xAE900000
 libdbus.so              0xAE800000
@@ -51,6 +52,7 @@
 libpixelflinger.so      0xACF00000
 libcorecg.so            0xACE00000
 libsurfaceflinger.so    0xACD00000
+libGLES_android.so      0xACC80000
 libagl.so               0xACC00000
 
 libGLESv1_CM.so         0xACB00000
@@ -59,6 +61,8 @@
 libOpenVGU_CM.so        0xAC800000
 libEGL.so               0xAC700000
 
+libacc.so               0xAC600000
+
 libexif.so              0xAC500000
 libui.so                0xAC400000
 libsgl.so               0xAC000000
@@ -79,6 +83,7 @@
 libsqlite.so            0xAAC00000
 libexpat.so             0xAAB00000
 libwebcore.so           0xAA000000
+libbinder.so            0xA9D80000
 libutils.so             0xA9D00000
 libcameraservice.so     0xA9C80000
 libhardware.so          0xA9C70000
@@ -90,11 +95,27 @@
 libcamera.so            0xA9680000
 libqcamera.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
@@ -104,6 +125,10 @@
 libOmxVidEnc.so               0xA6F60000
 libopencorehw.so              0xA6F50000
 
+# pv libraries
+libopencore_common.so         0xA6000000
+libqcomm_omx.so               0xA5A00000
+
 # libraries for specific apps or temporary libraries
 libcam_ipl.so           0x9F000000
 libwbxml.so             0x9E800000
@@ -128,3 +153,4 @@
 librpc.so               0x9A400000
 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 64488d8..3237801 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -110,11 +110,11 @@
   TARGET_BUILD_VARIANT := $(word 2,$(product_goals))
 
   # The build server wants to do make PRODUCT-dream-installclean
-  # which really means TARGET_PRODUCT=dream make installclean.  
+  # which really means TARGET_PRODUCT=dream make installclean.
   ifneq ($(filter-out $(INTERNAL_VALID_VARIANTS),$(TARGET_BUILD_VARIANT)),)
 	MAKECMDGOALS := $(MAKECMDGOALS) $(TARGET_BUILD_VARIANT)
 	TARGET_BUILD_VARIANT := eng
-    default_goal_substitution := 
+    default_goal_substitution :=
   else
     default_goal_substitution := $(DEFAULT_GOAL)
   endif
@@ -135,7 +135,7 @@
   #
   # Note that modifying this will not affect the goals that make will
   # attempt to build, but it's important because we inspect this value
-  # in certain situations (like for "make sdk").  
+  # in certain situations (like for "make sdk").
   #
   MAKECMDGOALS := $(patsubst $(goal_name),$(default_goal_substitution),$(MAKECMDGOALS))
 
@@ -185,7 +185,10 @@
 # in PRODUCT_LOCALES, add them to PRODUCT_LOCALES.
 extra_locales := $(filter-out $(PRODUCT_LOCALES),$(CUSTOM_LOCALES))
 ifneq (,$(extra_locales))
-  $(info Adding CUSTOM_LOCALES [$(extra_locales)] to PRODUCT_LOCALES [$(PRODUCT_LOCALES)])
+  ifneq ($(CALLED_FROM_SETUP),true)
+    # Don't spam stdout, because envsetup.sh may be scraping values from it.
+    $(info Adding CUSTOM_LOCALES [$(extra_locales)] to PRODUCT_LOCALES [$(PRODUCT_LOCALES)])
+  endif
   PRODUCT_LOCALES += $(extra_locales)
   extra_locales :=
 endif
@@ -202,7 +205,7 @@
 
 PRODUCT_MODEL := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_MODEL))
 ifndef PRODUCT_MODEL
-  PRODUCT_MODEL := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_NAME)) 
+  PRODUCT_MODEL := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_NAME))
 endif
 
 PRODUCT_MANUFACTURER := \
@@ -211,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))
 
@@ -245,32 +251,19 @@
 	$(ADDITIONAL_BUILD_PROPERTIES) \
 	$(PRODUCT_PROPERTY_OVERRIDES)
 
-# Get the list of OTA public keys for the product.
-OTA_PUBLIC_KEYS := \
-	$(sort \
-	    $(OTA_PUBLIC_KEYS) \
-	    $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_OTA_PUBLIC_KEYS) \
-	 )
-
-# HACK: Not all products define OTA keys yet, and the -user build
-# will fail if no keys are defined.
-# TODO: Let a product opt out of needing OTA keys, and stop defaulting to
-#       the test key as soon as possible.
-ifeq (,$(strip $(OTA_PUBLIC_KEYS)))
-  ifeq (,$(CALLED_FROM_SETUP))
-    $(warning WARNING: adding test OTA key)
-  endif
-  OTA_PUBLIC_KEYS := $(SRC_TARGET_DIR)/product/security/testkey.x509.pem
-endif
+# The OTA key(s) specified by the product config, if any.  The names
+# of these keys are stored in the target-files zip so that post-build
+# signing tools can substitute them for the test key embedded by
+# default.
+PRODUCT_OTA_PUBLIC_KEYS := $(sort \
+    $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_OTA_PUBLIC_KEYS))
 
 # ---------------------------------------------------------------
-# Force the simulator to be the simulator, and make BUILD_TYPE
-# default to debug.
+# Simulator overrides
 ifeq ($(TARGET_PRODUCT),sim)
+  # Tell the build system to turn on some special cases
+  # to deal with the simulator product.
   TARGET_SIMULATOR := true
-  ifeq (,$(strip $(TARGET_BUILD_TYPE)))
-    TARGET_BUILD_TYPE := debug
-  endif
   # dexpreopt doesn't work when building the simulator
   DISABLE_DEXPREOPT := true
 endif
diff --git a/core/static_library.mk b/core/static_library.mk
index 252dfd0..2138e46 100644
--- a/core/static_library.mk
+++ b/core/static_library.mk
@@ -25,5 +25,6 @@
 $(all_objects) : TARGET_GLOBAL_CPPFLAGS := 
 endif
 
+$(LOCAL_BUILT_MODULE): $(built_whole_libraries)
 $(LOCAL_BUILT_MODULE): $(all_objects)
 	$(transform-o-to-static-lib)
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index 890f2cd..eed9beb 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -17,21 +17,22 @@
 
 cts_name := android-cts
 
-CTS_EXECUTABLE := cts
+CTS_EXECUTABLE := startcts
 ifeq ($(HOST_OS),windows)
     CTS_EXECUTABLE_PATH := $(cts_tools_src_dir)/host/etc/cts.bat
 else
-    CTS_EXECUTABLE_PATH := $(HOST_OUT_EXECUTABLES)/$(CTS_EXECUTABLE)
+    CTS_EXECUTABLE_PATH := $(cts_tools_src_dir)/utils/$(CTS_EXECUTABLE)
 endif
 CTS_HOST_JAR := $(HOST_OUT_JAVA_LIBRARIES)/cts.jar
 
+junit_host_jar := $(HOST_OUT_JAVA_LIBRARIES)/junit.jar
+
 CTS_CORE_CASE_LIST := android.core.tests.annotation \
 	android.core.tests.archive \
 	android.core.tests.concurrent \
 	android.core.tests.crypto \
 	android.core.tests.dom \
 	android.core.tests.logging \
-	android.core.tests.luni \
 	android.core.tests.luni.io \
 	android.core.tests.luni.lang \
 	android.core.tests.luni.net \
@@ -45,7 +46,8 @@
 	android.core.tests.sql \
 	android.core.tests.text \
 	android.core.tests.xml \
-	android.core.tests.xnet
+	android.core.tests.xnet \
+	android.core.tests.runner
 
 CTS_CASE_LIST := \
 	DeviceInfoCollector \
@@ -65,11 +67,20 @@
 	CtsWidgetTestCases \
 	CtsNetTestCases \
 	SignatureTest \
+	CtsPerformanceTestCases \
+	CtsPerformance2TestCases \
+	CtsPerformance3TestCases \
+	CtsPerformance4TestCases \
+	CtsPerformance5TestCases \
+	ApiDemos \
+	ApiDemosReferenceTest \
 	$(CTS_CORE_CASE_LIST)
 
 DEFAULT_TEST_PLAN := $(PRIVATE_DIR)/resource/plans
 
-$(cts_dir)/all_cts_files_stamp: $(CTS_CASE_LIST) | $(ACP)
+$(cts_dir)/all_cts_files_stamp: PRIVATE_JUNIT_HOST_JAR := $(junit_host_jar)
+
+$(cts_dir)/all_cts_files_stamp: $(CTS_CASE_LIST) $(junit_host_jar) $(ACP)
 # Make necessary directory for CTS
 	@rm -rf $(PRIVATE_CTS_DIR)
 	@mkdir -p $(TMP_DIR)
@@ -80,13 +91,14 @@
 # Copy executable to CTS directory
 	$(hide) $(ACP) -fp $(CTS_HOST_JAR) $(PRIVATE_DIR)/tools
 	$(hide) $(ACP) -fp $(CTS_EXECUTABLE_PATH) $(PRIVATE_DIR)/tools
+# Copy junit jar
+	$(hide) $(ACP) -fp $(PRIVATE_JUNIT_HOST_JAR) $(PRIVATE_DIR)/tools
 # Change mode of the executables
 	$(hide) chmod ug+rwX $(PRIVATE_DIR)/tools/$(notdir $(CTS_EXECUTABLE_PATH))
 	$(foreach apk,$(CTS_CASE_LIST), \
 			$(call copy-testcase-apk,$(apk)))
-# Copy CTS host config and start script to CTS directory
+# Copy CTS host config to CTS directory
 	$(hide) $(ACP) -fp $(cts_tools_src_dir)/utils/host_config.xml $(PRIVATE_DIR)/repository/
-	$(hide) $(ACP) -fp $(cts_tools_src_dir)/utils/startcts $(PRIVATE_DIR)/tools/
 	$(hide) touch $@
 
 # Generate the test descriptions for the core-tests
@@ -112,7 +124,7 @@
 # build system requires that dependencies use javalib.jar.  If
 # javalib.jar is up-to-date, then classes.jar is as well.  Depending
 # on classes.jar will build the files incorrectly.
-$(cts_dir)/all_cts_core_files_stamp: $(CTS_CORE_CASE_LIST) $(HOST_OUT_JAVA_LIBRARIES)/descGen.jar $(CORE_INTERMEDIATES)/javalib.jar $(TESTS_INTERMEDIATES)/javalib.jar | $(ACP)
+$(cts_dir)/all_cts_core_files_stamp: $(CTS_CORE_CASE_LIST) $(HOST_OUT_JAVA_LIBRARIES)/descGen.jar $(CORE_INTERMEDIATES)/javalib.jar $(TESTS_INTERMEDIATES)/javalib.jar $(cts_dir)/all_cts_files_stamp | $(ACP)
 	$(call generate-core-test-description,$(cts_dir)/$(cts_name)/repository/testcases/android.core.tests.annotation,\
 		cts/tests/core/annotation/AndroidManifest.xml,\
 		tests.annotation.AllTests)
@@ -131,9 +143,6 @@
 	$(call generate-core-test-description,$(cts_dir)/$(cts_name)/repository/testcases/android.core.tests.logging,\
 		cts/tests/core/logging/AndroidManifest.xml,\
 		tests.logging.AllTests)
-	$(call generate-core-test-description,$(cts_dir)/$(cts_name)/repository/testcases/android.core.tests.luni,\
-		cts/tests/core/luni/AndroidManifest.xml,\
-		tests.luni.AllTests)
 	$(call generate-core-test-description,$(cts_dir)/$(cts_name)/repository/testcases/android.core.tests.luni.io,\
 		cts/tests/core/luni-io/AndroidManifest.xml,\
 		tests.luni.AllTestsIo)
diff --git a/core/tasks/localize.mk b/core/tasks/localize.mk
deleted file mode 100644
index 12e7b5c..0000000
--- a/core/tasks/localize.mk
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# 
-# Rules for building the xlb files for export for translation.
-# 
-
-# Gather all of the resource files for the default locale -- that is,
-# all resources in directories called values or values-something, where
-# one of the - separated segments is not two characters long -- those are the
-# language directories, and we don't want those.
-all_resource_files := $(foreach pkg, \
-        $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES), \
-        $(PACKAGES.$(pkg).RESOURCE_FILES))
-values_resource_files := $(shell echo $(all_resource_files) | \
-		tr -s / | \
-		tr " " "\n" | \
-		grep -E "\/values[^/]*/(strings.xml|arrays.xml)$$" | \
-		grep -v -E -e "-[a-zA-Z]{2}[/\-]")
-
-xlb_target := $(PRODUCT_OUT)/strings.xlb
-
-$(xlb_target): $(values_resource_files) | $(LOCALIZE)
-	@echo XLB: $@
-	$(hide) mkdir -p $(dir $@)
-	$(hide) rm -f $@
-	$(hide) $(LOCALIZE) xlb $@ $^
-
-# Add a phony target so typing make xlb is convenient
-.PHONY: xlb
-xlb: $(xlb_target)
-
-# We want this on the build-server builds, but no reason to inflict it on
-# everyone
-$(call dist-for-goals, droid, $(xlb_target))
-
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index 578d779..bbcbb51 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -20,6 +20,8 @@
 # Guarantees that the following are defined:
 #     PLATFORM_VERSION
 #     PLATFORM_SDK_VERSION
+#     PLATFORM_VERSION_CODENAME
+#     DEFAULT_APP_TARGET_SDK
 #     BUILD_ID
 #     BUILD_NUMBER
 #
@@ -39,17 +41,40 @@
   # which is the version that we reveal to the end user.
   # Update this value when the platform version changes (rather
   # than overriding it somewhere else).  Can be an arbitrary string.
-  PLATFORM_VERSION := 1.5
+  PLATFORM_VERSION := Eclair
 endif
 
 ifeq "" "$(PLATFORM_SDK_VERSION)"
   # This is the canonical definition of the SDK version, which defines
-  # the set of APIs and functionality available in the platform.  This is
-  # a single integer, that increases monotonically as updates to the SDK
-  # are released.
+  # the set of APIs and functionality available in the platform.  It
+  # is a single integer that increases monotonically as updates to
+  # the SDK are released.  It should only be incremented when the APIs for
+  # the new release are frozen (so that developers don't write apps against
+  # intermediate builds).  During development, this number remains at the
+  # SDK version the branch is based on and PLATFORM_VERSION_CODENAME holds
+  # the code-name of the new development work.
   PLATFORM_SDK_VERSION := 3
 endif
 
+ifeq "" "$(PLATFORM_VERSION_CODENAME)"
+  # 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)"
+  # This is the default minSdkVersion and targetSdkVersion to use for
+  # all .apks created by the build system.  It can be overridden by explicitly
+  # setting these in the .apk's AndroidManifest.xml.  It is either the code
+  # name of the development build or, if this is a release build, the official
+  # SDK version of this release.
+  ifeq "REL" "$(PLATFORM_VERSION_CODENAME)"
+    DEFAULT_APP_TARGET_SDK := $(PLATFORM_SDK_VERSION)
+  else
+    DEFAULT_APP_TARGET_SDK := $(PLATFORM_VERSION_CODENAME)
+  endif
+endif
+
 ifeq "" "$(BUILD_ID)"
   # Used to signify special builds.  E.g., branches and/or releases,
   # like "M5-RC7".  Can be an arbitrary string, but must be a single
diff --git a/envsetup.sh b/envsetup.sh
index ad8e2ff..d797b7c 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
@@ -289,30 +289,6 @@
 #
 function chooseproduct()
 {
-    # Find the makefiles that must exist for a product.
-    # Send stderr to /dev/null in case partner isn't present.
-    local -a choices
-    choices=(`/bin/ls build/target/board/*/BoardConfig.mk vendor/*/*/BoardConfig.mk 2> /dev/null`)
-
-    local choice
-    local -a prodlist
-    for choice in ${choices[@]}
-    do
-        # The product name is the name of the directory containing
-        # the makefile we found, above.
-        prodlist=(${prodlist[@]} `dirname ${choice} | xargs basename`)
-    done
-
-    local index=1
-    local p
-    echo "Product choices are:"
-    for p in ${prodlist[@]}
-    do
-        echo "     $index. $p"
-        let "index = $index + 1"
-    done
-
-
     if [ "x$TARGET_PRODUCT" != x ] ; then
         default_value=$TARGET_PRODUCT
     else
@@ -327,8 +303,7 @@
     local ANSWER
     while [ -z "$TARGET_PRODUCT" ]
     do
-        echo "You can also type the name of a product if you know it."
-        echo -n "Which would you like? [$default_value] "
+        echo -n "Which product would you like? [$default_value] "
         if [ -z "$1" ] ; then
             read ANSWER
         else
@@ -338,13 +313,6 @@
 
         if [ -z "$ANSWER" ] ; then
             export TARGET_PRODUCT=$default_value
-        elif (echo -n $ANSWER | grep -q -e "^[0-9][0-9]*$") ; then
-            local poo=`echo -n $ANSWER`
-            if [ $poo -le ${#prodlist[@]} ] ; then
-                export TARGET_PRODUCT=${prodlist[$(($ANSWER-$_arrayoffset))]}
-            else
-                echo "** Bad product selection: $ANSWER"
-            fi
         else
             if check_product $ANSWER
             then
@@ -666,6 +634,7 @@
                     ARGS="$ARGS showcommands"
                 else
                     echo "No Android.mk in $DIR."
+                    return 1
                 fi
             fi
         done
@@ -976,18 +945,14 @@
         echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
         return
     fi
-    (cd "$T" && development/tools/runtest $@)
+    (cd "$T" && development/testrunner/runtest.py $@)
 }
 
-# simple shortcut to the runtest.py command
+# TODO: Remove this some time after 1 June 2009
 function runtest_py()
 {
-    T=$(gettop)
-    if [ ! "$T" ]; then
-        echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
-        return
-    fi
-    (cd "$T" && development/testrunner/runtest.py $@)
+    echo "runtest_py is obsolete; use runtest instead" >&2
+    return 1
 }
 
 function godir () {
@@ -1045,7 +1010,7 @@
 unset _xarray
 
 # Execute the contents of any vendorsetup.sh files we can find.
-for f in `/bin/ls vendor/*/vendorsetup.sh 2> /dev/null`
+for f in `/bin/ls vendor/*/vendorsetup.sh vendor/*/build/vendorsetup.sh 2> /dev/null`
 do
     echo "including $f"
     . $f
diff --git a/libs/host/Android.mk b/libs/host/Android.mk
index 81f2cc5..d02e4b2 100644
--- a/libs/host/Android.mk
+++ b/libs/host/Android.mk
@@ -3,7 +3,6 @@
 
 LOCAL_SRC_FILES:= \
 	CopyFile.c \
-	Directories.cpp \
 	pseudolocalize.cpp
 
 ifeq ($(HOST_OS),cygwin)
diff --git a/libs/host/Directories.cpp b/libs/host/Directories.cpp
deleted file mode 100644
index a34f5b7..0000000
--- a/libs/host/Directories.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include <host/Directories.h>
-#include <utils/String8.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#ifdef HAVE_MS_C_RUNTIME
-#include <direct.h>
-#endif                    
-
-using namespace android;
-using namespace std;
-
-string
-parent_dir(const string& path)
-{
-    return string(String8(path.c_str()).getPathDir().string());
-}
-
-int
-mkdirs(const char* last)
-{
-    String8 dest;
-    const char* s = last-1;
-    int err;
-    do {
-        s++;
-        if (s > last && (*s == '.' || *s == 0)) {
-            String8 part(last, s-last);
-            dest.appendPath(part);
-#ifdef HAVE_MS_C_RUNTIME
-            err = _mkdir(dest.string());
-#else                    
-            err = mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
-#endif                    
-            if (err != 0) {
-                return err;
-            }
-            last = s+1;
-        }
-    } while (*s);
-    return 0;
-}
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 a874742..2b72d01 100644
--- a/target/board/generic/BoardConfig.mk
+++ b/target/board/generic/BoardConfig.mk
@@ -1,11 +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/board/sim/BoardConfig.mk b/target/board/sim/BoardConfig.mk
index 92679d9..491b30f 100644
--- a/target/board/sim/BoardConfig.mk
+++ b/target/board/sim/BoardConfig.mk
@@ -17,6 +17,9 @@
 # Don't bother with a kernel
 TARGET_NO_KERNEL := true
 
+# The simulator does not support native code at all
+TARGET_CPU_ABI := none
+
 #the simulator partially emulates the original HTC /dev/eac audio interface
 HAVE_HTC_AUDIO_DRIVER := true
 BOARD_USES_GENERIC_AUDIO := true
diff --git a/target/product/core.mk b/target/product/core.mk
index d79b1e1..8cf6b24 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 \
@@ -12,13 +13,18 @@
     Launcher \
     HTMLViewer \
     Phone \
+    ApplicationsProvider \
     ContactsProvider \
     DownloadProvider \
     GoogleSearch \
     MediaProvider \
+    PicoTts \
     SettingsProvider \
     TelephonyProvider \
+    TtsService \
+    VpnServices \
     UserDictionaryProvider \
     PackageInstaller \
+    WebSearchProvider \
     Bugreport
 
diff --git a/target/product/generic_with_google.mk b/target/product/generic_with_google.mk
index dddbbb7..57a2b86 100644
--- a/target/product/generic_with_google.mk
+++ b/target/product/generic_with_google.mk
@@ -7,10 +7,12 @@
     GoogleContactsProvider \
     GoogleSubscribedFeedsProvider \
     com.google.android.gtalkservice \
+    com.google.android.datamessaging \
     com.google.android.maps
 
 PRODUCT_COPY_FILES := \
     vendor/google/frameworks/maps/com.google.android.maps.xml:system/etc/permissions/com.google.android.maps.xml \
+    vendor/google/frameworks/datamessaging/com.google.android.datamessaging.xml:system/etc/permissions/com.google.android.datamessaging.xml \
     vendor/google/apps/GTalkService/com.google.android.gtalkservice.xml:system/etc/permissions/com.google.android.gtalkservice.xml
 
 
diff --git a/target/product/min_dev.mk b/target/product/min_dev.mk
index 7d0fbe6..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
@@ -12,6 +13,7 @@
     MediaProvider \
     SettingsProvider \
     PackageInstaller \
+    WebSearchProvider \
     Bugreport \
     Launcher \
     Settings \
diff --git a/target/product/sdk.mk b/target/product/sdk.mk
index 4b24cf5..46be49f 100644
--- a/target/product/sdk.mk
+++ b/target/product/sdk.mk
@@ -3,6 +3,7 @@
 PRODUCT_PACKAGES := \
 	AlarmClock \
 	Camera \
+	Calculator \
 	Development \
 	DrmProvider \
 	Email \
@@ -18,6 +19,11 @@
 	sqlite3 \
 	LatinIME \
 	PinyinIME \
+	OpenWnn \
+	libWnnEngDic \
+	libWnnJpnDic \
+	libWnnZHCNDic \
+	libwnndict \
 	ApiDemos \
 	SoftKeyboard
 
diff --git a/tools/applypatch/Android.mk b/tools/applypatch/Android.mk
index 09f9862..fe317ff 100644
--- a/tools/applypatch/Android.mk
+++ b/tools/applypatch/Android.mk
@@ -12,18 +12,40 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+ifneq ($(TARGET_SIMULATOR),true)
+
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
-ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SRC_FILES := applypatch.c bsdiff.c freecache.c imgpatch.c
+LOCAL_MODULE := libapplypatch
+LOCAL_MODULE_TAGS := eng
+LOCAL_C_INCLUDES += external/bzip2 external/zlib bootable/recovery
+LOCAL_STATIC_LIBRARIES += libmtdutils libmincrypt libbz libz
 
-LOCAL_SRC_FILES := applypatch.c bsdiff.c freecache.c
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := main.c
 LOCAL_MODULE := applypatch
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_TAGS := eng
-LOCAL_C_INCLUDES += external/bzip2
-LOCAL_STATIC_LIBRARIES += libmincrypt libbz libc
+LOCAL_STATIC_LIBRARIES += libapplypatch
+LOCAL_STATIC_LIBRARIES += libmtdutils libmincrypt libbz libz
+LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
 
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := imgdiff.c
+LOCAL_MODULE := imgdiff
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_TAGS := eng
+LOCAL_C_INCLUDES += external/zlib
+LOCAL_STATIC_LIBRARIES += libz
+
+include $(BUILD_HOST_EXECUTABLE)
+
 endif  # !TARGET_SIMULATOR
diff --git a/tools/applypatch/applypatch.c b/tools/applypatch/applypatch.c
index 9954869..185d3de 100644
--- a/tools/applypatch/applypatch.c
+++ b/tools/applypatch/applypatch.c
@@ -25,12 +25,25 @@
 
 #include "mincrypt/sha.h"
 #include "applypatch.h"
+#include "mtdutils/mtdutils.h"
+
+int SaveFileContents(const char* filename, FileContents file);
+int LoadMTDContents(const char* filename, FileContents* file);
+int ParseSha1(const char* str, uint8_t* digest);
+
+static int mtd_partitions_scanned = 0;
 
 // Read a file into memory; store it and its associated metadata in
 // *file.  Return 0 on success.
 int LoadFileContents(const char* filename, FileContents* file) {
   file->data = NULL;
 
+  // A special 'filename' beginning with "MTD:" means to load the
+  // contents of an MTD partition.
+  if (strncmp(filename, "MTD:", 4) == 0) {
+    return LoadMTDContents(filename, file);
+  }
+
   if (stat(filename, &file->st) != 0) {
     fprintf(stderr, "failed to stat \"%s\": %s\n", filename, strerror(errno));
     return -1;
@@ -43,6 +56,7 @@
   if (f == NULL) {
     fprintf(stderr, "failed to open \"%s\": %s\n", filename, strerror(errno));
     free(file->data);
+    file->data = NULL;
     return -1;
   }
 
@@ -51,6 +65,7 @@
     fprintf(stderr, "short read of \"%s\" (%d bytes of %d)\n",
             filename, bytes_read, file->size);
     free(file->data);
+    file->data = NULL;
     return -1;
   }
   fclose(f);
@@ -59,6 +74,182 @@
   return 0;
 }
 
+static size_t* size_array;
+// comparison function for qsort()ing an int array of indexes into
+// size_array[].
+static int compare_size_indices(const void* a, const void* b) {
+  int aa = *(int*)a;
+  int bb = *(int*)b;
+  if (size_array[aa] < size_array[bb]) {
+    return -1;
+  } else if (size_array[aa] > size_array[bb]) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+// Load the contents of an MTD partition into the provided
+// FileContents.  filename should be a string of the form
+// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:...".
+// The smallest size_n bytes for which that prefix of the mtd contents
+// has the corresponding sha1 hash will be loaded.  It is acceptable
+// for a size value to be repeated with different sha1s.  Will return
+// 0 on success.
+//
+// This complexity is needed because if an OTA installation is
+// interrupted, the partition might contain either the source or the
+// target data, which might be of different lengths.  We need to know
+// the length in order to read from MTD (there is no "end-of-file"
+// marker), so the caller must specify the possible lengths and the
+// hash of the data, and we'll do the load expecting to find one of
+// those hashes.
+int LoadMTDContents(const char* filename, FileContents* file) {
+  char* copy = strdup(filename);
+  const char* magic = strtok(copy, ":");
+  if (strcmp(magic, "MTD") != 0) {
+    fprintf(stderr, "LoadMTDContents called with bad filename (%s)\n",
+            filename);
+    return -1;
+  }
+  const char* partition = strtok(NULL, ":");
+
+  int i;
+  int colons = 0;
+  for (i = 0; filename[i] != '\0'; ++i) {
+    if (filename[i] == ':') {
+      ++colons;
+    }
+  }
+  if (colons < 3 || colons%2 == 0) {
+    fprintf(stderr, "LoadMTDContents called with bad filename (%s)\n",
+            filename);
+  }
+
+  int pairs = (colons-1)/2;     // # of (size,sha1) pairs in filename
+  int* index = malloc(pairs * sizeof(int));
+  size_t* size = malloc(pairs * sizeof(size_t));
+  char** sha1sum = malloc(pairs * sizeof(char*));
+
+  for (i = 0; i < pairs; ++i) {
+    const char* size_str = strtok(NULL, ":");
+    size[i] = strtol(size_str, NULL, 10);
+    if (size[i] == 0) {
+      fprintf(stderr, "LoadMTDContents called with bad size (%s)\n", filename);
+      return -1;
+    }
+    sha1sum[i] = strtok(NULL, ":");
+    index[i] = i;
+  }
+
+  // sort the index[] array so it indexes the pairs in order of
+  // increasing size.
+  size_array = size;
+  qsort(index, pairs, sizeof(int), compare_size_indices);
+
+  if (!mtd_partitions_scanned) {
+    mtd_scan_partitions();
+    mtd_partitions_scanned = 1;
+  }
+
+  const MtdPartition* mtd = mtd_find_partition_by_name(partition);
+  if (mtd == NULL) {
+    fprintf(stderr, "mtd partition \"%s\" not found (loading %s)\n",
+            partition, filename);
+    return -1;
+  }
+
+  MtdReadContext* ctx = mtd_read_partition(mtd);
+  if (ctx == NULL) {
+    fprintf(stderr, "failed to initialize read of mtd partition \"%s\"\n",
+            partition);
+    return -1;
+  }
+
+  SHA_CTX sha_ctx;
+  SHA_init(&sha_ctx);
+  uint8_t parsed_sha[SHA_DIGEST_SIZE];
+
+  // allocate enough memory to hold the largest size.
+  file->data = malloc(size[index[pairs-1]]);
+  char* p = (char*)file->data;
+  file->size = 0;                // # bytes read so far
+
+  for (i = 0; i < pairs; ++i) {
+    // Read enough additional bytes to get us up to the next size
+    // (again, we're trying the possibilities in order of increasing
+    // size).
+    size_t next = size[index[i]] - file->size;
+    size_t read = 0;
+    if (next > 0) {
+      read = mtd_read_data(ctx, p, next);
+      if (next != read) {
+        fprintf(stderr, "short read (%d bytes of %d) for partition \"%s\"\n",
+                read, next, partition);
+        free(file->data);
+        file->data = NULL;
+        return -1;
+      }
+      SHA_update(&sha_ctx, p, read);
+      file->size += read;
+    }
+
+    // Duplicate the SHA context and finalize the duplicate so we can
+    // check it against this pair's expected hash.
+    SHA_CTX temp_ctx;
+    memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX));
+    const uint8_t* sha_so_far = SHA_final(&temp_ctx);
+
+    if (ParseSha1(sha1sum[index[i]], parsed_sha) != 0) {
+      fprintf(stderr, "failed to parse sha1 %s in %s\n",
+              sha1sum[index[i]], filename);
+      free(file->data);
+      file->data = NULL;
+      return -1;
+    }
+
+    if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) {
+      // we have a match.  stop reading the partition; we'll return
+      // the data we've read so far.
+      printf("mtd read matched size %d sha %s\n",
+             size[index[i]], sha1sum[index[i]]);
+      break;
+    }
+
+    p += read;
+  }
+
+  mtd_read_close(ctx);
+
+  if (i == pairs) {
+    // Ran off the end of the list of (size,sha1) pairs without
+    // finding a match.
+    fprintf(stderr, "contents of MTD partition \"%s\" didn't match %s\n",
+            partition, filename);
+    free(file->data);
+    file->data = NULL;
+    return -1;
+  }
+
+  const uint8_t* sha_final = SHA_final(&sha_ctx);
+  for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
+    file->sha1[i] = sha_final[i];
+  }
+
+  // Fake some stat() info.
+  file->st.st_mode = 0644;
+  file->st.st_uid = 0;
+  file->st.st_gid = 0;
+
+  free(copy);
+  free(index);
+  free(size);
+  free(sha1sum);
+
+  return 0;
+}
+
+
 // Save the contents of the given FileContents object under the given
 // filename.  Return 0 on success.
 int SaveFileContents(const char* filename, FileContents file) {
@@ -91,6 +282,76 @@
   return 0;
 }
 
+// Copy the contents of source_file to target_mtd partition, a string
+// of the form "MTD:<partition>[:...]".  Return 0 on success.
+int CopyToMTDPartition(const char* source_file, const char* target_mtd) {
+  char* partition = strchr(target_mtd, ':');
+  if (partition == NULL) {
+    fprintf(stderr, "bad MTD target name \"%s\"\n", target_mtd);
+    return -1;
+  }
+  ++partition;
+  // Trim off anything after a colon, eg "MTD:boot:blah:blah:blah...".
+  // We want just the partition name "boot".
+  partition = strdup(partition);
+  char* end = strchr(partition, ':');
+  if (end != NULL)
+    *end = '\0';
+
+  FILE* f = fopen(source_file, "rb");
+  if (f == NULL) {
+    fprintf(stderr, "failed to open %s for reading: %s\n",
+            source_file, strerror(errno));
+    return -1;
+  }
+
+  if (!mtd_partitions_scanned) {
+    mtd_scan_partitions();
+    mtd_partitions_scanned = 1;
+  }
+
+  const MtdPartition* mtd = mtd_find_partition_by_name(partition);
+  if (mtd == NULL) {
+    fprintf(stderr, "mtd partition \"%s\" not found for writing\n", partition);
+    return -1;
+  }
+
+  MtdWriteContext* ctx = mtd_write_partition(mtd);
+  if (ctx == NULL) {
+    fprintf(stderr, "failed to init mtd partition \"%s\" for writing\n",
+            partition);
+    return -1;
+  }
+
+  const int buffer_size = 4096;
+  char buffer[buffer_size];
+  size_t read;
+  while ((read = fread(buffer, 1, buffer_size, f)) > 0) {
+    size_t written = mtd_write_data(ctx, buffer, read);
+    if (written != read) {
+      fprintf(stderr, "only wrote %d of %d bytes to MTD %s\n",
+              written, read, partition);
+      mtd_write_close(ctx);
+      return -1;
+    }
+  }
+
+  fclose(f);
+  if (mtd_erase_blocks(ctx, -1) < 0) {
+    fprintf(stderr, "error finishing mtd write of %s\n", partition);
+    mtd_write_close(ctx);
+    return -1;
+  }
+
+  if (mtd_write_close(ctx)) {
+    fprintf(stderr, "error closing mtd write of %s\n", partition);
+    return -1;
+  }
+
+  free(partition);
+  return 0;
+}
+
 
 // Take a string 'str' of 40 hex digits and parse it into the 20
 // byte array 'digest'.  'str' may contain only the digest or be of
@@ -176,8 +437,13 @@
   FileContents file;
   file.data = NULL;
 
+  // It's okay to specify no sha1s; the check will pass if the
+  // LoadFileContents is successful.  (Useful for reading MTD
+  // partitions, where the filename encodes the sha1s; no need to
+  // check them twice.)
   if (LoadFileContents(argv[2], &file) != 0 ||
-      FindMatchingPatch(file.sha1, patches, num_patches) == NULL) {
+      (num_patches > 0 &&
+       FindMatchingPatch(file.sha1, patches, num_patches) == NULL)) {
     fprintf(stderr, "file \"%s\" doesn't have any of expected "
             "sha1 sums; checking cache\n", argv[2]);
 
@@ -226,27 +492,57 @@
 // replacement for it) and idempotent (it's okay to run this program
 // multiple times).
 //
-// - if the sha1 hash of <file> is <tgt-sha1>, does nothing and exits
+// - if the sha1 hash of <tgt-file> is <tgt-sha1>, does nothing and exits
 //   successfully.
 //
-// - otherwise, if the sha1 hash of <file> is <src-sha1>, applies the
-//   bsdiff <patch> to <file> to produce a new file (the type of patch
+// - otherwise, if the sha1 hash of <src-file> is <src-sha1>, applies the
+//   bsdiff <patch> to <src-file> to produce a new file (the type of patch
 //   is automatically detected from the file header).  If that new
-//   file has sha1 hash <tgt-sha1>, moves it to replace <file>, and
-//   exits successfully.
+//   file has sha1 hash <tgt-sha1>, moves it to replace <tgt-file>, and
+//   exits successfully.  Note that if <src-file> and <tgt-file> are
+//   not the same, <src-file> is NOT deleted on success.  <tgt-file>
+//   may be the string "-" to mean "the same as src-file".
 //
 // - otherwise, or if any error is encountered, exits with non-zero
 //   status.
+//
+// <src-file> (or <file> in check mode) may refer to an MTD partition
+// to read the source data.  See the comments for the
+// LoadMTDContents() function above for the format of such a filename.
+//
+//
+// As you might guess from the arguments, this function used to be
+// main(); it was split out this way so applypatch could be built as a
+// static library and linked into other executables as well.  In the
+// future only the library form will exist; we will not need to build
+// this as a standalone executable.
+//
+// The arguments to this function are just the command-line of the
+// standalone executable:
+//
+// <src-file> <tgt-file> <tgt-sha1> <tgt-size> [<src-sha1>:<patch> ...]
+//    to apply a patch.  Returns 0 on success, 1 on failure.
+//
+// "-c" <file> [<sha1> ...]
+//    to check a file's contents against zero or more sha1s.  Returns
+//    0 if it matches any of them, 1 if it doesn't.
+//
+// "-s" <bytes>
+//    returns 0 if enough free space is available on /cache; 1 if it
+//    does not.
+//
+// "-l"
+//    shows open-source license information and returns 0.
+//
+// This function returns 2 if the arguments are not understood (in the
+// standalone executable, this causes the usage message to be
+// printed).
+//
+// TODO: make the interface more sensible for use as a library.
 
-int main(int argc, char** argv) {
+int applypatch(int argc, char** argv) {
   if (argc < 2) {
- usage:
-    fprintf(stderr, "usage: %s <file> <tgt-sha1> <tgt-size> [<src-sha1>:<patch> ...]\n"
-                    "   or  %s -c <file> [<sha1> ...]\n"
-                    "   or  %s -s <bytes>\n"
-                    "   or  %s -l\n",
-            argv[0], argv[0], argv[0], argv[0]);
-    return 1;
+    return 2;
   }
 
   if (strncmp(argv[1], "-l", 3) == 0) {
@@ -259,7 +555,7 @@
 
   if (strncmp(argv[1], "-s", 3) == 0) {
     if (argc != 3) {
-      goto usage;
+      return 2;
     }
     size_t bytes = strtol(argv[2], NULL, 10);
     if (MakeFreeSpaceOnCache(bytes) < 0) {
@@ -273,26 +569,22 @@
   uint8_t target_sha1[SHA_DIGEST_SIZE];
 
   const char* source_filename = argv[1];
-
-  // assume that source_filename (eg "/system/app/Foo.apk") is located
-  // on the same filesystem as its top-level directory ("/system").
-  // We need something that exists for calling statfs().
-  char* source_fs = strdup(argv[1]);
-  char* slash = strchr(source_fs+1, '/');
-  if (slash != NULL) {
-    *slash = '\0';
+  const char* target_filename = argv[2];
+  if (target_filename[0] == '-' &&
+      target_filename[1] == '\0') {
+    target_filename = source_filename;
   }
 
-  if (ParseSha1(argv[2], target_sha1) != 0) {
-    fprintf(stderr, "failed to parse tgt-sha1 \"%s\"\n", argv[2]);
+ if (ParseSha1(argv[3], target_sha1) != 0) {
+    fprintf(stderr, "failed to parse tgt-sha1 \"%s\"\n", argv[3]);
     return 1;
   }
 
-  unsigned long target_size = strtoul(argv[3], NULL, 0);
+  unsigned long target_size = strtoul(argv[4], NULL, 0);
 
   int num_patches;
   Patch* patches;
-  if (ParseShaArgs(argc-4, argv+4, &patches, &num_patches) < 0) { return 1; }
+  if (ParseShaArgs(argc-5, argv+5, &patches, &num_patches) < 0) { return 1; }
 
   FileContents copy_file;
   FileContents source_file;
@@ -300,15 +592,27 @@
   const char* copy_patch_filename = NULL;
   int made_copy = 0;
 
-  if (LoadFileContents(source_filename, &source_file) == 0) {
+  // We try to load the target file into the source_file object.
+  if (LoadFileContents(target_filename, &source_file) == 0) {
     if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
       // The early-exit case:  the patch was already applied, this file
       // has the desired hash, nothing for us to do.
       fprintf(stderr, "\"%s\" is already target; no patch needed\n",
-              source_filename);
+              target_filename);
       return 0;
     }
+  }
 
+  if (source_file.data == NULL ||
+      (target_filename != source_filename &&
+       strcmp(target_filename, source_filename) != 0)) {
+    // Need to load the source file:  either we failed to load the
+    // target file, or we did but it's different from the source file.
+    free(source_file.data);
+    LoadFileContents(source_filename, &source_file);
+  }
+
+  if (source_file.data != NULL) {
     const Patch* to_use =
         FindMatchingPatch(source_file.sha1, patches, num_patches);
     if (to_use != NULL) {
@@ -339,30 +643,70 @@
     }
   }
 
-  // Is there enough room in the target filesystem to hold the patched file?
-  size_t free_space = FreeSpaceForFile(source_fs);
-  int enough_space = free_space > (target_size * 3 / 2);  // 50% margin of error
-  printf("target %ld bytes; free space %ld bytes; enough %d\n",
-         (long)target_size, (long)free_space, enough_space);
+  // Is there enough room in the target filesystem to hold the patched
+  // file?
 
-  if (!enough_space && source_patch_filename != NULL) {
-    // Using the original source, but not enough free space.  First
-    // copy the source file to cache, then delete it from the original
-    // location.
+  if (strncmp(target_filename, "MTD:", 4) == 0) {
+    // If the target is an MTD partition, we're actually going to
+    // write the output to /tmp and then copy it to the partition.
+    // statfs() always returns 0 blocks free for /tmp, so instead
+    // we'll just assume that /tmp has enough space to hold the file.
+
+    // We still write the original source to cache, in case the MTD
+    // write is interrupted.
     if (MakeFreeSpaceOnCache(source_file.size) < 0) {
       fprintf(stderr, "not enough free space on /cache\n");
       return 1;
     }
-
     if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
       fprintf(stderr, "failed to back up source file\n");
       return 1;
     }
     made_copy = 1;
-    unlink(source_filename);
+  } else {
+    // assume that target_filename (eg "/system/app/Foo.apk") is located
+    // on the same filesystem as its top-level directory ("/system").
+    // We need something that exists for calling statfs().
+    char* target_fs = strdup(target_filename);
+    char* slash = strchr(target_fs+1, '/');
+    if (slash != NULL) {
+      *slash = '\0';
+    }
 
-    size_t free_space = FreeSpaceForFile(source_fs);
-    printf("(now %ld bytes free for source)\n", (long)free_space);
+    size_t free_space = FreeSpaceForFile(target_fs);
+    int enough_space =
+        free_space > (target_size * 3 / 2);  // 50% margin of error
+    printf("target %ld bytes; free space %ld bytes; enough %d\n",
+           (long)target_size, (long)free_space, enough_space);
+
+    if (!enough_space && source_patch_filename != NULL) {
+      // Using the original source, but not enough free space.  First
+      // copy the source file to cache, then delete it from the original
+      // location.
+
+      if (strncmp(source_filename, "MTD:", 4) == 0) {
+        // It's impossible to free space on the target filesystem by
+        // deleting the source if the source is an MTD partition.  If
+        // we're ever in a state where we need to do this, fail.
+        fprintf(stderr, "not enough free space for target but source is MTD\n");
+        return 1;
+      }
+
+      if (MakeFreeSpaceOnCache(source_file.size) < 0) {
+        fprintf(stderr, "not enough free space on /cache\n");
+        return 1;
+      }
+
+      if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
+        fprintf(stderr, "failed to back up source file\n");
+        return 1;
+      }
+      made_copy = 1;
+      unlink(source_filename);
+
+      size_t free_space = FreeSpaceForFile(target_fs);
+      printf("(now %ld bytes free for target)\n", (long)free_space);
+    }
   }
 
   FileContents* source_to_use;
@@ -375,14 +719,19 @@
     patch_filename = copy_patch_filename;
   }
 
-  // We write the decoded output to "<file>.patch".
-  char* outname = (char*)malloc(strlen(source_filename) + 10);
-  strcpy(outname, source_filename);
-  strcat(outname, ".patch");
+  char* outname = NULL;
+  if (strncmp(target_filename, "MTD:", 4) == 0) {
+    outname = MTD_TARGET_TEMP_FILE;
+  } else {
+    // We write the decoded output to "<tgt-file>.patch".
+    outname = (char*)malloc(strlen(target_filename) + 10);
+    strcpy(outname, target_filename);
+    strcat(outname, ".patch");
+  }
   FILE* output = fopen(outname, "wb");
   if (output == NULL) {
-    fprintf(stderr, "failed to patch file %s: %s\n",
-            source_filename, strerror(errno));
+    fprintf(stderr, "failed to open output file %s: %s\n",
+            outname, strerror(errno));
     return 1;
   }
 
@@ -410,11 +759,19 @@
   } else if (header_bytes_read >= 8 &&
              memcmp(header, "BSDIFF40", 8) == 0) {
     int result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
-                                  patch_filename, output, &ctx);
+                                  patch_filename, 0, output, &ctx);
     if (result != 0) {
       fprintf(stderr, "ApplyBSDiffPatch failed\n");
       return result;
     }
+  } else if (header_bytes_read >= 8 &&
+             memcmp(header, "IMGDIFF1", 8) == 0) {
+    int result = ApplyImagePatch(source_to_use->data, source_to_use->size,
+                                 patch_filename, output, &ctx);
+    if (result != 0) {
+      fprintf(stderr, "ApplyImagePatch failed\n");
+      return result;
+    }
   } else {
     fprintf(stderr, "Unknown patch file format");
     return 1;
@@ -430,22 +787,32 @@
     return 1;
   }
 
-  // Give the .patch file the same owner, group, and mode of the
-  // original source file.
-  if (chmod(outname, source_to_use->st.st_mode) != 0) {
-    fprintf(stderr, "chmod of \"%s\" failed: %s\n", outname, strerror(errno));
-    return 1;
-  }
-  if (chown(outname, source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) {
-    fprintf(stderr, "chown of \"%s\" failed: %s\n", outname, strerror(errno));
-    return 1;
-  }
+  if (strcmp(outname, MTD_TARGET_TEMP_FILE) == 0) {
+    // Copy the temp file to the MTD partition.
+    if (CopyToMTDPartition(outname, target_filename) != 0) {
+      fprintf(stderr, "copy of %s to %s failed\n", outname, target_filename);
+      return 1;
+    }
+    unlink(outname);
+  } else {
+    // Give the .patch file the same owner, group, and mode of the
+    // original source file.
+    if (chmod(outname, source_to_use->st.st_mode) != 0) {
+      fprintf(stderr, "chmod of \"%s\" failed: %s\n", outname, strerror(errno));
+      return 1;
+    }
+    if (chown(outname, source_to_use->st.st_uid,
+              source_to_use->st.st_gid) != 0) {
+      fprintf(stderr, "chown of \"%s\" failed: %s\n", outname, strerror(errno));
+      return 1;
+    }
 
-  // Finally, rename the .patch file to replace the original source file.
-  if (rename(outname, source_filename) != 0) {
-    fprintf(stderr, "rename of .patch to \"%s\" failed: %s\n",
-            source_filename, strerror(errno));
-    return 1;
+    // Finally, rename the .patch file to replace the target file.
+    if (rename(outname, target_filename) != 0) {
+      fprintf(stderr, "rename of .patch to \"%s\" failed: %s\n",
+              target_filename, strerror(errno));
+      return 1;
+    }
   }
 
   // If this run of applypatch created the copy, and we're here, we
diff --git a/tools/applypatch/applypatch.h b/tools/applypatch/applypatch.h
index 76fc80a..ccd8424 100644
--- a/tools/applypatch/applypatch.h
+++ b/tools/applypatch/applypatch.h
@@ -17,6 +17,7 @@
 #ifndef _APPLYPATCH_H
 #define _APPLYPATCH_H
 
+#include <sys/stat.h>
 #include "mincrypt/sha.h"
 
 typedef struct _Patch {
@@ -38,12 +39,26 @@
 // and use it as the source instead.
 #define CACHE_TEMP_SOURCE "/cache/saved.file"
 
+// When writing to an MTD partition, we first put the output in this
+// temp file, then copy it to the partition once the patching is
+// finished (and the target sha1 verified).
+#define MTD_TARGET_TEMP_FILE "/tmp/mtd-temp"
+
 // applypatch.c
 size_t FreeSpaceForFile(const char* filename);
+int applypatch(int argc, char** argv);
 
 // bsdiff.c
 void ShowBSDiffLicense();
 int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
+                     const char* patch_filename, ssize_t offset,
+                     FILE* output, SHA_CTX* ctx);
+int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
+                        const char* patch_filename, ssize_t patch_offset,
+                        unsigned char** new_data, ssize_t* new_size);
+
+// imgpatch.c
+int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
                      const char* patch_filename,
                      FILE* output, SHA_CTX* ctx);
 
diff --git a/tools/applypatch/applypatch.sh b/tools/applypatch/applypatch.sh
index 181cd5c..88f3025 100755
--- a/tools/applypatch/applypatch.sh
+++ b/tools/applypatch/applypatch.sh
@@ -24,16 +24,22 @@
 # partition that WORK_DIR is located on, without the leading slash
 WORK_FS=system
 
+# set to 0 to use a device instead
+USE_EMULATOR=1
+
 # ------------------------
 
 tmpdir=$(mktemp -d)
 
-emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
-pid_emulator=$!
+if [ "$USE_EMULATOR" == 1 ]; then
+  emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
+  pid_emulator=$!
+  ADB="adb -s emulator-$EMULATOR_PORT "
+else
+  ADB="adb -d "
+fi
 
-ADB="adb -s emulator-$EMULATOR_PORT "
-
-echo "emulator is $pid_emulator; waiting for startup"
+echo "waiting to connect to device"
 $ADB wait-for-device
 echo "device is available"
 $ADB remount
@@ -56,7 +62,8 @@
   echo
   echo FAIL: $testname
   echo
-  kill $pid_emulator
+  [ "$open_pid" == "" ] || kill $open_pid
+  [ "$pid_emulator" == "" ] || kill $pid_emulator
   exit 1
 }
 
@@ -68,6 +75,23 @@
   run_command df | awk "/$1/ {print gensub(/K/, \"\", \"g\", \$6)}"
 }
 
+cleanup() {
+  # not necessary if we're about to kill the emulator, but nice for
+  # running on real devices or already-running emulators.
+  testname "removing test files"
+  run_command rm $WORK_DIR/bloat.dat
+  run_command rm $WORK_DIR/old.file
+  run_command rm $WORK_DIR/patch.bsdiff
+  run_command rm $WORK_DIR/applypatch
+  run_command rm $CACHE_TEMP_SOURCE
+  run_command rm /cache/bloat*.dat
+
+  [ "$pid_emulator" == "" ] || kill $pid_emulator
+
+  rm -rf $tmpdir
+}
+
+cleanup
 
 $ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
 
@@ -146,16 +170,71 @@
 fi
 
 testname "apply bsdiff patch"
-run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
 $ADB pull $WORK_DIR/old.file $tmpdir/patched
 diff -q $DATA_DIR/new.file $tmpdir/patched || fail
 
 testname "reapply bsdiff patch"
-run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
 $ADB pull $WORK_DIR/old.file $tmpdir/patched
 diff -q $DATA_DIR/new.file $tmpdir/patched || fail
 
 
+# --------------- apply patch in new location ----------------------
+
+$ADB push $DATA_DIR/old.file $WORK_DIR
+$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
+
+# Check that the partition has enough space to apply the patch without
+# copying.  If it doesn't, we'll be testing the low-space condition
+# when we intend to test the not-low-space condition.
+testname "apply patch to new location (with enough space)"
+free_kb=$(free_space $WORK_FS)
+echo "${free_kb}kb free on /$WORK_FS."
+if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then
+  echo "Not enough space on /$WORK_FS to patch test file."
+  echo
+  echo "This doesn't mean that applypatch is necessarily broken;"
+  echo "just that /$WORK_FS doesn't have enough free space to"
+  echo "properly run this test."
+  exit 1
+fi
+
+run_command rm $WORK_DIR/new.file
+run_command rm $CACHE_TEMP_SOURCE
+
+testname "apply bsdiff patch to new location"
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+$ADB pull $WORK_DIR/new.file $tmpdir/patched
+diff -q $DATA_DIR/new.file $tmpdir/patched || fail
+
+testname "reapply bsdiff patch to new location"
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+$ADB pull $WORK_DIR/new.file $tmpdir/patched
+diff -q $DATA_DIR/new.file $tmpdir/patched || fail
+
+$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
+# put some junk in the old file
+run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
+
+testname "apply bsdiff patch to new location with corrupted source"
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo || fail
+$ADB pull $WORK_DIR/new.file $tmpdir/patched
+diff -q $DATA_DIR/new.file $tmpdir/patched || fail
+
+# put some junk in the cache copy, too
+run_command dd if=/dev/urandom of=$CACHE_TEMP_SOURCE count=100 bs=1024 || fail
+
+run_command rm $WORK_DIR/new.file
+testname "apply bsdiff patch to new location with corrupted source and copy (no new file)"
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail
+
+# put some junk in the new file
+run_command dd if=/dev/urandom of=$WORK_DIR/new.file count=100 bs=1024 || fail
+
+testname "apply bsdiff patch to new location with corrupted source and copy (bad new file)"
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail
+
 # --------------- apply patch with low space on /system ----------------------
 
 $ADB push $DATA_DIR/old.file $WORK_DIR
@@ -169,12 +248,12 @@
 echo "${free_kb}kb free on /$WORK_FS now."
 
 testname "apply bsdiff patch with low space"
-run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
 $ADB pull $WORK_DIR/old.file $tmpdir/patched
 diff -q $DATA_DIR/new.file $tmpdir/patched || fail
 
 testname "reapply bsdiff patch with low space"
-run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
 $ADB pull $WORK_DIR/old.file $tmpdir/patched
 diff -q $DATA_DIR/new.file $tmpdir/patched || fail
 
@@ -213,7 +292,7 @@
 run_command ls $CACHE_TEMP_SOURCE || fail              # wasn't deleted because it's the source file copy
 
 # should fail; not enough files can be deleted
-run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff && fail
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff && fail
 run_command ls /cache/bloat_large.dat || fail   # wasn't deleted because it was open
 run_command ls /cache/subdir/a.file || fail     # wasn't deleted because it's in a subdir
 run_command ls $CACHE_TEMP_SOURCE || fail       # wasn't deleted because it's the source file copy
@@ -229,7 +308,7 @@
 run_command ls $CACHE_TEMP_SOURCE || fail       # wasn't deleted because it's the source file copy
 
 # should succeed
-run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
 $ADB pull $WORK_DIR/old.file $tmpdir/patched
 diff -q $DATA_DIR/new.file $tmpdir/patched || fail
 run_command ls /cache/subdir/a.file || fail     # still wasn't deleted because it's in a subdir
@@ -242,7 +321,7 @@
 run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
 
 testname "apply bsdiff patch from cache (corrupted source) with low space"
-run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
 $ADB pull $WORK_DIR/old.file $tmpdir/patched
 diff -q $DATA_DIR/new.file $tmpdir/patched || fail
 
@@ -251,20 +330,14 @@
 run_command rm $WORK_DIR/old.file
 
 testname "apply bsdiff patch from cache (missing source) with low space"
-run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
 $ADB pull $WORK_DIR/old.file $tmpdir/patched
 diff -q $DATA_DIR/new.file $tmpdir/patched || fail
 
 
 # --------------- cleanup ----------------------
 
-# not necessary if we're about to kill the emulator, but nice for
-# running on real devices or already-running emulators.
-run_command rm /cache/bloat*.dat $WORK_DIR/bloat.dat $CACHE_TEMP_SOURCE $WORK_DIR/old.file $WORK_DIR/patch.xdelta3 $WORK_DIR/patch.bsdiff $WORK_DIR/applypatch
-
-kill $pid_emulator
-
-rm -rf $tmpdir
+cleanup
 
 echo
 echo PASS
diff --git a/tools/applypatch/bsdiff.c b/tools/applypatch/bsdiff.c
index a2851f9..9d55f3b 100644
--- a/tools/applypatch/bsdiff.c
+++ b/tools/applypatch/bsdiff.c
@@ -29,6 +29,7 @@
 #include <bzlib.h>
 
 #include "mincrypt/sha.h"
+#include "applypatch.h"
 
 void ShowBSDiffLicense() {
   puts("The bsdiff library used herein is:\n"
@@ -80,10 +81,34 @@
   return y;
 }
 
+
 int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
-                     const char* patch_filename,
+                     const char* patch_filename, ssize_t patch_offset,
                      FILE* output, SHA_CTX* ctx) {
 
+  unsigned char* new_data;
+  ssize_t new_size;
+  if (ApplyBSDiffPatchMem(old_data, old_size, patch_filename, patch_offset,
+                          &new_data, &new_size) != 0) {
+    return -1;
+  }
+
+  if (fwrite(new_data, 1, new_size, output) < new_size) {
+    fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno));
+    return 1;
+  }
+  if (ctx) {
+    SHA_update(ctx, new_data, new_size);
+  }
+  free(new_data);
+
+  return 0;
+}
+
+int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
+                        const char* patch_filename, ssize_t patch_offset,
+                        unsigned char** new_data, ssize_t* new_size) {
+
   FILE* f;
   if ((f = fopen(patch_filename, "rb")) == NULL) {
     fprintf(stderr, "failed to open patch file\n");
@@ -102,6 +127,8 @@
   // from oldfile to x bytes from the diff block; copy y bytes from the
   // extra block; seek forwards in oldfile by z bytes".
 
+  fseek(f, patch_offset, SEEK_SET);
+
   unsigned char header[32];
   if (fread(header, 1, 32, f) < 32) {
     fprintf(stderr, "failed to read patch file header\n");
@@ -109,17 +136,16 @@
   }
 
   if (memcmp(header, "BSDIFF40", 8) != 0) {
-    fprintf(stderr, "corrupt patch file header (magic number)\n");
+    fprintf(stderr, "corrupt bsdiff patch file header (magic number)\n");
     return 1;
   }
 
   ssize_t ctrl_len, data_len;
-  ssize_t new_size;
   ctrl_len = offtin(header+8);
   data_len = offtin(header+16);
-  new_size = offtin(header+24);
+  *new_size = offtin(header+24);
 
-  if (ctrl_len < 0 || data_len < 0 || new_size < 0) {
+  if (ctrl_len < 0 || data_len < 0 || *new_size < 0) {
     fprintf(stderr, "corrupt patch file header (data lengths)\n");
     return 1;
   }
@@ -135,7 +161,7 @@
     fprintf(stderr, "failed to open patch file\n");                      \
     return 1;                                                            \
   }                                                                      \
-  if (fseeko(f, offset, SEEK_SET)) {                                     \
+  if (fseeko(f, offset+patch_offset, SEEK_SET)) {                        \
     fprintf(stderr, "failed to seek in patch file\n");                   \
     return 1;                                                            \
   }                                                                      \
@@ -150,9 +176,10 @@
 
 #undef OPEN_AT
 
-  unsigned char* new_data = malloc(new_size);
-  if (new_data == NULL) {
-    fprintf(stderr, "failed to allocate memory for output file\n");
+  *new_data = malloc(*new_size);
+  if (*new_data == NULL) {
+    fprintf(stderr, "failed to allocate %d bytes of memory for output file\n",
+            (int)*new_size);
     return 1;
   }
 
@@ -161,7 +188,7 @@
   off_t len_read;
   int i;
   unsigned char buf[8];
-  while (newpos < new_size) {
+  while (newpos < *new_size) {
     // Read control data
     for (i = 0; i < 3; ++i) {
       len_read = BZ2_bzRead(&bzerr, cpfbz2, buf, 8);
@@ -173,13 +200,13 @@
     }
 
     // Sanity check
-    if (newpos + ctrl[0] > new_size) {
+    if (newpos + ctrl[0] > *new_size) {
       fprintf(stderr, "corrupt patch (new file overrun)\n");
       return 1;
     }
 
     // Read diff string
-    len_read = BZ2_bzRead(&bzerr, dpfbz2, new_data + newpos, ctrl[0]);
+    len_read = BZ2_bzRead(&bzerr, dpfbz2, *new_data + newpos, ctrl[0]);
     if (len_read < ctrl[0] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
       fprintf(stderr, "corrupt patch (read diff)\n");
       return 1;
@@ -188,7 +215,7 @@
     // Add old data to diff string
     for (i = 0; i < ctrl[0]; ++i) {
       if ((oldpos+i >= 0) && (oldpos+i < old_size)) {
-        new_data[newpos+i] += old_data[oldpos+i];
+        (*new_data)[newpos+i] += old_data[oldpos+i];
       }
     }
 
@@ -197,13 +224,13 @@
     oldpos += ctrl[0];
 
     // Sanity check
-    if (newpos + ctrl[1] > new_size) {
+    if (newpos + ctrl[1] > *new_size) {
       fprintf(stderr, "corrupt patch (new file overrun)\n");
       return 1;
     }
 
     // Read extra string
-    len_read = BZ2_bzRead(&bzerr, epfbz2, new_data + newpos, ctrl[1]);
+    len_read = BZ2_bzRead(&bzerr, epfbz2, *new_data + newpos, ctrl[1]);
     if (len_read < ctrl[1] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
       fprintf(stderr, "corrupt patch (read extra)\n");
       return 1;
@@ -221,12 +248,5 @@
   fclose(dpf);
   fclose(epf);
 
-  if (fwrite(new_data, 1, new_size, output) < new_size) {
-    fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno));
-    return 1;
-  }
-  SHA_update(ctx, new_data, new_size);
-  free(new_data);
-
   return 0;
 }
diff --git a/tools/applypatch/imgdiff.c b/tools/applypatch/imgdiff.c
new file mode 100644
index 0000000..f0b5fea
--- /dev/null
+++ b/tools/applypatch/imgdiff.c
@@ -0,0 +1,560 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This program constructs binary patches for images -- such as boot.img
+ * and recovery.img -- that consist primarily of large chunks of gzipped
+ * data interspersed with uncompressed data.  Doing a naive bsdiff of
+ * these files is not useful because small changes in the data lead to
+ * large changes in the compressed bitstream; bsdiff patches of gzipped
+ * data are typically as large as the data itself.
+ *
+ * To patch these usefully, we break the source and target images up into
+ * chunks of two types: "normal" and "gzip".  Normal chunks are simply
+ * patched using a plain bsdiff.  Gzip chunks are first expanded, then a
+ * bsdiff is applied to the uncompressed data, then the patched data is
+ * gzipped using the same encoder parameters.  Patched chunks are
+ * concatenated together to create the output file; the output image
+ * should be *exactly* the same series of bytes as the target image used
+ * originally to generate the patch.
+ *
+ * To work well with this tool, the gzipped sections of the target
+ * image must have been generated using the same deflate encoder that
+ * is available in applypatch, namely, the one in the zlib library.
+ * In practice this means that images should be compressed using the
+ * "minigzip" tool included in the zlib distribution, not the GNU gzip
+ * program.
+ *
+ * An "imgdiff" patch consists of a header describing the chunk structure
+ * of the file and any encoding parameters needed for the gzipped
+ * chunks, followed by N bsdiff patches, one per chunk.
+ *
+ * For a diff to be generated, the source and target images must have the
+ * same "chunk" structure: that is, the same number of gzipped and normal
+ * chunks in the same order.  Android boot and recovery images currently
+ * consist of five chunks:  a small normal header, a gzipped kernel, a
+ * small normal section, a gzipped ramdisk, and finally a small normal
+ * footer.
+ *
+ * Caveats:  we locate gzipped sections within the source and target
+ * images by searching for the byte sequence 1f8b0800:  1f8b is the gzip
+ * magic number; 08 specifies the "deflate" encoding [the only encoding
+ * supported by the gzip standard]; and 00 is the flags byte.  We do not
+ * currently support any extra header fields (which would be indicated by
+ * a nonzero flags byte).  We also don't handle the case when that byte
+ * sequence appears spuriously in the file.  (Note that it would have to
+ * occur spuriously within a normal chunk to be a problem.)
+ *
+ *
+ * The imgdiff patch header looks like this:
+ *
+ *    "IMGDIFF1"                  (8)   [magic number and version]
+ *    chunk count                 (4)
+ *    for each chunk:
+ *        chunk type              (4)   [CHUNK_NORMAL or CHUNK_GZIP]
+ *        source start            (8)
+ *        source len              (8)
+ *        bsdiff patch offset     (8)   [from start of patch file]
+ *        if chunk type == CHUNK_GZIP:
+ *           source expanded len  (8)   [size of uncompressed source]
+ *           target expected len  (8)   [size of uncompressed target]
+ *           gzip level           (4)
+ *                method          (4)
+ *                windowBits      (4)
+ *                memLevel        (4)
+ *                strategy        (4)
+ *           gzip header len      (4)
+ *           gzip header          (gzip header len)
+ *           gzip footer          (8)
+ *
+ * All integers are little-endian.  "source start" and "source len"
+ * specify the section of the input image that comprises this chunk,
+ * including the gzip header and footer for gzip chunks.  "source
+ * expanded len" is the size of the uncompressed source data.  "target
+ * expected len" is the size of the uncompressed data after applying
+ * the bsdiff patch.  The next five parameters specify the zlib
+ * parameters to be used when compressing the patched data, and the
+ * next three specify the header and footer to be wrapped around the
+ * compressed data to create the output chunk (so that header contents
+ * like the timestamp are recreated exactly).
+ *
+ * After the header there are 'chunk count' bsdiff patches; the offset
+ * of each from the beginning of the file is specified in the header.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "zlib.h"
+#include "imgdiff.h"
+
+typedef struct {
+  int type;             // CHUNK_NORMAL or CHUNK_GZIP
+  size_t start;         // offset of chunk in original image file
+
+  size_t len;
+  unsigned char* data;  // data to be patched (ie, uncompressed, for
+                        // gzip chunks)
+
+  // everything else is for CHUNK_GZIP chunks only:
+
+  size_t gzip_header_len;
+  unsigned char* gzip_header;
+  unsigned char* gzip_footer;
+
+  // original (compressed) gzip data, including header and footer
+  size_t gzip_len;
+  unsigned char* gzip_data;
+
+  // deflate encoder parameters
+  int level, method, windowBits, memLevel, strategy;
+} ImageChunk;
+
+/*
+ * Read the given file and break it up into chunks, putting the number
+ * of chunks and their info in *num_chunks and **chunks,
+ * respectively.  Returns a malloc'd block of memory containing the
+ * contents of the file; various pointers in the output chunk array
+ * will point into this block of memory.  The caller should free the
+ * return value when done with all the chunks.  Returns NULL on
+ * failure.
+ */
+unsigned char* ReadImage(const char* filename,
+                         int* num_chunks, ImageChunk** chunks) {
+  struct stat st;
+  if (stat(filename, &st) != 0) {
+    fprintf(stderr, "failed to stat \"%s\": %s\n", filename, strerror(errno));
+    return NULL;
+  }
+
+  unsigned char* img = malloc(st.st_size + 4);
+  FILE* f = fopen(filename, "rb");
+  if (fread(img, 1, st.st_size, f) != st.st_size) {
+    fprintf(stderr, "failed to read \"%s\" %s\n", filename, strerror(errno));
+    fclose(f);
+    return NULL;
+  }
+  fclose(f);
+
+  // append 4 zero bytes to the data so we can always search for the
+  // four-byte string 1f8b0800 starting at any point in the actual
+  // file data, without special-casing the end of the data.
+  memset(img+st.st_size, 0, 4);
+
+  size_t pos = 0;
+
+  *num_chunks = 0;
+  *chunks = NULL;
+
+  while (pos < st.st_size) {
+    unsigned char* p = img+pos;
+
+    // Reallocate the list for every chunk; we expect the number of
+    // chunks to be small (5 for typical boot and recovery images).
+    ++*num_chunks;
+    *chunks = realloc(*chunks, *num_chunks * sizeof(ImageChunk));
+    ImageChunk* curr = *chunks + (*num_chunks-1);
+    curr->start = pos;
+
+    if (st.st_size - pos >= 4 &&
+        p[0] == 0x1f && p[1] == 0x8b &&
+        p[2] == 0x08 &&    // deflate compression
+        p[3] == 0x00) {    // no header flags
+      // 'pos' is the offset of the start of a gzip chunk.
+
+      curr->type = CHUNK_GZIP;
+      curr->gzip_header_len = GZIP_HEADER_LEN;
+      curr->gzip_header = p;
+
+      // We must decompress this chunk in order to discover where it
+      // ends, and so we can put the uncompressed data and its length
+      // into curr->data and curr->len;
+
+      size_t allocated = 32768;
+      curr->len = 0;
+      curr->data = malloc(allocated);
+      curr->gzip_data = p;
+
+      z_stream strm;
+      strm.zalloc = Z_NULL;
+      strm.zfree = Z_NULL;
+      strm.opaque = Z_NULL;
+      strm.avail_in = st.st_size - (pos + curr->gzip_header_len);
+      strm.next_in = p + GZIP_HEADER_LEN;
+
+      // -15 means we are decoding a 'raw' deflate stream; zlib will
+      // not expect zlib headers.
+      int ret = inflateInit2(&strm, -15);
+
+      do {
+        strm.avail_out = allocated - curr->len;
+        strm.next_out = curr->data + curr->len;
+        ret = inflate(&strm, Z_NO_FLUSH);
+        curr->len = allocated - strm.avail_out;
+        if (strm.avail_out == 0) {
+          allocated *= 2;
+          curr->data = realloc(curr->data, allocated);
+        }
+      } while (ret != Z_STREAM_END);
+
+      curr->gzip_len = st.st_size - strm.avail_in - pos + GZIP_FOOTER_LEN;
+      pos = st.st_size - strm.avail_in;
+      inflateEnd(&strm);
+
+      // consume the gzip footer.
+      curr->gzip_footer = img+pos;
+      pos += GZIP_FOOTER_LEN;
+      p = img+pos;
+
+      // The footer (that we just skipped over) contains the size of
+      // the uncompressed data.  Double-check to make sure that it
+      // matches the size of the data we got when we actually did
+      // the decompression.
+      size_t footer_size = p[-4] + (p[-3] << 8) + (p[-2] << 16) + (p[-1] << 24);
+      if (footer_size != curr->len) {
+        fprintf(stderr, "Error: footer size %d != decompressed size %d\n",
+                footer_size, curr->len);
+        free(img);
+        return NULL;
+      }
+    } else {
+      // 'pos' is not the offset of the start of a gzip chunk, so scan
+      // forward until we find a gzip header.
+      curr->type = CHUNK_NORMAL;
+      curr->data = p;
+
+      for (curr->len = 0; curr->len < (st.st_size - pos); ++curr->len) {
+        if (p[curr->len] == 0x1f &&
+            p[curr->len+1] == 0x8b &&
+            p[curr->len+2] == 0x08 &&
+            p[curr->len+3] == 0x00) {
+          break;
+        }
+      }
+      pos += curr->len;
+    }
+  }
+
+  return img;
+}
+
+#define BUFFER_SIZE 32768
+
+/*
+ * Takes the uncompressed data stored in the chunk, compresses it
+ * using the zlib parameters stored in the chunk, and checks that it
+ * matches exactly the compressed data we started with (also stored in
+ * the chunk).  Return 0 on success.
+ */
+int TryReconstruction(ImageChunk* chunk, unsigned char* out) {
+  size_t p = chunk->gzip_header_len;
+
+  z_stream strm;
+  strm.zalloc = Z_NULL;
+  strm.zfree = Z_NULL;
+  strm.opaque = Z_NULL;
+  strm.avail_in = chunk->len;
+  strm.next_in = chunk->data;
+  int ret;
+  ret = deflateInit2(&strm, chunk->level, chunk->method, chunk->windowBits,
+                     chunk->memLevel, chunk->strategy);
+  do {
+    strm.avail_out = BUFFER_SIZE;
+    strm.next_out = out;
+    ret = deflate(&strm, Z_FINISH);
+    size_t have = BUFFER_SIZE - strm.avail_out;
+
+    if (memcmp(out, chunk->gzip_data+p, have) != 0) {
+      // mismatch; data isn't the same.
+      deflateEnd(&strm);
+      return -1;
+    }
+    p += have;
+  } while (ret != Z_STREAM_END);
+  deflateEnd(&strm);
+  if (p + GZIP_FOOTER_LEN != chunk->gzip_len) {
+    // mismatch; ran out of data before we should have.
+    return -1;
+  }
+  return 0;
+}
+
+/*
+ * Verify that we can reproduce exactly the same compressed data that
+ * we started with.  Sets the level, method, windowBits, memLevel, and
+ * strategy fields in the chunk to the encoding parameters needed to
+ * produce the right output.  Returns 0 on success.
+ */
+int ReconstructGzipChunk(ImageChunk* chunk) {
+  if (chunk->type != CHUNK_GZIP) {
+    fprintf(stderr, "attempt to reconstruct non-gzip chunk\n");
+    return -1;
+  }
+
+  size_t p = 0;
+  unsigned char* out = malloc(BUFFER_SIZE);
+
+  // We only check two combinations of encoder parameters:  level 6
+  // (the default) and level 9 (the maximum).
+  for (chunk->level = 6; chunk->level <= 9; chunk->level += 3) {
+    chunk->windowBits = -15;  // 32kb window; negative to indicate a raw stream.
+    chunk->memLevel = 8;      // the default value.
+    chunk->method = Z_DEFLATED;
+    chunk->strategy = Z_DEFAULT_STRATEGY;
+
+    if (TryReconstruction(chunk, out) == 0) {
+      free(out);
+      return 0;
+    }
+  }
+
+  free(out);
+  return -1;
+}
+
+/** Write a 4-byte value to f in little-endian order. */
+void Write4(int value, FILE* f) {
+  fputc(value & 0xff, f);
+  fputc((value >> 8) & 0xff, f);
+  fputc((value >> 16) & 0xff, f);
+  fputc((value >> 24) & 0xff, f);
+}
+
+/** Write an 8-byte value to f in little-endian order. */
+void Write8(long long value, FILE* f) {
+  fputc(value & 0xff, f);
+  fputc((value >> 8) & 0xff, f);
+  fputc((value >> 16) & 0xff, f);
+  fputc((value >> 24) & 0xff, f);
+  fputc((value >> 32) & 0xff, f);
+  fputc((value >> 40) & 0xff, f);
+  fputc((value >> 48) & 0xff, f);
+  fputc((value >> 56) & 0xff, f);
+}
+
+
+/*
+ * Given source and target chunks, compute a bsdiff patch between them
+ * by running bsdiff in a subprocess.  Return the patch data, placing
+ * its length in *size.  Return NULL on failure.  We expect the bsdiff
+ * program to be in the path.
+ */
+unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
+  char stemp[] = "/tmp/imgdiff-src-XXXXXX";
+  char ttemp[] = "/tmp/imgdiff-tgt-XXXXXX";
+  char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
+  mkstemp(stemp);
+  mkstemp(ttemp);
+  mkstemp(ptemp);
+
+  FILE* f = fopen(stemp, "wb");
+  if (f == NULL) {
+    fprintf(stderr, "failed to open src chunk %s: %s\n",
+            stemp, strerror(errno));
+    return NULL;
+  }
+  if (fwrite(src->data, 1, src->len, f) != src->len) {
+    fprintf(stderr, "failed to write src chunk to %s: %s\n",
+            stemp, strerror(errno));
+    return NULL;
+  }
+  fclose(f);
+
+  f = fopen(ttemp, "wb");
+  if (f == NULL) {
+    fprintf(stderr, "failed to open tgt chunk %s: %s\n",
+            ttemp, strerror(errno));
+    return NULL;
+  }
+  if (fwrite(tgt->data, 1, tgt->len, f) != tgt->len) {
+    fprintf(stderr, "failed to write tgt chunk to %s: %s\n",
+            ttemp, strerror(errno));
+    return NULL;
+  }
+  fclose(f);
+
+  char cmd[200];
+  sprintf(cmd, "bsdiff %s %s %s", stemp, ttemp, ptemp);
+  if (system(cmd) != 0) {
+    fprintf(stderr, "failed to run bsdiff: %s\n", strerror(errno));
+    return NULL;
+  }
+
+  struct stat st;
+  if (stat(ptemp, &st) != 0) {
+    fprintf(stderr, "failed to stat patch file %s: %s\n",
+            ptemp, strerror(errno));
+    return NULL;
+  }
+
+  unsigned char* data = malloc(st.st_size);
+  *size = st.st_size;
+
+  f = fopen(ptemp, "rb");
+  if (f == NULL) {
+    fprintf(stderr, "failed to open patch %s: %s\n", ptemp, strerror(errno));
+    return NULL;
+  }
+  if (fread(data, 1, st.st_size, f) != st.st_size) {
+    fprintf(stderr, "failed to read patch %s: %s\n", ptemp, strerror(errno));
+    return NULL;
+  }
+  fclose(f);
+
+  unlink(stemp);
+  unlink(ttemp);
+  unlink(ptemp);
+
+  return data;
+}
+
+/*
+ * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob
+ * of uninterpreted data).  The resulting patch will likely be about
+ * as big as the target file, but it lets us handle the case of images
+ * where some gzip chunks are reconstructible but others aren't (by
+ * treating the ones that aren't as normal chunks).
+ */
+void ChangeGzipChunkToNormal(ImageChunk* ch) {
+  ch->type = CHUNK_NORMAL;
+  free(ch->data);
+  ch->data = ch->gzip_data;
+  ch->len = ch->gzip_len;
+}
+
+int main(int argc, char** argv) {
+  if (argc != 4) {
+    fprintf(stderr, "usage: %s <src-img> <tgt-img> <patch-file>\n", argv[0]);
+    return 2;
+  }
+
+  int num_src_chunks;
+  ImageChunk* src_chunks;
+  if (ReadImage(argv[1], &num_src_chunks, &src_chunks) == NULL) {
+    fprintf(stderr, "failed to break apart source image\n");
+    return 1;
+  }
+
+  int num_tgt_chunks;
+  ImageChunk* tgt_chunks;
+  if (ReadImage(argv[2], &num_tgt_chunks, &tgt_chunks) == NULL) {
+    fprintf(stderr, "failed to break apart target image\n");
+    return 1;
+  }
+
+  // Verify that the source and target images have the same chunk
+  // structure (ie, the same sequence of gzip and normal chunks).
+
+  if (num_src_chunks != num_tgt_chunks) {
+    fprintf(stderr, "source and target don't have same number of chunks!\n");
+    return 1;
+  }
+  int i;
+  for (i = 0; i < num_src_chunks; ++i) {
+    if (src_chunks[i].type != tgt_chunks[i].type) {
+      fprintf(stderr, "source and target don't have same chunk "
+              "structure! (chunk %d)\n", i);
+      return 1;
+    }
+  }
+
+  // Confirm that given the uncompressed chunk data in the target, we
+  // can recompress it and get exactly the same bits as are in the
+  // input target image.  If this fails, treat the chunk as a normal
+  // non-gzipped chunk.
+
+  for (i = 0; i < num_tgt_chunks; ++i) {
+    if (tgt_chunks[i].type == CHUNK_GZIP) {
+      if (ReconstructGzipChunk(tgt_chunks+i) < 0) {
+        printf("failed to reconstruct target gzip chunk %d; "
+               "treating as normal chunk\n", i);
+        ChangeGzipChunkToNormal(tgt_chunks+i);
+        ChangeGzipChunkToNormal(src_chunks+i);
+      } else {
+        printf("reconstructed target gzip chunk %d\n", i);
+      }
+    }
+  }
+
+  // Compute bsdiff patches for each chunk's data (the uncompressed
+  // data, in the case of gzip chunks).
+
+  unsigned char** patch_data = malloc(num_src_chunks * sizeof(unsigned char*));
+  size_t* patch_size = malloc(num_src_chunks * sizeof(size_t));
+  for (i = 0; i < num_src_chunks; ++i) {
+    patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i);
+    printf("patch %d is %d bytes (of %d)\n", i, patch_size[i],
+           tgt_chunks[i].type == CHUNK_NORMAL ? tgt_chunks[i].len : tgt_chunks[i].gzip_len);
+
+  }
+
+  // Figure out how big the imgdiff file header is going to be, so
+  // that we can correctly compute the offset of each bsdiff patch
+  // within the file.
+
+  size_t total_header_size = 12;
+  for (i = 0; i < num_src_chunks; ++i) {
+    total_header_size += 4 + 8*3;
+    if (src_chunks[i].type == CHUNK_GZIP) {
+      total_header_size += 8*2 + 4*6 + tgt_chunks[i].gzip_header_len + 8;
+    }
+  }
+
+  size_t offset = total_header_size;
+
+  FILE* f = fopen(argv[3], "wb");
+
+  // Write out the headers.
+
+  fwrite("IMGDIFF1", 1, 8, f);
+  Write4(num_src_chunks, f);
+  for (i = 0; i < num_tgt_chunks; ++i) {
+    Write4(tgt_chunks[i].type, f);
+    Write8(src_chunks[i].start, f);
+    Write8(src_chunks[i].type == CHUNK_NORMAL ? src_chunks[i].len :
+           (src_chunks[i].gzip_len + src_chunks[i].gzip_header_len + 8), f);
+    Write8(offset, f);
+
+    if (tgt_chunks[i].type == CHUNK_GZIP) {
+      Write8(src_chunks[i].len, f);
+      Write8(tgt_chunks[i].len, f);
+      Write4(tgt_chunks[i].level, f);
+      Write4(tgt_chunks[i].method, f);
+      Write4(tgt_chunks[i].windowBits, f);
+      Write4(tgt_chunks[i].memLevel, f);
+      Write4(tgt_chunks[i].strategy, f);
+      Write4(tgt_chunks[i].gzip_header_len, f);
+      fwrite(tgt_chunks[i].gzip_header, 1, tgt_chunks[i].gzip_header_len, f);
+      fwrite(tgt_chunks[i].gzip_footer, 1, GZIP_FOOTER_LEN, f);
+    }
+
+    offset += patch_size[i];
+  }
+
+  // Append each chunk's bsdiff patch, in order.
+
+  for (i = 0; i < num_tgt_chunks; ++i) {
+    fwrite(patch_data[i], 1, patch_size[i], f);
+  }
+
+  fclose(f);
+
+  return 0;
+}
diff --git a/tools/applypatch/imgdiff.h b/tools/applypatch/imgdiff.h
new file mode 100644
index 0000000..7ec45c5
--- /dev/null
+++ b/tools/applypatch/imgdiff.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Image patch chunk types
+#define CHUNK_NORMAL 0
+#define CHUNK_GZIP   1
+
+// The gzip header size is actually variable, but we currently don't
+// support gzipped data with any of the optional fields, so for now it
+// will always be ten bytes.  See RFC 1952 for the definition of the
+// gzip format.
+#define GZIP_HEADER_LEN   10
+
+// The gzip footer size really is fixed.
+#define GZIP_FOOTER_LEN   8
diff --git a/tools/applypatch/imgpatch.c b/tools/applypatch/imgpatch.c
new file mode 100644
index 0000000..2efe874
--- /dev/null
+++ b/tools/applypatch/imgpatch.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// See imgdiff.c in this directory for a description of the patch file
+// format.
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "zlib.h"
+#include "mincrypt/sha.h"
+#include "applypatch.h"
+#include "imgdiff.h"
+
+int Read4(unsigned char* p) {
+  return (int)(((unsigned int)p[3] << 24) |
+               ((unsigned int)p[2] << 16) |
+               ((unsigned int)p[1] << 8) |
+               (unsigned int)p[0]);
+}
+
+long long Read8(unsigned char* p) {
+  return (long long)(((unsigned long long)p[7] << 56) |
+                     ((unsigned long long)p[6] << 48) |
+                     ((unsigned long long)p[5] << 40) |
+                     ((unsigned long long)p[4] << 32) |
+                     ((unsigned long long)p[3] << 24) |
+                     ((unsigned long long)p[2] << 16) |
+                     ((unsigned long long)p[1] << 8) |
+                     (unsigned long long)p[0]);
+}
+
+/*
+ * Apply the patch given in 'patch_filename' to the source data given
+ * by (old_data, old_size).  Write the patched output to the 'output'
+ * file, and update the SHA context with the output data as well.
+ * Return 0 on success.
+ */
+int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
+                    const char* patch_filename,
+                    FILE* output, SHA_CTX* ctx) {
+  FILE* f;
+  if ((f = fopen(patch_filename, "rb")) == NULL) {
+    fprintf(stderr, "failed to open patch file\n");
+    return -1;
+  }
+
+  unsigned char header[12];
+  if (fread(header, 1, 12, f) != 12) {
+    fprintf(stderr, "failed to read patch file header\n");
+    return -1;
+  }
+
+  if (memcmp(header, "IMGDIFF1", 8) != 0) {
+    fprintf(stderr, "corrupt patch file header (magic number)\n");
+    return -1;
+  }
+
+  int num_chunks = Read4(header+8);
+
+  int i;
+  for (i = 0; i < num_chunks; ++i) {
+    // each chunk's header record starts with 28 bytes (4 + 8*3).
+    unsigned char chunk[28];
+    if (fread(chunk, 1, 28, f) != 28) {
+      fprintf(stderr, "failed to read chunk %d record\n", i);
+      return -1;
+    }
+
+    int type = Read4(chunk);
+    size_t src_start = Read8(chunk+4);
+    size_t src_len = Read8(chunk+12);
+    size_t patch_offset = Read8(chunk+20);
+
+    if (type == CHUNK_NORMAL) {
+      fprintf(stderr, "CHUNK %d:  normal   patch offset %d\n", i, patch_offset);
+
+      ApplyBSDiffPatch(old_data + src_start, src_len,
+                       patch_filename, patch_offset,
+                       output, ctx);
+    } else if (type == CHUNK_GZIP) {
+      fprintf(stderr, "CHUNK %d:  gzip     patch offset %d\n", i, patch_offset);
+
+      // gzip chunks have an additional 40 + gzip_header_len + 8 bytes
+      // in their chunk header.
+      unsigned char* gzip = malloc(40);
+      if (fread(gzip, 1, 40, f) != 40) {
+        fprintf(stderr, "failed to read chunk %d initial gzip data\n", i);
+        return -1;
+      }
+      size_t gzip_header_len = Read4(gzip+36);
+      gzip = realloc(gzip, 40 + gzip_header_len + 8);
+      if (fread(gzip+40, 1, gzip_header_len+8, f) != gzip_header_len+8) {
+        fprintf(stderr, "failed to read chunk %d remaining gzip data\n", i);
+        return -1;
+      }
+
+      size_t expanded_len = Read8(gzip);
+      size_t target_len = Read8(gzip);
+      int gz_level = Read4(gzip+16);
+      int gz_method = Read4(gzip+20);
+      int gz_windowBits = Read4(gzip+24);
+      int gz_memLevel = Read4(gzip+28);
+      int gz_strategy = Read4(gzip+32);
+
+      // Decompress the source data; the chunk header tells us exactly
+      // how big we expect it to be when decompressed.
+
+      unsigned char* expanded_source = malloc(expanded_len);
+      if (expanded_source == NULL) {
+        fprintf(stderr, "failed to allocate %d bytes for expanded_source\n",
+                expanded_len);
+        return -1;
+      }
+
+      z_stream strm;
+      strm.zalloc = Z_NULL;
+      strm.zfree = Z_NULL;
+      strm.opaque = Z_NULL;
+      strm.avail_in = src_len - (gzip_header_len + 8);
+      strm.next_in = (unsigned char*)(old_data + src_start + gzip_header_len);
+      strm.avail_out = expanded_len;
+      strm.next_out = expanded_source;
+
+      int ret;
+      ret = inflateInit2(&strm, -15);
+      if (ret != Z_OK) {
+        fprintf(stderr, "failed to init source inflation: %d\n", ret);
+        return -1;
+      }
+
+      // Because we've provided enough room to accommodate the output
+      // data, we expect one call to inflate() to suffice.
+      ret = inflate(&strm, Z_SYNC_FLUSH);
+      if (ret != Z_STREAM_END) {
+        fprintf(stderr, "source inflation returned %d\n", ret);
+        return -1;
+      }
+      // We should have filled the output buffer exactly.
+      if (strm.avail_out != 0) {
+        fprintf(stderr, "source inflation short by %d bytes\n", strm.avail_out);
+        return -1;
+      }
+      inflateEnd(&strm);
+
+      // Next, apply the bsdiff patch (in memory) to the uncompressed
+      // data.
+      unsigned char* uncompressed_target_data;
+      ssize_t uncompressed_target_size;
+      if (ApplyBSDiffPatchMem(expanded_source, expanded_len,
+                              patch_filename, patch_offset,
+                              &uncompressed_target_data,
+                              &uncompressed_target_size) != 0) {
+        return -1;
+      }
+
+      // Now compress the target data and append it to the output.
+
+      // start with the gzip header.
+      fwrite(gzip+40, 1, gzip_header_len, output);
+      SHA_update(ctx, gzip+40, gzip_header_len);
+
+      // we're done with the expanded_source data buffer, so we'll
+      // reuse that memory to receive the output of deflate.
+      unsigned char* temp_data = expanded_source;
+      ssize_t temp_size = expanded_len;
+      if (temp_size < 32768) {
+        // ... unless the buffer is too small, in which case we'll
+        // allocate a fresh one.
+        free(temp_data);
+        temp_data = malloc(32768);
+        temp_size = 32768;
+      }
+
+      // now the deflate stream
+      strm.zalloc = Z_NULL;
+      strm.zfree = Z_NULL;
+      strm.opaque = Z_NULL;
+      strm.avail_in = uncompressed_target_size;
+      strm.next_in = uncompressed_target_data;
+      ret = deflateInit2(&strm, gz_level, gz_method, gz_windowBits,
+                         gz_memLevel, gz_strategy);
+      do {
+        strm.avail_out = temp_size;
+        strm.next_out = temp_data;
+        ret = deflate(&strm, Z_FINISH);
+        size_t have = temp_size - strm.avail_out;
+
+        if (fwrite(temp_data, 1, have, output) != have) {
+          fprintf(stderr, "failed to write %d compressed bytes to output\n",
+                  have);
+          return -1;
+        }
+        SHA_update(ctx, temp_data, have);
+      } while (ret != Z_STREAM_END);
+      deflateEnd(&strm);
+
+      // lastly, the gzip footer.
+      fwrite(gzip+40+gzip_header_len, 1, 8, output);
+      SHA_update(ctx, gzip+40+gzip_header_len, 8);
+
+      free(temp_data);
+      free(uncompressed_target_data);
+      free(gzip);
+    } else {
+      fprintf(stderr, "patch chunk %d is unknown type %d\n", i, type);
+      return -1;
+    }
+  }
+
+  return 0;
+}
diff --git a/tools/applypatch/main.c b/tools/applypatch/main.c
new file mode 100644
index 0000000..e25c730
--- /dev/null
+++ b/tools/applypatch/main.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+extern int applypatch(int argc, char** argv);
+
+// This program applies binary patches to files in a way that is safe
+// (the original file is not touched until we have the desired
+// replacement for it) and idempotent (it's okay to run this program
+// multiple times).
+//
+// - if the sha1 hash of <tgt-file> is <tgt-sha1>, does nothing and exits
+//   successfully.
+//
+// - otherwise, if the sha1 hash of <src-file> is <src-sha1>, applies the
+//   bsdiff <patch> to <src-file> to produce a new file (the type of patch
+//   is automatically detected from the file header).  If that new
+//   file has sha1 hash <tgt-sha1>, moves it to replace <tgt-file>, and
+//   exits successfully.  Note that if <src-file> and <tgt-file> are
+//   not the same, <src-file> is NOT deleted on success.  <tgt-file>
+//   may be the string "-" to mean "the same as src-file".
+//
+// - otherwise, or if any error is encountered, exits with non-zero
+//   status.
+//
+// <src-file> (or <file> in check mode) may refer to an MTD partition
+// to read the source data.  See the comments for the
+// LoadMTDContents() function above for the format of such a filename.
+
+int main(int argc, char** argv) {
+  int result = applypatch(argc, argv);
+  if (result == 2) {
+    fprintf(stderr,
+            "usage: %s <src-file> <tgt-file> <tgt-sha1> <tgt-size> "
+            "[<src-sha1>:<patch> ...]\n"
+            "   or  %s -c <file> [<sha1> ...]\n"
+            "   or  %s -s <bytes>\n"
+            "   or  %s -l\n"
+            "\n"
+            "Filenames may be of the form\n"
+            "  MTD:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
+            "to specify reading from or writing to an MTD partition.\n\n",
+            argv[0], argv[0], argv[0], argv[0]);
+  }
+  return result;
+}
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 4e99bf5..af5aa47 100755
--- a/tools/buildinfo.sh
+++ b/tools/buildinfo.sh
@@ -7,6 +7,7 @@
 echo "ro.build.display.id=$BUILD_DISPLAY_ID"
 echo "ro.build.version.incremental=$BUILD_NUMBER"
 echo "ro.build.version.sdk=$PLATFORM_SDK_VERSION"
+echo "ro.build.version.codename=$PLATFORM_VERSION_CODENAME"
 echo "ro.build.version.release=$PLATFORM_VERSION"
 echo "ro.build.date=`date`"
 echo "ro.build.date.utc=`date +%s`"
@@ -19,9 +20,11 @@
 echo "ro.product.name=$PRODUCT_NAME"
 echo "ro.product.device=$TARGET_DEVICE"
 echo "ro.product.board=$TARGET_BOOTLOADER_BOARD_NAME"
+echo "ro.product.cpu.abi=$TARGET_CPU_ABI"
 echo "ro.product.manufacturer=$PRODUCT_MANUFACTURER"
 echo "ro.product.locale.language=$PRODUCT_DEFAULT_LANGUAGE"
 echo "ro.product.locale.region=$PRODUCT_DEFAULT_REGION"
+echo "ro.wifi.channels=$PRODUCT_DEFAULT_WIFI_CHANNELS"
 echo "ro.board.platform=$TARGET_BOARD_PLATFORM"
 
 echo "# ro.build.product is obsolete; use ro.product.device"
diff --git a/tools/dexpreopt/afar/Android.mk b/tools/dexpreopt/afar/Android.mk
index d224675..9f1b987 100644
--- a/tools/dexpreopt/afar/Android.mk
+++ b/tools/dexpreopt/afar/Android.mk
@@ -24,6 +24,6 @@
 LOCAL_SHARED_LIBRARIES := libz
 
 LOCAL_MODULE := afar
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
diff --git a/tools/dexpreopt/dexopt-wrapper/Android.mk b/tools/dexpreopt/dexopt-wrapper/Android.mk
index e6ca389..ae2b6a3 100644
--- a/tools/dexpreopt/dexopt-wrapper/Android.mk
+++ b/tools/dexpreopt/dexopt-wrapper/Android.mk
@@ -31,6 +31,6 @@
 
 LOCAL_MODULE := dexopt-wrapper
 
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
diff --git a/tools/droiddoc/src/Comment.java b/tools/droiddoc/src/Comment.java
index 3a24357..3f1bf6c 100644
--- a/tools/droiddoc/src/Comment.java
+++ b/tools/droiddoc/src/Comment.java
@@ -47,6 +47,7 @@
             "@deprecated",
             "@undeprecate",
             "@docRoot",
+            "@sdkCurrent",
             "@inheritDoc",
             "@more",
             "@code",
diff --git a/tools/droiddoc/src/DocFile.java b/tools/droiddoc/src/DocFile.java
index f53a35c..b71c081 100644
--- a/tools/droiddoc/src/DocFile.java
+++ b/tools/droiddoc/src/DocFile.java
@@ -115,23 +115,30 @@
         TagInfo.makeHDF(hdf, "root.descr", tags);
 
         hdf.setValue("commentText", commentText);
-        
-        if (outfile.indexOf("sdk/") != -1) {
-            hdf.setValue("sdk", "true");
-            if (outfile.indexOf("index.html") != -1) {
-                ClearPage.write(hdf, "sdkpage.cs", outfile);
-            } else {
-                ClearPage.write(hdf, "docpage.cs", outfile);
-            }
-        } else if (outfile.indexOf("guide/") != -1){
-            hdf.setValue("guide", "true");
-            ClearPage.write(hdf, "docpage.cs", outfile);
-        } else if (outfile.indexOf("publish/") != -1){
-            hdf.setValue("publish", "true");
+
+        // write the page using the appropriate root template, based on the 
+        // whichdoc value supplied by build
+        String fromWhichmodule = hdf.getValue("android.whichmodule", "");
+        if (fromWhichmodule.equals("online-pdk")) {
+            //leaving this in just for temporary compatibility with pdk doc
+            hdf.setValue("online-pdk", "true");
+            // add any conditional login for root template here (such as 
+            // for custom left nav based on tab etc. 
             ClearPage.write(hdf, "docpage.cs", outfile);
         } else {
-            ClearPage.write(hdf, "nosidenavpage.cs", outfile);
+            if (outfile.indexOf("sdk/") != -1) {
+                hdf.setValue("sdk", "true");
+                if (outfile.indexOf("index.html") != -1) {
+                    ClearPage.write(hdf, "sdkpage.cs", outfile);
+                } else {
+                    ClearPage.write(hdf, "docpage.cs", outfile);
+                }
+            } else if (outfile.indexOf("guide/") != -1) {
+                hdf.setValue("guide", "true");
+                ClearPage.write(hdf, "docpage.cs", outfile);
+            } else {
+                ClearPage.write(hdf, "nosidenavpage.cs", outfile);
+            }
         }
-    }
-
+    } //writePage
 }
diff --git a/tools/droiddoc/templates-pdk/customization.cs b/tools/droiddoc/templates-pdk/customization.cs
index 8d3cde6..e2d6682 100644
--- a/tools/droiddoc/templates-pdk/customization.cs
+++ b/tools/droiddoc/templates-pdk/customization.cs
@@ -5,8 +5,8 @@
 def:custom_masthead() ?>
   <div id="header">
       <div id="headerLeft">
-          <a href="<?cs var:toroot ?>index.html" tabindex="-1"><img
-              src="<?cs var:toroot ?>assets/images/bg_logo.png" alt="Android Porting Development Kit" /></a>
+          <a href="<?cs var:toroot ?>guide/index.html" tabindex="-1"><img
+              src="<?cs var:toroot ?>assets/images/open_source.png" alt="Open Source Project: Platform Development Kit" /></a>
           <ul class="<?cs 
                   if:reference ?> <?cs
                   elif:guide ?> <?cs
@@ -15,17 +15,10 @@
                   elif:community ?> <?cs
                   elif:publish ?> <?cs
                   elif:about ?> <?cs /if ?>">
-              <li id="home-link"><a href="<?cs var:toroot ?><?cs 
-                  if:android.whichdoc != "online-pdk" ?>offline.html<?cs 
-                  else ?>index.html<?cs /if ?>">
-                  <span>Home</span></a></li>
-              <!--<li id="sdk-link"><a href="<?cs var:toroot ?>index.html"><span>SDK</span></a></li>-->
-              <li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html"
-                                  onClick="return loadLast('guide')"><span>Porting Guide</span></a></li>
-              <!--<li id="reference-link"><a href="<?cs var:toroot ?>reference/packages.html" 
-                                  onClick="return loadLast('reference')"><span>Reference</span></a></li> 
-              <li><a href="http://android-developers.blogspot.com"><span>Blog</span></a></li>
-              <li id="community-link"><a href="<?cs var:toroot ?>community/index.html"><span>Community</span></a></li>-->
+              <!--<li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html"
+                                  onClick="return loadLast('guide)'"><span>Dev Guide</span></a></li>
+              <li id="opensource-link"><a href="http://source.android.com/"
+				 onClick="return loadLast('open')"><span>Open Source</span></a></li>-->
           </ul> 
       </div>
       <div id="headerRight">
@@ -35,8 +28,7 @@
               <!-- &nbsp;<a href="#">English</a> | -->
               <a href="http://www.android.com">Android.com</a>
             </span>
-          </div><?cs 
-          call:default_search_box() ?>
+          </div>
       </div><!-- headerRight -->
   </div><!-- header --><?cs 
 /def ?><?cs # custom_masthead ?>
@@ -47,7 +39,7 @@
   <div class="g-section g-tpl-240" id="body-content">
     <div class="g-unit g-first side-nav-resizable" id="side-nav">
       <div id="devdoc-nav"><?cs 
-        include:"../../../../development/pdk/docs/html/guide/guide_toc.cs" ?>
+        include:"../../../../development/pdk/docs/guide/pdk_toc.cs" ?>
       </div>
     </div> <!-- end side-nav -->
     <script>
@@ -59,11 +51,7 @@
 
 <?cs 
 def:custom_left_nav() ?><?cs 
-  if:guide ?><?cs 
     call:guide_nav() ?><?cs 
-  else ?><?cs 
-    call:default_left_nav() ?><?cs 
-  /if ?><?cs 
 /def ?>
 
 <?cs # appears at the bottom of every page ?><?cs 
diff --git a/tools/droiddoc/templates-pdk/head_tag.cs b/tools/droiddoc/templates-pdk/head_tag.cs
index 1a7f1a8..47b332a 100644
--- a/tools/droiddoc/templates-pdk/head_tag.cs
+++ b/tools/droiddoc/templates-pdk/head_tag.cs
@@ -7,7 +7,7 @@
     if:sdk.version ?> (<?cs
       var:sdk.version ?>)<?cs
     /if ?> | <?cs
-  /if ?>Android Developers</title>
+  /if ?>Android Open Source</title>
 <link href="<?cs var:toroot ?>assets/android-developer-docs-devguide.css" rel="stylesheet" type="text/css" />
 <link href="<?cs var:toroot ?>assets-pdk/pdk-local.css" rel="stylesheet" type="text/css" />
 <script src="<?cs var:toroot ?>assets/search_autocomplete.js" type="text/javascript"></script>
diff --git a/tools/droiddoc/templates-sdk/customization.cs b/tools/droiddoc/templates-sdk/customization.cs
index 4a1dde6..0cb85e8 100644
--- a/tools/droiddoc/templates-sdk/customization.cs
+++ b/tools/droiddoc/templates-sdk/customization.cs
@@ -1,45 +1,74 @@
 <?cs # This default template file is meant to be replaced. ?>
 <?cs # Use the -tempatedir arg to javadoc to set your own directory with a replacement for this file in it. ?>
 
+
+<?cs # The default search box that goes in the header ?><?cs 
+def:default_search_box() ?>
+  <div id="search" >
+      <div id="searchForm">
+          <form accept-charset="utf-8" class="gsc-search-box" 
+                onsubmit="return submit_search()">
+            <table class="gsc-search-box" cellpadding="0" cellspacing="0"><tbody>
+                <tr>
+                  <td class="gsc-input">
+                    <input id="search_autocomplete" class="gsc-input" type="text" size="33" autocomplete="off" 
+                      title="search developer docs" name="q"
+                      value="search developer docs" 
+                      onFocus="search_focus_changed(this, true)" 
+                      onBlur="search_focus_changed(this, false)" 
+                      onkeydown="return search_changed(event, true, '<?cs var:toroot?>')" 
+                      onkeyup="return search_changed(event, false, '<?cs var:toroot?>')" />
+                  <div id="search_filtered_div" class="no-display">
+                      <table id="search_filtered" cellspacing=0>
+                      </table>
+                  </div>
+                  </td>
+                  <td class="gsc-search-button">
+                    <input type="submit" value="Search" title="search" id="search-button" class="gsc-search-button" />
+                  </td>
+                  <td class="gsc-clear-button">
+                    <div title="clear results" class="gsc-clear-button">&nbsp;</div>
+                  </td>
+                </tr></tbody>
+              </table>
+          </form>
+      </div><!-- searchForm -->
+  </div><!-- search --><?cs 
+/def ?>
+
 <?cs 
 def:custom_masthead() ?>
   <div id="header">
       <div id="headerLeft">
           <a href="<?cs var:toroot ?>index.html" tabindex="-1"><img
               src="<?cs var:toroot ?>assets/images/bg_logo.png" alt="Android Developers" /></a>
-          <ul class="<?cs 
-                  if:reference ?>reference<?cs
-                  elif:guide ?>guide<?cs
-                  elif:sdk ?>sdk<?cs
-                  elif:home ?>home<?cs
-                  elif:community ?>community<?cs
-                  elif:publish ?>publish<?cs
-                  elif:about ?>about<?cs /if ?>">
-              <li id="home-link"><a href="<?cs var:toroot ?><?cs 
-                  if:android.whichdoc != "online" ?>offline.html<?cs 
-                  else ?>index.html<?cs /if ?>">
-                  <span>Home</span></a></li>
-              <li id="sdk-link"><a href="<?cs var:toroot ?>sdk/1.1_r1/index.html"><span>SDK</span></a></li>
-              <li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html"
-                                  onClick="return loadLast('guide')"><span>Dev Guide</span></a></li>
-              <li id="reference-link"><a href="<?cs var:toroot ?>reference/packages.html" 
-                                  onClick="return loadLast('reference')"><span>Reference</span></a></li>
-              <li><a href="http://android-developers.blogspot.com"><span>Blog</span></a></li>
-              <li id="community-link"><a href="<?cs var:toroot ?>community/index.html"><span>Community</span></a></li>
-          </ul>
+          <?cs include:"header_tabs.cs" ?>     <?cs # The links are extracted so we can better manage localization ?>
       </div>
       <div id="headerRight">
           <div id="headerLinks">
-            <!-- <img src="<?cs var:toroot ?>assets/images/icon_world.jpg" alt="" /> -->
-            <span class="text">
-              <!-- &nbsp;<a href="#">English</a> | -->
-              <a href="http://www.android.com">Android.com</a>
-            </span>
+            <!-- 	<img src="<?cs var:toroot ?>assets/images/icon_world.jpg" alt="" />  -->
+              <span id="language">
+             		<select name="language" onChange="changeLangPref(this.value)">
+    							<option value="en">English</option>
+    				  	<!--  <option value="ja"></option>  -->
+             		</select>	
+             		<script type="text/javascript">
+             		  <!--  
+             		  loadLangPref();  
+             		  //-->
+             		</script>
+             	</span>
+            <a href="http://www.android.com">Android.com</a>
           </div><?cs 
           call:default_search_box() ?>
       </div><!-- headerRight -->
+      <script type="text/javascript">
+        <!--  
+        changeTabLang(getLangPref());  
+        //-->
+      </script>
   </div><!-- header --><?cs 
-/def ?><?cs # custom_masthead ?>
+/def ?>
 
 <?cs 
 def:sdk_nav() ?>
@@ -66,22 +95,80 @@
     </script>
 <?cs /def ?>
 
-<?cs 
-def:publish_nav() ?>
-  <div class="g-section g-tpl-180" id="body-content">
-    <div class="g-unit g-first" id="side-nav">
-      <div id="devdoc-nav"><?cs 
-        include:"../../../../frameworks/base/docs/html/publish/publish_toc.cs" ?>
-      </div>
+<?cs # The default side navigation for the reference docs ?><?cs 
+def:default_left_nav() ?>
+  <div class="g-section g-tpl-240" id="body-content">
+    <div class="g-unit g-first side-nav-resizable" id="side-nav">
+      <div id="swapper">
+        <div id="nav-panels">
+          <div id="resize-packages-nav">
+            <div id="packages-nav">
+              <div id="index-links"><nobr>
+                <a href="<?cs var:toroot ?>reference/packages.html" <?cs if:(page.title == "Package Index") ?>class="selected"<?cs /if ?> >Package Index</a> | 
+                <a href="<?cs var:toroot ?>reference/classes.html" <?cs if:(page.title == "Class Index") ?>class="selected"<?cs /if ?>>Class Index</a></nobr>
+              </div>
+              <ul><?cs 
+              each:pkg=docs.packages ?>
+                <li <?cs if:(class.package.name == pkg.name) || (package.name == pkg.name)?>class="selected"<?cs /if ?>><?cs call:package_link(pkg) ?></li><?cs 
+              /each ?>
+              </ul><br/>
+            </div> <!-- end packages -->
+          </div> <!-- end resize-packages -->
+          <div id="classes-nav"><?cs 
+            if:subcount(class.package) ?>
+            <ul>
+              <?cs call:list("Interfaces", class.package.interfaces) ?>
+              <?cs call:list("Classes", class.package.classes) ?>
+              <?cs call:list("Enums", class.package.enums) ?>
+              <?cs call:list("Exceptions", class.package.exceptions) ?>
+              <?cs call:list("Errors", class.package.errors) ?>
+            </ul><?cs 
+            elif:subcount(package) ?>
+            <ul>
+              <?cs call:class_link_list("Interfaces", package.interfaces) ?>
+              <?cs call:class_link_list("Classes", package.classes) ?>
+              <?cs call:class_link_list("Enums", package.enums) ?>
+              <?cs call:class_link_list("Exceptions", package.exceptions) ?>
+              <?cs call:class_link_list("Errors", package.errors) ?>
+            </ul><?cs 
+            else ?>
+              <script>
+                /*addLoadEvent(maxPackageHeight);*/
+              </script>
+              <p style="padding:10px">Select a package to view its members</p><?cs 
+            /if ?><br/>
+          </div><!-- end classes -->
+        </div><!-- end nav-panels -->
+        <div id="nav-tree" style="display:none">
+          <div id="index-links"><nobr>
+            <a href="<?cs var:toroot ?>reference/packages.html" <?cs if:(page.title == "Package Index") ?>class="selected"<?cs /if ?> >Package Index</a> | 
+            <a href="<?cs var:toroot ?>reference/classes.html" <?cs if:(page.title == "Class Index") ?>class="selected"<?cs /if ?>>Class Index</a></nobr>
+          </div>
+        </div><!-- end nav-tree -->
+      </div><!-- end swapper -->
     </div> <!-- end side-nav -->
-<?cs /def ?>
+    <script>
+      if (!isMobile) {
+        $("<a href='#' id='nav-swap' onclick='swapNav();return false;' style='font-size:10px;line-height:9px;margin-left:1em;text-decoration:none;'><span id='tree-link'>Use Tree Navigation</span><span id='panel-link' style='display:none'>Use Panel Navigation</span></a>").appendTo("#side-nav");
+        chooseDefaultNav();
+        if ($("#nav-tree").is(':visible')) init_navtree("nav-tree", "<?cs var:toroot ?>", NAVTREE_DATA);
+        else {
+          addLoadEvent(function() {
+            scrollIntoView("packages-nav");
+            scrollIntoView("classes-nav");
+          });
+        }
+        $("#swapper").css({borderBottom:"2px solid #aaa"});
+      } else {
+        swapNav(); // tree view should be used on mobile
+      }
+    </script><?cs 
+/def ?>
 
 <?cs 
 def:custom_left_nav() ?><?cs 
   if:guide ?><?cs 
     call:guide_nav() ?><?cs 
-  elif:publish ?><?cs 
-    call:publish_nav() ?><?cs 
   elif:sdk ?><?cs 
     call:sdk_nav() ?><?cs 
   else ?><?cs 
@@ -115,7 +202,7 @@
   </p><?cs 
 /def ?>
 
-<?cs # appears on the right side of the blue bar at the bottom of every page ?><?cs 
+<?cs # appears on the right side of the blue bar at the bottom off every page ?><?cs 
 def:custom_buildinfo() ?>
-  Android 1.1 r1 - <?cs var:page.now ?><?cs 
-/def ?>
+  Android <?cs var:sdk.version ?>&nbsp;r<?cs var:sdk.rel.id ?> - <?cs var:page.now ?>
+<?cs /def ?>
diff --git a/tools/droiddoc/templates-sdk/header_tabs.cs b/tools/droiddoc/templates-sdk/header_tabs.cs
new file mode 100644
index 0000000..12b747e
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/header_tabs.cs
@@ -0,0 +1,35 @@
+<ul id="header-tabs" class="<?cs 
+	if:reference ?>reference<?cs
+	elif:guide ?>guide<?cs
+	elif:sdk ?>sdk<?cs
+	elif:home ?>home<?cs
+	elif:community ?>community<?cs
+	elif:publish ?>publish<?cs
+	elif:about ?>about<?cs /if ?>">
+         
+	<li id="home-link"><a href="<?cs var:toroot ?><?cs if:android.whichdoc != "online" ?>offline.html<?cs else ?>index.html<?cs /if ?>">
+		<span class="en">Home</span>
+    <span class="ja"></span>
+	</a></li>
+	<li id="sdk-link"><a href="<?cs var:toroot ?>sdk/<?cs var:sdk.current ?>/index.html">
+		<span class="en">SDK</span>
+    <span class="ja"></span>
+	</a></li>
+	<li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html" onClick="return loadLast('guide')">
+		<span class="en">Dev Guide</span>
+    <span class="ja"></span>
+	</a></li>
+	<li id="reference-link"><a href="<?cs var:toroot ?>reference/packages.html" onClick="return loadLast('reference')">
+		<span class="en">Reference</span>
+    <span class="ja"></span>
+	</a></li>
+	<li><a href="http://android-developers.blogspot.com">
+		<span class="en">Blog</span>
+    <span class="ja"></span>
+	</a></li>
+	<li id="community-link"><a href="<?cs var:toroot ?>community/index.html">
+		<span class="en">Community</span>
+    <span class="ja"></span>
+	</a></li>
+     
+</ul>
\ No newline at end of file
diff --git a/tools/droiddoc/templates-sdk/sdkpage.cs b/tools/droiddoc/templates-sdk/sdkpage.cs
index b425da0..8bf30d1 100644
--- a/tools/droiddoc/templates-sdk/sdkpage.cs
+++ b/tools/droiddoc/templates-sdk/sdkpage.cs
@@ -4,7 +4,7 @@
 <?cs if:sdk.redirect ?>
   <head>
     <title>Redirecting...</title>
-    <meta http-equiv="refresh" content="0;url=<?cs var:toroot ?>sdk/<?cs var:sdk.redirect ?>/index.html">
+    <meta http-equiv="refresh" content="0;url=<?cs var:toroot ?>sdk/<?cs var:sdk.current ?>/index.html">
     <link href="<?cs var:toroot ?>assets/android-developer-docs.css" rel="stylesheet" type="text/css" />
   </head>
 <?cs else ?>
@@ -16,32 +16,33 @@
 
 <?cs call:sdk_nav() ?>
 
-  
-<div class="g-unit" id="doc-content" >
-
 <?cs if:sdk.redirect ?>
-  Redirecting to 
-  <a href="<?cs var:toroot ?>sdk/<?cs var:sdk.redirect ?>/index.html">
-  <?cs var:toroot ?>sdk/<?cs var:sdk.redirect ?>/index.html
-  </a>...
+<div class="g-unit">
+  <div id="jd-content">
+    <p>Redirecting to 
+    <a href="/sdk/<?cs var:sdk.current ?>/index.html">
+    /sdk/<?cs var:sdk.current ?>/index.html
+    </a></p>
 <?cs else ?>
-  
+<div class="g-unit" id="doc-content" >
   <div id="jd-header" class="guide-header" >
     <span class="crumb">&nbsp;</span>
     <h1><?cs if:android.whichdoc == "online" ?>Download <?cs /if ?><?cs var:page.title ?></h1>
   </div>
 
-
-<div id="jd-content">
-
-    <p><em>
-    <?cs var:sdk.date ?>
-    </em></p>
+  <div id="jd-content">
+    <p><em><?cs 
+    if:ndk ?><?cs 
+      var:ndk.date ?><?cs 
+    else ?><?cs 
+      var:sdk.date ?><?cs 
+    /if ?></em>
+    </p>
 
 <?cs if:sdk.not_latest_version ?>
   <div class="special">
     <p><strong>This is NOT the current Android SDK release.</strong></p>
-    <p>Use the links under <strong>Current SDK Release</strong>, on the left, to be directed to the current SDK.</p>
+    <p><a href="/sdk/<?cs var:sdk.current ?>/index.html">Download the current Android SDK</a></p>
   </div>
 <?cs /if ?>
   
@@ -51,8 +52,63 @@
 <p>The sections below provide an overview of the SDK package. </p>
 
 <?cs else ?>
+  <?cs if:ndk ?>
 
-<p>Before downloading, please read the <a href="<?cs var:toroot ?>sdk/<?cs var:sdk.version ?>/requirements.html">
+<p>The Android NDK is a companion tool to the Android SDK that lets Android
+application developers build performance-critical portions of their apps in
+native code. It is designed for use <em>only</em> in conjunction with the
+Android SDK, so if you have not already installed the Android 1.5 SDK, please do
+so before downloading the NDK. Also, please read <a href="">What is the Android
+NDK?</a> to get an understanding of what the NDK offers and whether it will be
+useful to you.</p>
+
+<p>Select the download package that is appropriate for your development
+computer. Note that separate download packages are provided for 32- and 64-bit
+Linux platforms.</p>
+  
+  <table class="download">
+    <tr>
+      <th>Platform</th>
+      <th>Package</th>
+      <th>Size</th>
+      <th>MD5 Checksum</th>
+  </tr>
+  <tr>
+    <td>Windows</td>
+    <td>
+  <a href="http://dl.google.com/android/<?cs var:ndk.win_download ?>"><?cs var:ndk.win_download ?></a>
+    </td>
+    <td><?cs var:ndk.win_bytes ?> bytes</td>
+    <td><?cs var:ndk.win_checksum ?></td>
+  </tr>
+  <tr class="alt-color">
+    <td>Mac OS X (intel)</td>
+    <td>
+  <a href="http://dl.google.com/android/<?cs var:ndk.mac_download ?>"><?cs var:ndk.mac_download ?></a>
+    </td>
+    <td><?cs var:ndk.mac_bytes ?> bytes</td>
+    <td><?cs var:ndk.mac_checksum ?></td>
+  </tr>
+  <tr>
+    <td>Linux 32-bit (i386)</td>
+    <td>
+  <a href="http://dl.google.com/android/<?cs var:ndk.linux_download ?>"><?cs var:ndk.linux_download ?></a>
+    </td>
+    <td><?cs var:ndk.linux_bytes ?> bytes</td>
+    <td><?cs var:ndk.linux_checksum ?></td>
+  </tr>
+  <tr class="alt-color">
+    <td>Linux 64-bit (x86_64)</td>
+    <td>
+  <a href="http://dl.google.com/android/<?cs var:ndk.linux_64_download ?>"><?cs var:ndk.linux_64_download ?></a>
+    </td>
+    <td><?cs var:ndk.linux_64_bytes ?> bytes</td>
+    <td><?cs var:ndk.linux_64_checksum ?></td>
+  </tr>
+  </table>
+
+  <?cs else ?>
+<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>
   
@@ -90,13 +146,17 @@
   </table>
 
 <?cs /if ?>
+<?cs /if ?>
 
       <?cs call:tag_list(root.descr) ?>
 
 <?cs /if ?>
 </div><!-- end jd-content -->
 
-<?cs include:"footer.cs" ?>
+<?cs if:!sdk.redirect ?>
+     <?cs include:"footer.cs" ?>
+<?cs /if ?>
+
 </div><!-- end doc-content -->
 
 <?cs include:"trailer.cs" ?>
diff --git a/tools/droiddoc/templates/assets/android-developer-core.css b/tools/droiddoc/templates/assets/android-developer-core.css
index 81e233a..8a1b9cd 100644
--- a/tools/droiddoc/templates/assets/android-developer-core.css
+++ b/tools/droiddoc/templates/assets/android-developer-core.css
@@ -110,11 +110,18 @@
   padding:0 0 0 2em;
 }
 
-li p, dd p {
+li p {
+  margin:.5em 0 0;
+}
+
+dd p {
   margin:1em 0 0;
 }
 
-li pre, li table, li img,
+li pre, li table, li img {
+  margin:.5em 0 0 1em;
+}
+
 dd pre, dd table, dd img {
   margin:1em 0 0 1em;
 }
@@ -169,7 +176,6 @@
   margin:0;
   position:relative;
   width:100%;
-  background: url('images/preliminary.png');
 }
 
 #header {
@@ -262,20 +268,16 @@
 #headerLinks {
   margin:10px 10px 0 0;
   height:13px;
-}
-
-#headerLinks .text {
-  text-decoration: none;
-  color: #7FA9B5;
   font-size: 11px;
   vertical-align: top;
 }
 
 #headerLinks a {
-  text-decoration: underline;
   color: #7FA9B5;
-  font-size: 11px;
-  vertical-align: top;
+}
+
+#language {
+  margin:0 10px;
 }
 
 #search {
@@ -717,14 +719,16 @@
   float: left;
   width: 584px;
   height: 580px;
-  background:url(images/home/bg_home_middle.png) no-repeat 0 0;
   position:relative;
 }
 
+#topAnnouncement {
+  background:url(images/home/bg_home_announcement.png) no-repeat 0 0;
+}
+  
 #homeTitle {
-  margin:15px 15px 0;
-  height:30px;
-  background:url(images/hr_gray_side.jpg) no-repeat 0 29px;
+  padding:15px 15px 0;
+  height:30px;  
 }
 
 #homeTitle h2 {
@@ -732,8 +736,14 @@
 }
 
 #announcement-block {
-  margin:15px 15px 0;
-  height:125px;
+  padding:0 15px 0;
+  overflow:hidden;
+  background: url(images/hr_gray_side.jpg) no-repeat 15px 0;
+  zoom:1;
+}
+
+#announcement-block>* {
+  padding:15px 0 0;
 }
 
 #announcement-block img {
@@ -746,6 +756,29 @@
   margin:0;
 }
 
+#carousel {
+  background:url(images/home/bg_home_carousel.png) no-repeat 0 0;
+  position:relative;
+  height:400px;
+}
+
+#carouselMain {
+  padding: 25px 21px 0;
+  height:185px;
+  background-position:top;
+  overflow:hidden;
+  position:relative;
+}
+
+#carouselMain img {
+  margin:0;
+}
+
+#homeMiddle p {
+  margin:0;
+  padding-bottom: 1em;
+}
+
 .clearer { clear:both; }
 
 #arrow-left, #arrow-right {
@@ -824,6 +857,12 @@
   text-decoration:none;
   text-align:center;
   font-size:11px;
+  line-height:11px;
+}
+
+#app-list a span {
+  position:relative;
+  top:-4px;
 }
 
 #app-list img {  
@@ -857,18 +896,6 @@
   padding-bottom:.25em;
 }
 
-#carouselMain {
-  margin: 25px 21px 0;
-  height:185px;
-  background-position:top;
-  background-repeat:no-repeat;
-  overflow:hidden;
-}
-
-#carouselMain img {
-  margin:0;
-}
-
 /*carousel bulletin layouts*/
 /*460px width*/
 /*185px height*/
@@ -877,24 +904,24 @@
   width:230px;
   height:165px;
   overflow:hidden;
-  margin:8px 0 8px 8px;
+  padding:8px 0 8px 8px;
 }
 .desc-right {
   float:left;
   width:270px;
-  margin:10px;
+  padding:10px;
 }
 .img-right {
   float:right;
   width:220px;
   height:165px;
   overflow:hidden;
-  margin:8px 8px 8px 0;
+  padding:8px 8px 8px 0;
 }
 .desc-left {
   float:right;
   width:280px;
-  margin:10px;
+  padding:10px;
   text-align:right;
 }
 .img-top {
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.css b/tools/droiddoc/templates/assets/android-developer-docs.css
index 747da92..f4aa5ef 100644
--- a/tools/droiddoc/templates/assets/android-developer-docs.css
+++ b/tools/droiddoc/templates/assets/android-developer-docs.css
@@ -433,11 +433,11 @@
 .nolist {
   list-style:none;
   padding:0;
-  margin:0 0 0 1em;
+  margin:0 0 1em 1em;
 }
 
 .nolist li {
-  padding:0;
+  padding:0 0 2px;
   margin:0;
 }
 
@@ -570,15 +570,7 @@
   margin:0 0 .5em;
   padding:0;
 }
-  
-/* old p.note, p.caution, p.warning {
-  margin:0 0 1em;
-  padding: 4px 10px;
-  background-color: #efefef;
-  border-top: 1px solid;  
-  border-bottom: 1px solid;
-}
-*/
+
 p.note, p.caution, p.warning {
   margin: 1em;
   padding: 0 0 0 .5em;
@@ -594,21 +586,20 @@
 p.note {
  border-color: #99aacc;
 }
-    
-p.caution {
-  border-color: #ffcc33;
-}
 
 p.warning {
   border-color: #aa0033;
 }
-  
-p.warning b, p.warning em, p.warning strong {
-  color: #aa0033;
+
+p.caution {
+  border-color: #ffcf00;
+}
+
+p.warning b, p.warning strong {
   font-weight: bold;
 }
 
-li p.note, li p.warning, li p.caution {
+li p.note, li p.warning {
   margin: .5em 0 0 0;  
   padding: .2em .5em .2em .9em;
 }
@@ -681,7 +672,7 @@
 
 #qv ol ol{
   list-style:none;
-  padding: 0 0 3px 12px;
+  padding: 0 0 0 12px;
   margin:0;
 }
 
@@ -690,11 +681,14 @@
 }
 
 #qv li {
-  padding: 0 10px;
-  margin: 2 0 0;
+  padding: 0 10px 3px;
   line-height: 1.2em;
 }
 
+#qv li li {
+  padding: 3px 10px 0;
+}
+
 #qv ul li {
   padding: 0 10px 0 0;
 }
@@ -810,6 +804,63 @@
 
 /* End sidebox sidebar element styles */
 
+/* BEGIN image and caption styles (originally for UI Guidelines docs) */
+
+table.image-caption {
+  padding:0;
+  margin:.5em 0;
+  border:0;
+}
+
+td.image-caption-i {
+  font-size:92%;
+  padding:0;
+  margin:0;
+  border:0;
+}
+
+td.image-caption-i img {
+  padding:0 1em;
+  margin:0;
+}
+
+.image-list {
+  width:24px;
+  text-align:center;
+}
+
+.image-list .caption {
+  margin:0 2px;
+}
+
+td.image-caption-c {
+  font-size:92%;
+  padding:1em 2px 2px 2px;
+  margin:0;
+  border:0;
+  width:350px;
+}
+
+.grad-rule-top {
+background-image:url(images/grad-rule-qv.png);
+background-repeat:no-repeat;
+padding-top:1em;
+margin-top:0;
+}
+
+.image-caption-nested {
+  margin-top:0;
+  padding:0 0 0 1em;
+}
+
+.image-caption-nested td {
+  padding:0 4px 2px 0;
+  margin:0;
+  border:0;
+}
+
+/* END image and caption styles */
+
 /* table of contents */
 
 ol.toc {
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.js b/tools/droiddoc/templates/assets/android-developer-docs.js
index 2a8c3bf..016fa4e 100644
--- a/tools/droiddoc/templates/assets/android-developer-docs.js
+++ b/tools/droiddoc/templates/assets/android-developer-docs.js
@@ -4,7 +4,7 @@
 var sidenav;
 var content;
 var HEADER_HEIGHT = 117;
-var cookie_style = 'android_developer';
+var cookie_namespace = 'android_developer';
 var NAV_PREF_TREE = "tree";
 var NAV_PREF_PANELS = "panels";
 var nav_pref;
@@ -70,8 +70,8 @@
   $("#nav-tree").css({height:swapperHeight + "px"});
 }
 
-function getCookie(cookie) {
-  var myCookie = cookie_style+"_"+cookie+"=";
+function readCookie(cookie) {
+  var myCookie = cookie_namespace+"_"+cookie+"=";
   if (document.cookie) {
     var index = document.cookie.indexOf(myCookie);
     if (index != -1) {
@@ -87,16 +87,15 @@
   return 0;
 }
 
-function writeCookie(cookie, val, path, expiration) {
+function writeCookie(cookie, val, section, expiration) {
   if (!val) return;  
-  var date = new Date();
-  date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
-  expiration = expiration ? expiration : date.toGMTString();
-  if (location.href.indexOf("/reference/") != -1) {
-    document.cookie = cookie_style+'_reference_'+cookie+'='+val+'; expires='+expiration+'; path='+'/'+path;
-  } else if (location.href.indexOf("/guide/") != -1) {
-    document.cookie = cookie_style+'_guide_'+cookie+'='+val+'; expires='+expiration+'; path='+'/'+path;
+  section = section == null ? "_" : "_"+section+"_";
+  if (expiration == null) {
+    var date = new Date();
+    date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
+    expiration = date.toGMTString();
   }
+  document.cookie = cookie_namespace+section+cookie+"="+val+"; expires="+expiration+"; path=/";
 } 
 
 function init() {
@@ -116,8 +115,8 @@
   if (!isMobile) {
     $("#resize-packages-nav").resizable({handles: "s", resize: function(e, ui) { resizeHeight(); } });
     $(".side-nav-resizable").resizable({handles: "e", resize: function(e, ui) { resizeWidth(); } });
-    var cookieWidth = getCookie(cookiePath+'width');
-    var cookieHeight = getCookie(cookiePath+'height');
+    var cookieWidth = readCookie(cookiePath+'width');
+    var cookieHeight = readCookie(cookiePath+'height');
     if (cookieWidth) {
       restoreWidth(cookieWidth);
     } else if ($(".side-nav-resizable").length) {
@@ -175,7 +174,9 @@
   $("#packages-nav").css({height:parseInt(resizePackagesNav.css("height")) - 6 + "px"}); //move 6px for handle
   devdocNav.css({height:sidenav.css("height")});
   $("#nav-tree").css({height:swapperHeight + "px"});
-  writeCookie("height", resizePackagesNav.css("height"), "", null);
+  
+  var section = location.pathname.substring(1,location.pathname.indexOf("/",1));
+  writeCookie("height", resizePackagesNav.css("height"), section, null);
 }
 
 function resizeWidth() {
@@ -190,7 +191,9 @@
   resizePackagesNav.css({width:sidenavWidth});
   classesNav.css({width:sidenavWidth});
   $("#packages-nav").css({width:sidenavWidth});
-  writeCookie("width", sidenavWidth, "", null);
+  
+  var section = location.pathname.substring(1,location.pathname.indexOf("/",1));
+  writeCookie("width", sidenavWidth, section, null);
 }
 
 function resizeAll() {
@@ -207,7 +210,7 @@
   if (location.indexOf("/"+cookiePath+"/") != -1) {
     return true;
   }
-  var lastPage = getCookie(cookiePath + "_lastpage");
+  var lastPage = readCookie(cookiePath + "_lastpage");
   if (lastPage) {
     window.location = lastPage;
     return false;
@@ -216,11 +219,11 @@
 }
 
 $(window).unload(function(){
-  var href = location.href;
-  if (href.indexOf("/reference/") != -1) {
-    writeCookie("lastpage", href, "", null);
-  } else if (href.indexOf("/guide/") != -1) {
-    writeCookie("lastpage", href, "", null);
+  var path = location.pathname;
+  if (path.indexOf("/reference/") != -1) {
+    writeCookie("lastpage", path, "reference", null);
+  } else if (path.indexOf("/guide/") != -1) {
+    writeCookie("lastpage", path, "guide", null);
   }
 });
 
@@ -257,7 +260,7 @@
 }
 
 function getNavPref() {
-  var v = getCookie('reference_nav');
+  var v = readCookie('reference_nav');
   if (v != NAV_PREF_TREE) {
     v = NAV_PREF_PANELS;
   }
@@ -283,7 +286,7 @@
   }
   var date = new Date();
   date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
-  writeCookie("nav", nav_pref, "", date.toGMTString());
+  writeCookie("nav", nav_pref, null, date.toGMTString());
 
   $("#nav-panels").toggle();
   $("#panel-link").toggle();
@@ -349,3 +352,57 @@
   }
   return false;
 }
+
+
+function changeTabLang(lang) {
+  var nodes = $("#header-tabs").find("."+lang);
+  for (i=0; i < nodes.length; i++) { // for each node in this language 
+    var node = $(nodes[i]);
+    node.siblings().css("display","none"); // hide all siblings 
+    if (node.not(":empty").length != 0) { //if this languages node has a translation, show it 
+      node.css("display","inline");
+    } else { //otherwise, show English instead 
+      node.css("display","none");
+      node.siblings().filter(".en").css("display","inline");
+    }
+  }
+}
+
+function changeNavLang(lang) {
+  var nodes = $("#side-nav").find("."+lang);
+  for (i=0; i < nodes.length; i++) { // for each node in this language 
+    var node = $(nodes[i]);
+    node.siblings().css("display","none"); // hide all siblings 
+    if (node.not(":empty").length != 0) { // if this languages node has a translation, show it 
+      node.css("display","inline");
+    } else { // otherwise, show English instead 
+      node.css("display","none");
+      node.siblings().filter(".en").css("display","inline");
+    }
+  }
+}
+
+function changeDocLang(lang) {
+  changeTabLang(lang);
+  changeNavLang(lang);
+}
+
+function changeLangPref(lang) {
+  var date = new Date();
+  date.setTime(date.getTime()+(50*365*24*60*60*1000)); // keep this for 50 years
+  writeCookie("pref_lang", lang, null, date);
+  
+  changeDocLang(lang);
+}
+
+function loadLangPref() {
+  var lang = readCookie("pref_lang");
+  if (lang != 0) {
+    $("#language").find("option[value='"+lang+"']").attr("selected",true);
+  }
+}
+
+function getLangPref() {
+  return $("#language").find(":selected").attr("value");
+}
+
diff --git a/tools/droiddoc/templates/assets/images/home/bg_home_announcement.png b/tools/droiddoc/templates/assets/images/home/bg_home_announcement.png
new file mode 100755
index 0000000..91485ff
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/bg_home_announcement.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/bg_home_carousel.png b/tools/droiddoc/templates/assets/images/home/bg_home_carousel.png
new file mode 100755
index 0000000..5ce5e30
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/bg_home_carousel.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/bg_home_middle.png b/tools/droiddoc/templates/assets/images/home/bg_home_middle.png
deleted file mode 100644
index 4221e96..0000000
--- a/tools/droiddoc/templates/assets/images/home/bg_home_middle.png
+++ /dev/null
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_guidelines_logo.png b/tools/droiddoc/templates/assets/images/icon_guidelines_logo.png
new file mode 100644
index 0000000..9362c8f
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_guidelines_logo.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/open_source.png b/tools/droiddoc/templates/assets/images/open_source.png
new file mode 100755
index 0000000..12bb1fb
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/open_source.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/uiguidelines1.png b/tools/droiddoc/templates/assets/images/uiguidelines1.png
new file mode 100644
index 0000000..5ce1611
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/uiguidelines1.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/search_autocomplete.js b/tools/droiddoc/templates/assets/search_autocomplete.js
index 2e12e0f..929751f 100644
--- a/tools/droiddoc/templates/assets/search_autocomplete.js
+++ b/tools/droiddoc/templates/assets/search_autocomplete.js
@@ -168,6 +168,6 @@
 
 function submit_search() {
   var query = document.getElementById('search_autocomplete').value;
-  document.location = '/search.html#q=' + query; 
+  document.location = toRoot + 'search.html#q=' + query; // toRoot is initialized in android-developer-docs.js 
   return false;
 }
diff --git a/tools/droiddoc/templates/customization.cs b/tools/droiddoc/templates/customization.cs
index 3646495..1988a89 100644
--- a/tools/droiddoc/templates/customization.cs
+++ b/tools/droiddoc/templates/customization.cs
@@ -2,6 +2,10 @@
 <?cs # Use the -templatedir arg to javadoc to set your own directory with a     ?>
 <?cs # replacement for this file in it. ?>
 
+
+<?cs def:default_search_box() ?><?cs /def ?>
+<?cs def:default_left_nav() ?><?cs /def ?>
+
 <?cs # appears at the top of every page ?><?cs 
 def:custom_masthead() ?>
   <div id="header">
@@ -9,7 +13,9 @@
           <a href="<?cs var:toroot ?>index.html" tabindex="-1"><?cs var:page.title ?></a>
       </div>
       <div id="headerRight">
-          <?cs call:default_search_box() ?>
+          <?cs if:!online-pdk ?>
+            <?cs call:default_search_box() ?>
+          <?cs /if ?>
       </div><!-- headerRight -->
   </div><!-- header --><?cs 
 /def ?>
@@ -21,4 +27,4 @@
 <?cs def:custom_buildinfo() ?>Build <?cs var:page.build ?> - <?cs var:page.now ?><?cs /def ?>
 
 <?cs # appears on the side of the page ?>
-<?cs def:custom_left_nav() ?><?cs call:default_left_nav() ?><?cs /def ?>
+<?cs def:custom_left_nav() ?><?cs call:default_left_nav() ?><?cs /def ?>
\ No newline at end of file
diff --git a/tools/droiddoc/templates/head_tag.cs b/tools/droiddoc/templates/head_tag.cs
index fea89b6..61559e1 100644
--- a/tools/droiddoc/templates/head_tag.cs
+++ b/tools/droiddoc/templates/head_tag.cs
@@ -3,10 +3,7 @@
 <link rel="shortcut icon" type="image/x-icon" href="<?cs var:toroot ?>favicon.ico" />
 <title><?cs 
   if:page.title ?><?cs 
-    var:page.title ?><?cs
-    if:sdk.version ?> (<?cs
-      var:sdk.version ?>)<?cs
-    /if ?> | <?cs
+    var:page.title ?> | <?cs
   /if ?>Android Developers</title><?cs 
 if:guide||sdk ?>
 <link href="<?cs var:toroot ?>assets/android-developer-docs-devguide.css" rel="stylesheet" type="text/css" /><?cs 
diff --git a/tools/droiddoc/templates/macros.cs b/tools/droiddoc/templates/macros.cs
index aec9c22..a2306a0 100644
--- a/tools/droiddoc/templates/macros.cs
+++ b/tools/droiddoc/templates/macros.cs
@@ -61,6 +61,9 @@
       elif:tag.name == "@sample" ?><pre class="Code prettyprint"><?cs var:tag.text ?></pre><?cs
       elif:tag.name == "@include" ?><?cs var:tag.text ?><?cs
       elif:tag.kind == "@docRoot" ?><?cs var:toroot ?><?cs
+      elif:tag.kind == "@sdkCurrent" ?><?cs var:sdk.current ?><?cs
+      elif:tag.kind == "@sdkCurrentVersion" ?><?cs var:sdk.version ?><?cs
+      elif:tag.kind == "@sdkCurrentRelId" ?><?cs var:sdk.rel.id ?><?cs
       elif:tag.kind == "@inheritDoc" ?><?cs # This is the case when @inheritDoc is in something
                                               that doesn't inherit from anything?><?cs
       elif:tag.kind == "@attr" ?><?cs
@@ -230,108 +233,5 @@
   </div><?cs 
 /def ?>
 
-<?cs # The default side navigation for the reference docs ?><?cs 
-def:default_left_nav() ?>
-  <div class="g-section g-tpl-240" id="body-content">
-    <div class="g-unit g-first side-nav-resizable" id="side-nav">
-      <div id="swapper">
-        <div id="nav-panels">
-          <div id="resize-packages-nav">
-            <div id="packages-nav">
-              <div id="index-links"><nobr>
-                <a href="<?cs var:toroot ?>reference/packages.html" <?cs if:(page.title == "Package Index") ?>class="selected"<?cs /if ?> >Package Index</a> | 
-                <a href="<?cs var:toroot ?>reference/classes.html" <?cs if:(page.title == "Class Index") ?>class="selected"<?cs /if ?>>Class Index</a></nobr>
-              </div>
-              <ul><?cs 
-              each:pkg=docs.packages ?>
-                <li <?cs if:(class.package.name == pkg.name) || (package.name == pkg.name)?>class="selected"<?cs /if ?>><?cs call:package_link(pkg) ?></li><?cs 
-              /each ?>
-              </ul><br/>
-            </div> <!-- end packages -->
-          </div> <!-- end resize-packages -->
-          <div id="classes-nav"><?cs 
-            if:subcount(class.package) ?>
-            <ul>
-              <?cs call:list("Interfaces", class.package.interfaces) ?>
-              <?cs call:list("Classes", class.package.classes) ?>
-              <?cs call:list("Enums", class.package.enums) ?>
-              <?cs call:list("Exceptions", class.package.exceptions) ?>
-              <?cs call:list("Errors", class.package.errors) ?>
-            </ul><?cs 
-            elif:subcount(package) ?>
-            <ul>
-              <?cs call:class_link_list("Interfaces", package.interfaces) ?>
-              <?cs call:class_link_list("Classes", package.classes) ?>
-              <?cs call:class_link_list("Enums", package.enums) ?>
-              <?cs call:class_link_list("Exceptions", package.exceptions) ?>
-              <?cs call:class_link_list("Errors", package.errors) ?>
-            </ul><?cs 
-            else ?>
-              <script>
-                /*addLoadEvent(maxPackageHeight);*/
-              </script>
-              <p style="padding:10px">Select a package to view its members</p><?cs 
-            /if ?><br/>
-          </div><!-- end classes -->
-        </div><!-- end nav-panels -->
-        <div id="nav-tree" style="display:none">
-          <div id="index-links"><nobr>
-            <a href="<?cs var:toroot ?>reference/packages.html" <?cs if:(page.title == "Package Index") ?>class="selected"<?cs /if ?> >Package Index</a> | 
-            <a href="<?cs var:toroot ?>reference/classes.html" <?cs if:(page.title == "Class Index") ?>class="selected"<?cs /if ?>>Class Index</a></nobr>
-          </div>
-        </div><!-- end nav-tree -->
-      </div><!-- end swapper -->
-    </div> <!-- end side-nav -->
-    <script>
-      if (!isMobile) {
-        $("<a href='#' id='nav-swap' onclick='swapNav();return false;' style='font-size:10px;line-height:9px;margin-left:1em;text-decoration:none;'><span id='tree-link'>Use Tree Navigation</span><span id='panel-link' style='display:none'>Use Panel Navigation</span></a>").appendTo("#side-nav");
-        chooseDefaultNav();
-        if ($("#nav-tree").is(':visible')) init_navtree("nav-tree", "<?cs var:toroot ?>", NAVTREE_DATA);
-        else {
-          addLoadEvent(function() {
-            scrollIntoView("packages-nav");
-            scrollIntoView("classes-nav");
-          });
-        }
-        $("#swapper").css({borderBottom:"2px solid #aaa"});
-      } else {
-        swapNav(); // tree view should be used on mobile
-      }
-    </script><?cs 
-/def ?>
-
-<?cs # The default search box that goes in the header ?><?cs 
-def:default_search_box() ?>
-  <div id="search" >
-      <div id="searchForm">
-          <form accept-charset="utf-8" class="gsc-search-box" 
-                onsubmit="return submit_search()">
-            <table class="gsc-search-box" cellpadding="0" cellspacing="0"><tbody>
-                <tr>
-                  <td class="gsc-input">
-                    <input id="search_autocomplete" class="gsc-input" type="text" size="33" autocomplete="off" 
-                      title="search developer docs" name="q"
-                      value="search developer docs" 
-                      onFocus="search_focus_changed(this, true)" 
-                      onBlur="search_focus_changed(this, false)" 
-                      onkeydown="return search_changed(event, true, '<?cs var:toroot?>')" 
-                      onkeyup="return search_changed(event, false, '<?cs var:toroot?>')" />
-                  <div id="search_filtered_div" class="no-display">
-                      <table id="search_filtered" cellspacing=0>
-                      </table>
-                  </div>
-                  </td>
-                  <td class="gsc-search-button">
-                    <input type="submit" value="Search" title="search" id="search-button" class="gsc-search-button" />
-                  </td>
-                  <td class="gsc-clear-button">
-                    <div title="clear results" class="gsc-clear-button">&nbsp;</div>
-                  </td>
-                </tr></tbody>
-              </table>
-          </form>
-      </div><!-- searchForm -->
-  </div><!-- search --><?cs 
-/def ?>
 
 <?cs include:"customization.cs" ?>
diff --git a/tools/droiddoc/templates/sample.cs b/tools/droiddoc/templates/sample.cs
index 7ab5dc9..e919111 100644
--- a/tools/droiddoc/templates/sample.cs
+++ b/tools/droiddoc/templates/sample.cs
@@ -18,7 +18,7 @@
 
 <div id="jd-content">
 
-<p><a href="<?cs var:realFile ?>">Original <?cs var:realFile ?></a></p>
+<p>The file containing the source code shown below is located in the corresponding directory in <code>&lt;sdk&gt;/platforms/android-&lt;version&gt;/samples/...</code></p>
 
 <!-- begin file contents -->
 <pre class="Code prettyprint"><?cs var:fileContents ?></pre>
diff --git a/tools/releasetools/amend_generator.py b/tools/releasetools/amend_generator.py
new file mode 100644
index 0000000..8341599
--- /dev/null
+++ b/tools/releasetools/amend_generator.py
@@ -0,0 +1,205 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+import common
+
+class AmendGenerator(object):
+  """Class to generate scripts in the 'amend' recovery script language
+  used up through cupcake."""
+
+  def __init__(self):
+    self.script = ['assert compatible_with("0.2") == "true"']
+    self.included_files = set()
+
+  def MakeTemporary(self):
+    """Make a temporary script object whose commands can latter be
+    appended to the parent script with AppendScript().  Used when the
+    caller wants to generate script commands out-of-order."""
+    x = AmendGenerator()
+    x.script = []
+    x.included_files = self.included_files
+    return x
+
+  @staticmethod
+  def _FileRoot(fn):
+    """Convert a file path to the 'root' notation used by amend."""
+    if fn.startswith("/system/"):
+      return "SYSTEM:" + fn[8:]
+    elif fn == "/system":
+      return "SYSTEM:"
+    elif fn.startswith("/tmp/"):
+      return "CACHE:.." + fn
+    else:
+      raise ValueError("don't know root for \"%s\"" % (fn,))
+
+  @staticmethod
+  def _PartitionRoot(partition):
+    """Convert a partition name to the 'root' notation used by amend."""
+    if partition == "userdata":
+      return "DATA:"
+    else:
+      return partition.upper() + ":"
+
+  def AppendScript(self, other):
+    """Append the contents of another script (which should be created
+    with temporary=True) to this one."""
+    self.script.extend(other.script)
+    self.included_files.update(other.included_files)
+
+  def AssertSomeFingerprint(self, *fp):
+    """Assert that the current fingerprint is one of *fp."""
+    x = [('file_contains("SYSTEM:build.prop", '
+          '"ro.build.fingerprint=%s") == "true"') % i for i in fp]
+    self.script.append("assert %s" % (" || ".join(x),))
+
+  def AssertOlderBuild(self, timestamp):
+    """Assert that the build on the device is older (or the same as)
+    the given timestamp."""
+    self.script.append("run_program PACKAGE:check_prereq %s" % (timestamp,))
+    self.included_files.add("check_prereq")
+
+  def AssertDevice(self, device):
+    """Assert that the device identifier is the given string."""
+    self.script.append('assert getprop("ro.product.device") == "%s" || '
+                       'getprop("ro.build.product") == "%s"' % (device, device))
+
+  def AssertSomeBootloader(self, *bootloaders):
+    """Asert that the bootloader version is one of *bootloaders."""
+    self.script.append("assert " +
+                  " || ".join(['getprop("ro.bootloader") == "%s"' % (b,)
+                               for b in bootloaders]))
+
+  def ShowProgress(self, frac, dur):
+    """Update the progress bar, advancing it over 'frac' over the next
+    'dur' seconds."""
+    self.script.append("show_progress %f %d" % (frac, int(dur)))
+
+  def PatchCheck(self, filename, *sha1):
+    """Check that the given file (or MTD reference) has one of the
+    given *sha1 hashes."""
+    out = ["run_program PACKAGE:applypatch -c %s" % (filename,)]
+    for i in sha1:
+      out.append(" " + i)
+    self.script.append("".join(out))
+    self.included_files.add("applypatch")
+
+  def CacheFreeSpaceCheck(self, amount):
+    """Check that there's at least 'amount' space that can be made
+    available on /cache."""
+    self.script.append("run_program PACKAGE:applypatch -s %d" % (amount,))
+    self.included_files.add("applypatch")
+
+  def Mount(self, kind, what, path):
+    # no-op; amend uses it's 'roots' system to automatically mount
+    # things when they're referred to
+    pass
+
+  def UnpackPackageDir(self, src, dst):
+    """Unpack a given directory from the OTA package into the given
+    destination directory."""
+    dst = self._FileRoot(dst)
+    self.script.append("copy_dir PACKAGE:%s %s" % (src, dst))
+
+  def Comment(self, comment):
+    """Write a comment into the update script."""
+    self.script.append("")
+    for i in comment.split("\n"):
+      self.script.append("# " + i)
+    self.script.append("")
+
+  def Print(self, message):
+    """Log a message to the screen (if the logs are visible)."""
+    # no way to do this from amend; substitute a script comment instead
+    self.Comment(message)
+
+  def FormatPartition(self, partition):
+    """Format the given MTD partition."""
+    self.script.append("format %s" % (self._PartitionRoot(partition),))
+
+  def DeleteFiles(self, file_list):
+    """Delete all files in file_list."""
+    line = []
+    t = 0
+    for i in file_list:
+      i = self._FileRoot(i)
+      line.append(i)
+      t += len(i) + 1
+      if t > 80:
+        self.script.append("delete " + " ".join(line))
+        line = []
+        t = 0
+    if line:
+      self.script.append("delete " + " ".join(line))
+
+  def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs):
+    """Apply binary patches (in *patchpairs) to the given srcfile to
+    produce tgtfile (which may be "-" to indicate overwriting the
+    source file."""
+    if len(patchpairs) % 2 != 0:
+      raise ValueError("bad patches given to ApplyPatch")
+    self.script.append(
+        ("run_program PACKAGE:applypatch %s %s %s %d " %
+         (srcfile, tgtfile, tgtsha1, tgtsize)) +
+        " ".join(["%s:%s" % patchpairs[i:i+2]
+                  for i in range(0, len(patchpairs), 2)]))
+    self.included_files.add("applypatch")
+
+  def WriteFirmwareImage(self, kind, fn):
+    """Arrange to update the given firmware image (kind must be
+    "hboot" or "radio") when recovery finishes."""
+    self.script.append("write_%s_image PACKAGE:%s" % (kind, fn))
+
+  def WriteRawImage(self, partition, fn):
+    """Write the given file into the given MTD partition."""
+    self.script.append("write_raw_image PACKAGE:%s %s" %
+                       (fn, self._PartitionRoot(partition)))
+
+  def SetPermissions(self, fn, uid, gid, mode):
+    """Set file ownership and permissions."""
+    fn = self._FileRoot(fn)
+    self.script.append("set_perm %d %d 0%o %s" % (uid, gid, mode, fn))
+
+  def SetPermissionsRecursive(self, fn, uid, gid, dmode, fmode):
+    """Recursively set path ownership and permissions."""
+    fn = self._FileRoot(fn)
+    self.script.append("set_perm_recursive %d %d 0%o 0%o %s" %
+                       (uid, gid, dmode, fmode, fn))
+
+  def MakeSymlinks(self, symlink_list):
+    """Create symlinks, given a list of (dest, link) pairs."""
+    self.script.extend(["symlink %s %s" % (i[0], self._FileRoot(i[1]))
+                        for i in sorted(symlink_list)])
+
+  def AppendExtra(self, extra):
+    """Append text verbatim to the output script."""
+    self.script.append(extra)
+
+  def AddToZip(self, input_zip, output_zip, input_path=None):
+    """Write the accumulated script to the output_zip file.  input_zip
+    is used as the source for any ancillary binaries needed by the
+    script.  If input_path is not None, it will be used as a local
+    path for binaries instead of input_zip."""
+    common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-script",
+                       "\n".join(self.script) + "\n")
+    for i in self.included_files:
+      try:
+        if input_path is None:
+          data = input_zip.read(os.path.join("OTA/bin", i))
+        else:
+          data = open(os.path.join(input_path, i)).read()
+        common.ZipWriteStr(output_zip, i, data, perms=0755)
+      except (IOError, KeyError), e:
+        raise ExternalError("unable to include binary %s: %s" % (i, e))
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 705ed84..4174db5 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -12,14 +12,17 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import errno
 import getopt
 import getpass
+import imp
 import os
 import re
 import shutil
 import subprocess
 import sys
 import tempfile
+import zipfile
 
 # missing in Python 2.4 and before
 if not hasattr(os, "SEEK_SET"):
@@ -27,11 +30,11 @@
 
 class Options(object): pass
 OPTIONS = Options()
-OPTIONS.signapk_jar = "out/host/linux-x86/framework/signapk.jar"
+OPTIONS.search_path = "out/host/linux-x86"
 OPTIONS.max_image_size = {}
 OPTIONS.verbose = False
 OPTIONS.tempfiles = []
-
+OPTIONS.device_specific = None
 
 class ExternalError(RuntimeError): pass
 
@@ -61,40 +64,62 @@
 def BuildAndAddBootableImage(sourcedir, targetname, output_zip):
   """Take a kernel, cmdline, and ramdisk directory from the input (in
   'sourcedir'), and turn them into a boot image.  Put the boot image
-  into the output zip file under the name 'targetname'."""
+  into the output zip file under the name 'targetname'.  Returns
+  targetname on success or None on failure (if sourcedir does not
+  appear to contain files for the requested image)."""
 
   print "creating %s..." % (targetname,)
 
   img = BuildBootableImage(sourcedir)
+  if img is None:
+    return None
 
   CheckSize(img, targetname)
-  output_zip.writestr(targetname, img)
+  ZipWriteStr(output_zip, targetname, img)
+  return targetname
 
 def BuildBootableImage(sourcedir):
   """Take a kernel, cmdline, and ramdisk directory from the input (in
-  'sourcedir'), and turn them into a boot image.  Return the image data."""
+  'sourcedir'), and turn them into a boot image.  Return the image
+  data, or None if sourcedir does not appear to contains files for
+  building the requested image."""
+
+  if (not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK) or
+      not os.access(os.path.join(sourcedir, "kernel"), os.F_OK)):
+    return None
 
   ramdisk_img = tempfile.NamedTemporaryFile()
   img = tempfile.NamedTemporaryFile()
 
   p1 = Run(["mkbootfs", os.path.join(sourcedir, "RAMDISK")],
            stdout=subprocess.PIPE)
-  p2 = Run(["gzip", "-n"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
+  p2 = Run(["minigzip"],
+           stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
 
   p2.wait()
   p1.wait()
   assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (targetname,)
-  assert p2.returncode == 0, "gzip of %s ramdisk failed" % (targetname,)
+  assert p2.returncode == 0, "minigzip of %s ramdisk failed" % (targetname,)
 
-  cmdline = open(os.path.join(sourcedir, "cmdline")).read().rstrip("\n")
-  p = Run(["mkbootimg",
-           "--kernel", os.path.join(sourcedir, "kernel"),
-           "--cmdline", cmdline,
-           "--ramdisk", ramdisk_img.name,
-           "--output", img.name],
-          stdout=subprocess.PIPE)
+  cmd = ["mkbootimg", "--kernel", os.path.join(sourcedir, "kernel")]
+
+  fn = os.path.join(sourcedir, "cmdline")
+  if os.access(fn, os.F_OK):
+    cmd.append("--cmdline")
+    cmd.append(open(fn).read().rstrip("\n"))
+
+  fn = os.path.join(sourcedir, "base")
+  if os.access(fn, os.F_OK):
+    cmd.append("--base")
+    cmd.append(open(fn).read().rstrip("\n"))
+
+  cmd.extend(["--ramdisk", ramdisk_img.name,
+              "--output", img.name])
+
+  p = Run(cmd, stdout=subprocess.PIPE)
   p.communicate()
-  assert p.returncode == 0, "mkbootimg of %s image failed" % (targetname,)
+  assert p.returncode == 0, "mkbootimg of %s image failed" % (
+      os.path.basename(sourcedir),)
 
   img.seek(os.SEEK_SET, 0)
   data = img.read()
@@ -131,22 +156,30 @@
   those which require them.  Return a {key: password} dict.  password
   will be None if the key has no password."""
 
-  key_passwords = {}
+  no_passwords = []
+  need_passwords = []
   devnull = open("/dev/null", "w+b")
   for k in sorted(keylist):
-    p = subprocess.Popen(["openssl", "pkcs8", "-in", k+".pk8",
-                          "-inform", "DER", "-nocrypt"],
-                         stdin=devnull.fileno(),
-                         stdout=devnull.fileno(),
-                         stderr=subprocess.STDOUT)
+    # An empty-string key is used to mean don't re-sign this package.
+    # Obviously we don't need a password for this non-key.
+    if not k:
+      no_passwords.append(k)
+      continue
+
+    p = Run(["openssl", "pkcs8", "-in", k+".pk8",
+             "-inform", "DER", "-nocrypt"],
+            stdin=devnull.fileno(),
+            stdout=devnull.fileno(),
+            stderr=subprocess.STDOUT)
     p.communicate()
     if p.returncode == 0:
-      print "%s.pk8 does not require a password" % (k,)
-      key_passwords[k] = None
+      no_passwords.append(k)
     else:
-      key_passwords[k] = getpass.getpass("Enter password for %s.pk8> " % (k,))
+      need_passwords.append(k)
   devnull.close()
-  print
+
+  key_passwords = PasswordManager().GetPasswords(need_passwords)
+  key_passwords.update(dict.fromkeys(no_passwords, None))
   return key_passwords
 
 
@@ -167,12 +200,13 @@
   else:
     sign_name = output_name
 
-  p = subprocess.Popen(["java", "-jar", OPTIONS.signapk_jar,
-                        key + ".x509.pem",
-                        key + ".pk8",
-                        input_name, sign_name],
-                       stdin=subprocess.PIPE,
-                       stdout=subprocess.PIPE)
+  p = Run(["java", "-jar",
+           os.path.join(OPTIONS.search_path, "framework", "signapk.jar"),
+           key + ".x509.pem",
+           key + ".pk8",
+           input_name, sign_name],
+          stdin=subprocess.PIPE,
+          stdout=subprocess.PIPE)
   if password is not None:
     password += "\n"
   p.communicate(password)
@@ -180,7 +214,7 @@
     raise ExternalError("signapk.jar failed: return code %s" % (p.returncode,))
 
   if align:
-    p = subprocess.Popen(["zipalign", "-f", str(align), sign_name, output_name])
+    p = Run(["zipalign", "-f", str(align), sign_name, output_name])
     p.communicate()
     if p.returncode != 0:
       raise ExternalError("zipalign failed: return code %s" % (p.returncode,))
@@ -209,8 +243,12 @@
 
 COMMON_DOCSTRING = """
   -p  (--path)  <dir>
-      Prepend <dir> to the list of places to search for binaries run
-      by this script.
+      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.
@@ -236,8 +274,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), "**"
@@ -252,15 +291,15 @@
     elif o in ("-v", "--verbose"):
       OPTIONS.verbose = True
     elif o in ("-p", "--path"):
-      os.environ["PATH"] = a + os.pathsep + os.environ["PATH"]
-      path_specified = True
+      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,)
 
-  if not path_specified:
-    os.environ["PATH"] = ("out/host/linux-x86/bin" + os.pathsep +
-                          os.environ["PATH"])
+  os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
+                        os.pathsep + os.environ["PATH"])
 
   return args
 
@@ -271,3 +310,176 @@
       shutil.rmtree(i)
     else:
       os.remove(i)
+
+
+class PasswordManager(object):
+  def __init__(self):
+    self.editor = os.getenv("EDITOR", None)
+    self.pwfile = os.getenv("ANDROID_PW_FILE", None)
+
+  def GetPasswords(self, items):
+    """Get passwords corresponding to each string in 'items',
+    returning a dict.  (The dict may have keys in addition to the
+    values in 'items'.)
+
+    Uses the passwords in $ANDROID_PW_FILE if available, letting the
+    user edit that file to add more needed passwords.  If no editor is
+    available, or $ANDROID_PW_FILE isn't define, prompts the user
+    interactively in the ordinary way.
+    """
+
+    current = self.ReadFile()
+
+    first = True
+    while True:
+      missing = []
+      for i in items:
+        if i not in current or not current[i]:
+          missing.append(i)
+      # Are all the passwords already in the file?
+      if not missing: return current
+
+      for i in missing:
+        current[i] = ""
+
+      if not first:
+        print "key file %s still missing some passwords." % (self.pwfile,)
+        answer = raw_input("try to edit again? [y]> ").strip()
+        if answer and answer[0] not in 'yY':
+          raise RuntimeError("key passwords unavailable")
+      first = False
+
+      current = self.UpdateAndReadFile(current)
+
+  def PromptResult(self, current):
+    """Prompt the user to enter a value (password) for each key in
+    'current' whose value is fales.  Returns a new dict with all the
+    values.
+    """
+    result = {}
+    for k, v in sorted(current.iteritems()):
+      if v:
+        result[k] = v
+      else:
+        while True:
+          result[k] = getpass.getpass("Enter password for %s key> "
+                                      % (k,)).strip()
+          if result[k]: break
+    return result
+
+  def UpdateAndReadFile(self, current):
+    if not self.editor or not self.pwfile:
+      return self.PromptResult(current)
+
+    f = open(self.pwfile, "w")
+    os.chmod(self.pwfile, 0600)
+    f.write("# Enter key passwords between the [[[ ]]] brackets.\n")
+    f.write("# (Additional spaces are harmless.)\n\n")
+
+    first_line = None
+    sorted = [(not v, k, v) for (k, v) in current.iteritems()]
+    sorted.sort()
+    for i, (_, k, v) in enumerate(sorted):
+      f.write("[[[  %s  ]]] %s\n" % (v, k))
+      if not v and first_line is None:
+        # position cursor on first line with no password.
+        first_line = i + 4
+    f.close()
+
+    p = Run([self.editor, "+%d" % (first_line,), self.pwfile])
+    _, _ = p.communicate()
+
+    return self.ReadFile()
+
+  def ReadFile(self):
+    result = {}
+    if self.pwfile is None: return result
+    try:
+      f = open(self.pwfile, "r")
+      for line in f:
+        line = line.strip()
+        if not line or line[0] == '#': continue
+        m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line)
+        if not m:
+          print "failed to parse password file: ", line
+        else:
+          result[m.group(2)] = m.group(1)
+      f.close()
+    except IOError, e:
+      if e.errno != errno.ENOENT:
+        print "error reading password file: ", str(e)
+    return result
+
+
+def ZipWriteStr(zip, filename, data, perms=0644):
+  # use a fixed timestamp so the output is repeatable.
+  zinfo = zipfile.ZipInfo(filename=filename,
+                          date_time=(2009, 1, 1, 0, 0, 0))
+  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/edify_generator.py b/tools/releasetools/edify_generator.py
new file mode 100644
index 0000000..e7a15cd
--- /dev/null
+++ b/tools/releasetools/edify_generator.py
@@ -0,0 +1,226 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import re
+
+import common
+
+class EdifyGenerator(object):
+  """Class to generate scripts in the 'edify' recovery script language
+  used from donut onwards."""
+
+  def __init__(self, version):
+    self.script = []
+    self.mounts = set()
+    self.version = version
+
+  def MakeTemporary(self):
+    """Make a temporary script object whose commands can latter be
+    appended to the parent script with AppendScript().  Used when the
+    caller wants to generate script commands out-of-order."""
+    x = EdifyGenerator(self.version)
+    x.mounts = self.mounts
+    return x
+
+  @staticmethod
+  def _WordWrap(cmd, linelen=80):
+    """'cmd' should be a function call with null characters after each
+    parameter (eg, "somefun(foo,\0bar,\0baz)").  This function wraps cmd
+    to a given line length, replacing nulls with spaces and/or newlines
+    to format it nicely."""
+    indent = cmd.index("(")+1
+    out = []
+    first = True
+    x = re.compile("^(.{,%d})\0" % (linelen-indent,))
+    while True:
+      if not first:
+        out.append(" " * indent)
+      first = False
+      m = x.search(cmd)
+      if not m:
+        parts = cmd.split("\0", 1)
+        out.append(parts[0]+"\n")
+        if len(parts) == 1:
+          break
+        else:
+          cmd = parts[1]
+          continue
+      out.append(m.group(1)+"\n")
+      cmd = cmd[m.end():]
+
+    return "".join(out).replace("\0", " ").rstrip("\n")
+
+  def AppendScript(self, other):
+    """Append the contents of another script (which should be created
+    with temporary=True) to this one."""
+    self.script.extend(other.script)
+
+  def AssertSomeFingerprint(self, *fp):
+    """Assert that the current system build fingerprint is one of *fp."""
+    if not fp:
+      raise ValueError("must specify some fingerprints")
+    cmd = ('assert(' +
+           ' ||\0'.join([('file_getprop("/system/build.prop", '
+                         '"ro.build.fingerprint") == "%s"')
+                        % i for i in fp]) +
+           ');')
+    self.script.append(self._WordWrap(cmd))
+
+  def AssertOlderBuild(self, timestamp):
+    """Assert that the build on the device is older (or the same as)
+    the given timestamp."""
+    self.script.append(('assert(!less_than_int(%s, '
+                        'getprop("ro.build.date.utc")));') % (timestamp,))
+
+  def AssertDevice(self, device):
+    """Assert that the device identifier is the given string."""
+    cmd = ('assert(getprop("ro.product.device") == "%s" ||\0'
+           'getprop("ro.build.product") == "%s");' % (device, device))
+    self.script.append(self._WordWrap(cmd))
+
+  def AssertSomeBootloader(self, *bootloaders):
+    """Asert that the bootloader version is one of *bootloaders."""
+    cmd = ("assert(" +
+           " ||\0".join(['getprop("ro.bootloader") == "%s"' % (b,)
+                         for b in bootloaders]) +
+           ");")
+    self.script.append(self._WordWrap(cmd))
+
+  def ShowProgress(self, frac, dur):
+    """Update the progress bar, advancing it over 'frac' over the next
+    'dur' seconds."""
+    self.script.append("show_progress(%f, %d);" % (frac, int(dur)))
+
+  def PatchCheck(self, filename, *sha1):
+    """Check that the given file (or MTD reference) has one of the
+    given *sha1 hashes."""
+    self.script.append('assert(apply_patch_check("%s"' % (filename,) +
+                       "".join([', "%s"' % (i,) for i in sha1]) +
+                       '));')
+
+  def CacheFreeSpaceCheck(self, amount):
+    """Check that there's at least 'amount' space that can be made
+    available on /cache."""
+    self.script.append("assert(apply_patch_space(%d));" % (amount,))
+
+  def Mount(self, kind, what, path):
+    """Mount the given 'what' at the given path.  'what' should be a
+    partition name if kind is "MTD", or a block device if kind is
+    "vfat".  No other values of 'kind' are supported."""
+    self.script.append('mount("%s", "%s", "%s");' % (kind, what, path))
+    self.mounts.add(path)
+
+  def UnpackPackageDir(self, src, dst):
+    """Unpack a given directory from the OTA package into the given
+    destination directory."""
+    self.script.append('package_extract_dir("%s", "%s");' % (src, dst))
+
+  def Comment(self, comment):
+    """Write a comment into the update script."""
+    self.script.append("")
+    for i in comment.split("\n"):
+      self.script.append("# " + i)
+    self.script.append("")
+
+  def Print(self, message):
+    """Log a message to the screen (if the logs are visible)."""
+    self.script.append('ui_print("%s");' % (message,))
+
+  def FormatPartition(self, partition):
+    """Format the given MTD partition."""
+    self.script.append('format("MTD", "%s");' % (partition,))
+
+  def DeleteFiles(self, file_list):
+    """Delete all files in file_list."""
+    if not file_list: return
+    cmd = "delete(" + ",\0".join(['"%s"' % (i,) for i in file_list]) + ");"
+    self.script.append(self._WordWrap(cmd))
+
+  def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs):
+    """Apply binary patches (in *patchpairs) to the given srcfile to
+    produce tgtfile (which may be "-" to indicate overwriting the
+    source file."""
+    if len(patchpairs) % 2 != 0 or len(patchpairs) == 0:
+      raise ValueError("bad patches given to ApplyPatch")
+    cmd = ['apply_patch("%s",\0"%s",\0%s,\0%d'
+           % (srcfile, tgtfile, tgtsha1, tgtsize)]
+    for i in range(0, len(patchpairs), 2):
+      cmd.append(',\0"%s:%s"' % patchpairs[i:i+2])
+    cmd.append(');')
+    cmd = "".join(cmd)
+    self.script.append(self._WordWrap(cmd))
+
+  def WriteFirmwareImage(self, kind, fn):
+    """Arrange to update the given firmware image (kind must be
+    "hboot" or "radio") when recovery finishes."""
+    if self.version == 1:
+      self.script.append(
+          ('assert(package_extract_file("%(fn)s", "/tmp/%(kind)s.img"),\n'
+           '       write_firmware_image("/tmp/%(kind)s.img", "%(kind)s"));')
+          % {'kind': kind, 'fn': fn})
+    else:
+      self.script.append(
+          'write_firmware_image("PACKAGE:%s", "%s");' % (fn, kind))
+
+  def WriteRawImage(self, partition, fn):
+    """Write the given package file into the given MTD partition."""
+    self.script.append(
+        ('assert(package_extract_file("%(fn)s", "/tmp/%(partition)s.img"),\n'
+         '       write_raw_image("/tmp/%(partition)s.img", "%(partition)s"),\n'
+         '       delete("/tmp/%(partition)s.img"));')
+        % {'partition': partition, 'fn': fn})
+
+  def SetPermissions(self, fn, uid, gid, mode):
+    """Set file ownership and permissions."""
+    self.script.append('set_perm(%d, %d, 0%o, "%s");' % (uid, gid, mode, fn))
+
+  def SetPermissionsRecursive(self, fn, uid, gid, dmode, fmode):
+    """Recursively set path ownership and permissions."""
+    self.script.append('set_perm_recursive(%d, %d, 0%o, 0%o, "%s");'
+                       % (uid, gid, dmode, fmode, fn))
+
+  def MakeSymlinks(self, symlink_list):
+    """Create symlinks, given a list of (dest, link) pairs."""
+    by_dest = {}
+    for d, l in symlink_list:
+      by_dest.setdefault(d, []).append(l)
+
+    for dest, links in sorted(by_dest.iteritems()):
+      cmd = ('symlink("%s", ' % (dest,) +
+             ",\0".join(['"' + i + '"' for i in sorted(links)]) + ");")
+      self.script.append(self._WordWrap(cmd))
+
+  def AppendExtra(self, extra):
+    """Append text verbatim to the output script."""
+    self.script.append(extra)
+
+  def AddToZip(self, input_zip, output_zip, input_path=None):
+    """Write the accumulated script to the output_zip file.  input_zip
+    is used as the source for the 'updater' binary needed to run
+    script.  If input_path is not None, it will be used as a local
+    path for the binary instead of input_zip."""
+
+    for p in sorted(self.mounts):
+      self.script.append('unmount("%s");' % (p,))
+
+    common.ZipWriteStr(output_zip, "META-INF/com/google/android/updater-script",
+                       "\n".join(self.script) + "\n")
+
+    if input_path is None:
+      data = input_zip.read("OTA/bin/updater")
+    else:
+      data = open(os.path.join(input_path, "updater")).read()
+    common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-binary",
+                       data, perms=0755)
diff --git a/tools/releasetools/img_from_target_files b/tools/releasetools/img_from_target_files
index 3451352..1d154b9 100755
--- a/tools/releasetools/img_from_target_files
+++ b/tools/releasetools/img_from_target_files
@@ -96,7 +96,7 @@
   img.close()
 
   common.CheckSize(data, "system.img")
-  output_zip.writestr("system.img", data)
+  common.ZipWriteStr(output_zip, "system.img", data)
 
 
 def CopyInfo(output_zip):
diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files
index dbac03d..d89f47f 100755
--- a/tools/releasetools/ota_from_target_files
+++ b/tools/releasetools/ota_from_target_files
@@ -33,6 +33,22 @@
       Generate an incremental OTA using the given target-files zip as
       the starting build.
 
+  -w  (--wipe_user_data)
+      Generate an OTA package that will wipe the user data partition
+      when installed.
+
+  -n  (--no_prereq)
+      Omit the timestamp prereq check normally included at the top of
+      the build scripts (used for developer OTA packages which
+      legitimately need to go back and forth).
+
+  -e  (--extra_script)  <file>
+      Insert the contents of file at the end of the update script.
+
+  -m  (--script_mode)  <mode>
+      Specify 'amend' or 'edify' scripts, or 'auto' to pick
+      automatically (this is the default).
+
 """
 
 import sys
@@ -51,6 +67,8 @@
 import zipfile
 
 import common
+import amend_generator
+import edify_generator
 
 OPTIONS = common.OPTIONS
 OPTIONS.package_key = "build/target/product/security/testkey"
@@ -58,6 +76,10 @@
 OPTIONS.require_verbatim = set()
 OPTIONS.prohibit_verbatim = set(("system/build.prop",))
 OPTIONS.patch_threshold = 0.95
+OPTIONS.wipe_user_data = False
+OPTIONS.omit_prereq = False
+OPTIONS.extra_script = None
+OPTIONS.script_mode = 'auto'
 
 def MostPopularKey(d, default):
   """Given a dict, return the key corresponding to the largest
@@ -178,11 +200,10 @@
 
     return d
 
-  def SetPermissions(self, script, renamer=lambda x: x):
+  def SetPermissions(self, script):
     """Append set_perm/set_perm_recursive commands to 'script' to
     set all permissions, users, and groups for the tree of files
-    rooted at 'self'.  'renamer' turns the filenames stored in the
-    tree of Items into the strings used in the script."""
+    rooted at 'self'."""
 
     self.CountChildMetadata()
 
@@ -193,22 +214,19 @@
       # supposed to be something different.
       if item.dir:
         if current != item.best_subtree:
-          script.append("set_perm_recursive %d %d 0%o 0%o %s" %
-                        (item.best_subtree + (renamer(item.name),)))
+          script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
           current = item.best_subtree
 
         if item.uid != current[0] or item.gid != current[1] or \
            item.mode != current[2]:
-          script.append("set_perm %d %d 0%o %s" %
-                        (item.uid, item.gid, item.mode, renamer(item.name)))
+          script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
 
         for i in item.children:
           recurse(i, current)
       else:
         if item.uid != current[0] or item.gid != current[1] or \
                item.mode != current[3]:
-          script.append("set_perm %d %d 0%o %s" %
-                        (item.uid, item.gid, item.mode, renamer(item.name)))
+          script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
 
     recurse(self, (-1, -1, -1, -1))
 
@@ -230,7 +248,7 @@
       basefilename = info.filename[7:]
       if IsSymlink(info):
         symlinks.append((input_zip.read(info.filename),
-                         "SYSTEM:" + basefilename))
+                         "/system/" + basefilename))
       else:
         info2 = copy.copy(info)
         fn = info2.filename = "system/" + basefilename
@@ -251,14 +269,6 @@
   return symlinks
 
 
-def AddScript(script, output_zip):
-  now = time.localtime()
-  i = zipfile.ZipInfo("META-INF/com/google/android/update-script",
-                      (now.tm_year, now.tm_mon, now.tm_mday,
-                       now.tm_hour, now.tm_min, now.tm_sec))
-  output_zip.writestr(i, "\n".join(script) + "\n")
-
-
 def SignOutput(temp_zip_name, output_zip_name):
   key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
   pw = key_passwords[OPTIONS.package_key]
@@ -266,89 +276,76 @@
   common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw)
 
 
-def SubstituteRoot(s):
-  if s == "system": return "SYSTEM:"
-  assert s.startswith("system/")
-  return "SYSTEM:" + s[7:]
-
 def FixPermissions(script):
   Item.GetMetadata()
   root = Item.Get("system")
-  root.SetPermissions(script, renamer=SubstituteRoot)
+  root.SetPermissions(script)
 
-def DeleteFiles(script, to_delete):
-  line = []
-  t = 0
-  for i in to_delete:
-    line.append(i)
-    t += len(i) + 1
-    if t > 80:
-      script.append("delete " + " ".join(line))
-      line = []
-      t = 0
-  if line:
-    script.append("delete " + " ".join(line))
 
 def AppendAssertions(script, input_zip):
-  script.append('assert compatible_with("0.2") == "true"')
-
   device = GetBuildProp("ro.product.device", input_zip)
-  script.append('assert getprop("ro.product.device") == "%s" || '
-                'getprop("ro.build.product") == "%s"' % (device, device))
+  script.AssertDevice(device)
 
   info = input_zip.read("OTA/android-info.txt")
   m = re.search(r"require\s+version-bootloader\s*=\s*(\S+)", info)
-  if not m:
-    raise ExternalError("failed to find required bootloaders in "
-                        "android-info.txt")
-  bootloaders = m.group(1).split("|")
-  script.append("assert " +
-                " || ".join(['getprop("ro.bootloader") == "%s"' % (b,)
-                             for b in bootloaders]))
-
-
-def IncludeBinary(name, input_zip, output_zip):
-  try:
-    data = input_zip.read(os.path.join("OTA/bin", name))
-    output_zip.writestr(name, data)
-  except IOError:
-    raise ExternalError('unable to include device binary "%s"' % (name,))
+  if m:
+    bootloaders = m.group(1).split("|")
+    script.AssertSomeBootloader(*bootloaders)
 
 
 def WriteFullOTAPackage(input_zip, output_zip):
-  script = []
+  if OPTIONS.script_mode in ("amend", "auto"):
+    script = amend_generator.AmendGenerator()
+  else:
+    # TODO: how to determine this?  We don't know what version it will
+    # be installed on top of.  For now, we expect the API just won't
+    # change very often.
+    script = edify_generator.EdifyGenerator(1)
 
-  ts = GetBuildProp("ro.build.date.utc", input_zip)
-  script.append("run_program PACKAGE:check_prereq %s" % (ts,))
-  IncludeBinary("check_prereq", input_zip, output_zip)
+  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)
+  device_specific.FullOTA_Assertions()
 
-  script.append("format BOOT:")
-  script.append("show_progress 0.1 0")
+  script.ShowProgress(0.5, 0)
 
-  output_zip.writestr("radio.img", input_zip.read("RADIO/image"))
-  script.append("write_radio_image PACKAGE:radio.img")
-  script.append("show_progress 0.5 0")
+  if OPTIONS.wipe_user_data:
+    script.FormatPartition("userdata")
 
-  script.append("format SYSTEM:")
-  script.append("copy_dir PACKAGE:system SYSTEM:")
+  script.FormatPartition("system")
+  script.Mount("MTD", "system", "/system")
+  script.UnpackPackageDir("system", "/system")
 
   symlinks = CopySystemFiles(input_zip, output_zip)
-  script.extend(["symlink %s %s" % s for s in symlinks])
+  script.MakeSymlinks(symlinks)
 
-  common.BuildAndAddBootableImage(os.path.join(OPTIONS.input_tmp, "RECOVERY"),
-                                  "system/recovery.img", output_zip)
-  Item.Get("system/recovery.img", dir=False)
+  if common.BuildAndAddBootableImage(
+      os.path.join(OPTIONS.input_tmp, "RECOVERY"),
+      "system/recovery.img", output_zip):
+    Item.Get("system/recovery.img", dir=False)
 
   FixPermissions(script)
 
   common.AddBoot(output_zip)
-  script.append("show_progress 0.2 0")
-  script.append("write_raw_image PACKAGE:boot.img BOOT:")
-  script.append("show_progress 0.2 10")
 
-  AddScript(script, output_zip)
+  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)
+
+  script.AddToZip(input_zip, output_zip)
 
 
 class File(object):
@@ -365,7 +362,7 @@
     return t
 
   def AddToZip(self, z):
-    z.writestr(self.name, self.data)
+    common.ZipWriteStr(z, self.name, self.data)
 
 
 def LoadSystemFiles(z):
@@ -380,8 +377,11 @@
   return out
 
 
-def Difference(tf, sf):
-  """Return the patch (as a string of data) needed to turn sf into tf."""
+def Difference(tf, sf, diff_program):
+  """Return the patch (as a string of data) needed to turn sf into tf.
+  diff_program is the name of an external program (or list, if
+  additional arguments are desired) to run to generate the diff.
+  """
 
   ttemp = tf.WriteToTemp()
   stemp = sf.WriteToTemp()
@@ -390,13 +390,21 @@
 
   try:
     ptemp = tempfile.NamedTemporaryFile()
-    p = common.Run(["bsdiff", stemp.name, ttemp.name, ptemp.name])
+    if isinstance(diff_program, list):
+      cmd = copy.copy(diff_program)
+    else:
+      cmd = [diff_program]
+    cmd.append(stemp.name)
+    cmd.append(ttemp.name)
+    cmd.append(ptemp.name)
+    p = common.Run(cmd)
     _, err = p.communicate()
-    if err:
-      raise ExternalError("failure running bsdiff:\n%s\n" % (err,))
+    if err or p.returncode != 0:
+      print "WARNING: failure running %s:\n%s\n" % (diff_program, err)
+      return None
     diff = ptemp.read()
-    ptemp.close()
   finally:
+    ptemp.close()
     stemp.close()
     ttemp.close()
 
@@ -411,12 +419,48 @@
     return bp
   m = re.search(re.escape(property) + r"=(.*)\n", bp)
   if not m:
-    raise ExternalException("couldn't find %s in build.prop" % (property,))
+    raise common.ExternalError("couldn't find %s in build.prop" % (property,))
   return m.group(1).strip()
 
 
+def GetRecoveryAPIVersion(zip):
+  """Returns the version of the recovery API.  Version 0 is the older
+  amend code (no separate binary)."""
+  try:
+    version = zip.read("META/recovery-api-version.txt")
+    return int(version)
+  except KeyError:
+    try:
+      # version one didn't have the recovery-api-version.txt file, but
+      # it did include an updater binary.
+      zip.getinfo("OTA/bin/updater")
+      return 1
+    except KeyError:
+      return 0
+
 def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
-  script = []
+  source_version = GetRecoveryAPIVersion(source_zip)
+
+  if OPTIONS.script_mode == 'amend':
+    script = amend_generator.AmendGenerator()
+  elif OPTIONS.script_mode == 'edify':
+    if source_version == 0:
+      print ("WARNING: generating edify script for a source that "
+             "can't install it.")
+    script = edify_generator.EdifyGenerator(source_version)
+  elif OPTIONS.script_mode == 'auto':
+    if source_version > 0:
+      script = edify_generator.EdifyGenerator(source_version)
+    else:
+      script = amend_generator.AmendGenerator()
+  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)
@@ -433,20 +477,24 @@
     if sf is None or fn in OPTIONS.require_verbatim:
       # This file should be included verbatim
       if fn in OPTIONS.prohibit_verbatim:
-        raise ExternalError("\"%s\" must be sent verbatim" % (fn,))
+        raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
       print "send", fn, "verbatim"
       tf.AddToZip(output_zip)
       verbatim_targets.append((fn, tf.size))
     elif tf.sha1 != sf.sha1:
       # File is different; consider sending as a patch
-      d = Difference(tf, sf)
-      print fn, tf.size, len(d), (float(len(d)) / tf.size)
-      if len(d) > tf.size * OPTIONS.patch_threshold:
+      diff_method = "bsdiff"
+      if tf.name.endswith(".gz"):
+        diff_method = "imgdiff"
+      d = Difference(tf, sf, diff_method)
+      if d is not None:
+        print fn, tf.size, len(d), (float(len(d)) / tf.size)
+      if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
         # patch is almost as big as the file; don't bother patching
         tf.AddToZip(output_zip)
         verbatim_targets.append((fn, tf.size))
       else:
-        output_zip.writestr("patch/" + fn + ".p", d)
+        common.ZipWriteStr(output_zip, "patch/" + fn + ".p", d)
         patch_list.append((fn, tf, sf, tf.size))
         largest_source_size = max(largest_source_size, sf.size)
     else:
@@ -459,97 +507,132 @@
   source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
   target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
 
-  script.append(('assert file_contains("SYSTEM:build.prop", '
-                 '"ro.build.fingerprint=%s") == "true" || '
-                 'file_contains("SYSTEM:build.prop", '
-                 '"ro.build.fingerprint=%s") == "true"') %
-                (source_fp, target_fp))
+  script.Mount("MTD", "system", "/system")
+  script.AssertSomeFingerprint(source_fp, target_fp)
 
-  source_boot = common.BuildBootableImage(
-      os.path.join(OPTIONS.source_tmp, "BOOT"))
-  target_boot = common.BuildBootableImage(
-      os.path.join(OPTIONS.target_tmp, "BOOT"))
-  updating_boot = (source_boot != target_boot)
+  source_boot = File("/tmp/boot.img",
+                     common.BuildBootableImage(
+      os.path.join(OPTIONS.source_tmp, "BOOT")))
+  target_boot = File("/tmp/boot.img",
+                     common.BuildBootableImage(
+      os.path.join(OPTIONS.target_tmp, "BOOT")))
+  updating_boot = (source_boot.data != target_boot.data)
 
-  source_recovery = common.BuildBootableImage(
-      os.path.join(OPTIONS.source_tmp, "RECOVERY"))
-  target_recovery = common.BuildBootableImage(
-      os.path.join(OPTIONS.target_tmp, "RECOVERY"))
-  updating_recovery = (source_recovery != target_recovery)
+  source_recovery = File("system/recovery.img",
+                         common.BuildBootableImage(
+      os.path.join(OPTIONS.source_tmp, "RECOVERY")))
+  target_recovery = File("system/recovery.img",
+                         common.BuildBootableImage(
+      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...")
 
   pb_verify = progress_bar_total * 0.3 * \
               (total_patched_size /
-               float(total_patched_size+total_verbatim_size))
+               float(total_patched_size+total_verbatim_size+1))
 
   for i, (fn, tf, sf, size) in enumerate(patch_list):
     if i % 5 == 0:
       next_sizes = sum([i[3] for i in patch_list[i:i+5]])
-      script.append("show_progress %f 1" %
-                    (next_sizes * pb_verify / total_patched_size,))
-    script.append("run_program PACKAGE:applypatch -c /%s %s %s" %
-                  (fn, tf.sha1, sf.sha1))
+      script.ShowProgress(next_sizes * pb_verify / (total_patched_size+1), 1)
 
-  if patch_list:
-    script.append("run_program PACKAGE:applypatch -s %d" %
-                  (largest_source_size,))
-    script.append("copy_dir PACKAGE:patch CACHE:../tmp/patchtmp")
-    IncludeBinary("applypatch", target_zip, output_zip)
+    script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
 
-  script.append("\n# ---- start making changes here\n")
+  if updating_recovery:
+    d = Difference(target_recovery, source_recovery, "imgdiff")
+    print "recovery  target: %d  source: %d  diff: %d" % (
+        target_recovery.size, source_recovery.size, len(d))
 
-  DeleteFiles(script, [SubstituteRoot(i[0]) for i in verbatim_targets])
+    common.ZipWriteStr(output_zip, "patch/recovery.img.p", d)
+
+    script.PatchCheck("MTD:recovery:%d:%s:%d:%s" %
+                      (source_recovery.size, source_recovery.sha1,
+                       target_recovery.size, target_recovery.sha1))
 
   if updating_boot:
-    script.append("format BOOT:")
-    output_zip.writestr("boot.img", target_boot)
+    d = Difference(target_boot, source_boot, "imgdiff")
+    print "boot      target: %d  source: %d  diff: %d" % (
+        target_boot.size, source_boot.size, len(d))
+
+    common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
+
+    script.PatchCheck("MTD:boot:%d:%s:%d:%s" %
+                      (source_boot.size, source_boot.sha1,
+                       target_boot.size, target_boot.sha1))
+
+  if patch_list or updating_recovery or updating_boot:
+    script.CacheFreeSpaceCheck(largest_source_size)
+    script.Print("Unpacking patches...")
+    script.UnpackPackageDir("patch", "/tmp/patchtmp")
+
+  device_specific.IncrementalOTA_VerifyEnd()
+
+  script.Comment("---- start making changes here ----")
+
+  if OPTIONS.wipe_user_data:
+    script.Print("Erasing user data...")
+    script.FormatPartition("userdata")
+
+  script.Print("Removing unneeded files...")
+  script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
+                     ["/"+i for i in sorted(source_data)
+                            if i not in target_data])
+
+  if updating_boot:
+    # Produce the boot image by applying a patch to the current
+    # contents of the boot partition, and write it back to the
+    # partition.
+    script.Print("Patching boot image...")
+    script.ApplyPatch("MTD:boot:%d:%s:%d:%s"
+                      % (source_boot.size, source_boot.sha1,
+                         target_boot.size, target_boot.sha1),
+                      "-",
+                      target_boot.size, target_boot.sha1,
+                      source_boot.sha1, "/tmp/patchtmp/boot.img.p")
     print "boot image changed; including."
   else:
     print "boot image unchanged; skipping."
 
   if updating_recovery:
-    output_zip.writestr("system/recovery.img", target_recovery)
+    # Produce /system/recovery.img by applying a patch to the current
+    # contents of the recovery partition.
+    script.Print("Patching recovery image...")
+    script.ApplyPatch("MTD:recovery:%d:%s:%d:%s"
+                      % (source_recovery.size, source_recovery.sha1,
+                         target_recovery.size, target_recovery.sha1),
+                      "/system/recovery.img",
+                      target_recovery.size, target_recovery.sha1,
+                      source_recovery.sha1, "/tmp/patchtmp/recovery.img.p")
     print "recovery image changed; including."
   else:
     print "recovery image unchanged; skipping."
 
-  if updating_radio:
-    script.append("show_progress 0.3 10")
-    script.append("write_radio_image PACKAGE:radio.img")
-    output_zip.writestr("radio.img", target_radio)
-    print "radio image changed; including."
-  else:
-    print "radio image unchanged; skipping."
-
+  script.Print("Patching system files...")
   pb_apply = progress_bar_total * 0.7 * \
              (total_patched_size /
-              float(total_patched_size+total_verbatim_size))
+              float(total_patched_size+total_verbatim_size+1))
   for i, (fn, tf, sf, size) in enumerate(patch_list):
     if i % 5 == 0:
       next_sizes = sum([i[3] for i in patch_list[i:i+5]])
-      script.append("show_progress %f 1" %
-                    (next_sizes * pb_apply / total_patched_size,))
-    script.append(("run_program PACKAGE:applypatch "
-                   "/%s %s %d %s:/tmp/patchtmp/%s.p") %
-                  (fn, tf.sha1, tf.size, sf.sha1, fn))
+      script.ShowProgress(next_sizes * pb_apply / (total_patched_size+1), 1)
+    script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1,
+                      sf.sha1, "/tmp/patchtmp/"+fn+".p")
 
   target_symlinks = CopySystemFiles(target_zip, None)
 
   target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
-  temp_script = []
+  temp_script = script.MakeTemporary()
   FixPermissions(temp_script)
 
   # Note that this call will mess up the tree of Items, so make sure
@@ -564,14 +647,17 @@
   for dest, link in source_symlinks:
     if link not in target_symlinks_d:
       to_delete.append(link)
-  DeleteFiles(script, to_delete)
+  script.DeleteFiles(to_delete)
 
   if verbatim_targets:
     pb_verbatim = progress_bar_total * \
                   (total_verbatim_size /
-                   float(total_patched_size+total_verbatim_size))
-    script.append("show_progress %f 5" % (pb_verbatim,))
-    script.append("copy_dir PACKAGE:system SYSTEM:")
+                   float(total_patched_size+total_verbatim_size+1))
+    script.ShowProgress(pb_verbatim, 5)
+    script.Print("Unpacking new files...")
+    script.UnpackPackageDir("system", "/system")
+
+  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
@@ -583,18 +669,21 @@
         to_create.append((dest, link))
     else:
       to_create.append((dest, link))
-  DeleteFiles(script, [i[1] for i in to_create])
-  script.extend(["symlink %s %s" % s for s in to_create])
+  script.DeleteFiles([i[1] for i in to_create])
+  script.MakeSymlinks(to_create)
 
   # Now that the symlinks are created, we can set all the
   # permissions.
-  script.extend(temp_script)
+  script.AppendScript(temp_script)
 
-  if updating_boot:
-    script.append("show_progress 0.1 5")
-    script.append("write_raw_image PACKAGE:boot.img BOOT:")
+  # Write the radio image, if necessary.
+  script.ShowProgress(0.3, 10)
+  device_specific.IncrementalOTA_InstallEnd()
 
-  AddScript(script, output_zip)
+  if OPTIONS.extra_script is not None:
+    scirpt.AppendExtra(OPTIONS.extra_script)
+
+  script.AddToZip(target_zip, output_zip)
 
 
 def main(argv):
@@ -602,21 +691,31 @@
   def option_handler(o, a):
     if o in ("-b", "--board_config"):
       common.LoadBoardConfig(a)
-      return True
     elif o in ("-k", "--package_key"):
       OPTIONS.package_key = a
-      return True
     elif o in ("-i", "--incremental_from"):
       OPTIONS.incremental_source = a
-      return True
+    elif o in ("-w", "--wipe_user_data"):
+      OPTIONS.wipe_user_data = True
+    elif o in ("-n", "--no_prereq"):
+      OPTIONS.omit_prereq = True
+    elif o in ("-e", "--extra_script"):
+      OPTIONS.extra_script = a
+    elif o in ("-m", "--script_mode"):
+      OPTIONS.script_mode = a
     else:
       return False
+    return True
 
   args = common.ParseOptions(argv, __doc__,
-                             extra_opts="b:k:i:d:",
+                             extra_opts="b:k:i:d:wne:m:",
                              extra_long_opts=["board_config=",
                                               "package_key=",
-                                              "incremental_from="],
+                                              "incremental_from=",
+                                              "wipe_user_data",
+                                              "no_prereq",
+                                              "extra_script=",
+                                              "script_mode="],
                              extra_option_handler=option_handler)
 
   if len(args) != 2:
@@ -630,6 +729,12 @@
     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,))
+
+  if OPTIONS.extra_script is not None:
+    OPTIONS.extra_script = open(OPTIONS.extra_script).read()
+
   print "unzipping target target-files..."
   OPTIONS.input_tmp = common.UnzipTemp(args[0])
   OPTIONS.target_tmp = OPTIONS.input_tmp
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index b632924..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).
@@ -47,6 +43,20 @@
 
       -d and -k options are added to the set of mappings in the order
       in which they appear on the command line.
+
+  -o  (--replace_ota_keys)
+      Replace the certificate (public key) used by OTA package
+      verification with the one specified in the input target_files
+      zip (in the META/otakeys.txt file).  Key remapping (-k and -d)
+      is performed on this key.
+
+  -t  (--tag_changes)  <+tag>,<-tag>,...
+      Comma-separated list of changes to make to the set of tags (in
+      the last component of the build fingerprint).  Prefix each with
+      '+' or '-' to indicate whether that tag should be added or
+      removed.  Changes are processed in the order they appear.
+      Default value is "-test-keys,+ota-rel-keys,+release-keys".
+
 """
 
 import sys
@@ -55,6 +65,8 @@
   print >> sys.stderr, "Python 2.4 or newer is required."
   sys.exit(1)
 
+import cStringIO
+import copy
 import os
 import re
 import subprocess
@@ -67,7 +79,8 @@
 
 OPTIONS.extra_apks = {}
 OPTIONS.key_map = {}
-
+OPTIONS.replace_ota_keys = False
+OPTIONS.tag_changes = ("-test-keys", "+ota-rel-keys", "+release-keys")
 
 def GetApkCerts(tf_zip):
   certmap = {}
@@ -84,6 +97,85 @@
   return certmap
 
 
+def CheckAllApksSigned(input_tf_zip, apk_key_map):
+  """Check that all the APKs we want to sign have keys specified, and
+  error out if they don't."""
+  unknown_apks = []
+  for info in input_tf_zip.infolist():
+    if info.filename.endswith(".apk"):
+      name = os.path.basename(info.filename)
+      if name not in apk_key_map:
+        unknown_apks.append(name)
+  if unknown_apks:
+    print "ERROR: no key specified for:\n\n ",
+    print "\n  ".join(unknown_apks)
+    print "\nUse '-e <apkname>=' to specify a key (which may be an"
+    print "empty string to not sign this apk)."
+    sys.exit(1)
+
+
+def SharedUserForApk(data):
+  tmp = tempfile.NamedTemporaryFile()
+  tmp.write(data)
+  tmp.flush()
+
+  p = common.Run(["aapt", "dump", "xmltree", tmp.name, "AndroidManifest.xml"],
+                 stdout=subprocess.PIPE)
+  data, _ = p.communicate()
+  if p.returncode != 0:
+    raise ExternalError("failed to run aapt dump")
+  lines = data.split("\n")
+  for i in lines:
+    m = re.match(r'^\s*A: android:sharedUserId\([0-9a-fx]*\)="([^"]*)" .*$', i)
+    if m:
+      return m.group(1)
+  return None
+
+
+def CheckSharedUserIdsConsistent(input_tf_zip, apk_key_map):
+  """Check that all packages that request the same shared user id are
+  going to be signed with the same key."""
+
+  shared_user_apks = {}
+  maxlen = len("(unknown key)")
+
+  for info in input_tf_zip.infolist():
+    if info.filename.endswith(".apk"):
+      data = input_tf_zip.read(info.filename)
+
+      name = os.path.basename(info.filename)
+      shared_user = SharedUserForApk(data)
+      key = apk_key_map[name]
+      maxlen = max(maxlen, len(key))
+
+      if shared_user is not None:
+        shared_user_apks.setdefault(
+            shared_user, {}).setdefault(key, []).append(name)
+
+  errors = []
+  for k, v in shared_user_apks.iteritems():
+    # each shared user should have exactly one key used for all the
+    # apks that want that user.
+    if len(v) > 1:
+      errors.append((k, v))
+
+  if not errors: return
+
+  print "ERROR:  shared user inconsistency.  All apks wanting to use"
+  print "        a given shared user must be signed with the same key."
+  print
+  errors.sort()
+  for user, keys in errors:
+    print 'shared user id "%s":' % (user,)
+    for key, apps in keys.iteritems():
+      print '  %-*s   %s' % (maxlen, key or "(unknown key)", apps[0])
+      for a in apps[1:]:
+        print (' ' * (maxlen+5)) + a
+    print
+
+  sys.exit(1)
+
+
 def SignApk(data, keyname, pw):
   unsigned = tempfile.NamedTemporaryFile()
   unsigned.write(data)
@@ -100,52 +192,113 @@
   return data
 
 
-def SignApks(input_tf_zip, output_tf_zip):
-  apk_key_map = GetApkCerts(input_tf_zip)
-
-  key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
-
+def SignApks(input_tf_zip, output_tf_zip, apk_key_map, key_passwords):
   maxsize = max([len(os.path.basename(i.filename))
                  for i in input_tf_zip.infolist()
                  if i.filename.endswith('.apk')])
 
   for info in input_tf_zip.infolist():
     data = input_tf_zip.read(info.filename)
+    out_info = copy.copy(info)
     if info.filename.endswith(".apk"):
       name = os.path.basename(info.filename)
-      key = apk_key_map.get(name, None)
-      if key is not None:
-        print "signing: %-*s (%s)" % (maxsize, name, key)
+      key = apk_key_map[name]
+      if key:
+        print "    signing: %-*s (%s)" % (maxsize, name, key)
         signed_data = SignApk(data, key, key_passwords[key])
-        output_tf_zip.writestr(info, signed_data)
+        output_tf_zip.writestr(out_info, signed_data)
       else:
         # an APK we're not supposed to sign.
-        print "skipping: %s" % (name,)
-        output_tf_zip.writestr(info, data)
-    elif info.filename == "SYSTEM/build.prop":
-      # Change build fingerprint to reflect the fact that apps are signed.
-      m = re.search(r"ro\.build\.fingerprint=.*\b(test-keys)\b.*", data)
-      if not m:
-        print 'WARNING: ro.build.fingerprint does not contain "test-keys"'
-      else:
-        data = data[:m.start(1)] + "release-keys" + data[m.end(1):]
-      m = re.search(r"ro\.build\.description=.*\b(test-keys)\b.*", data)
-      if not m:
-        print 'WARNING: ro.build.description does not contain "test-keys"'
-      else:
-        data = data[:m.start(1)] + "release-keys" + data[m.end(1):]
-      output_tf_zip.writestr(info, data)
+        print "NOT signing: %s" % (name,)
+        output_tf_zip.writestr(out_info, data)
+    elif info.filename in ("SYSTEM/build.prop",
+                           "RECOVERY/RAMDISK/default.prop"):
+      print "rewriting %s:" % (info.filename,)
+      new_data = RewriteProps(data)
+      output_tf_zip.writestr(out_info, new_data)
     else:
       # a non-APK file; copy it verbatim
-      output_tf_zip.writestr(info, data)
+      output_tf_zip.writestr(out_info, data)
+
+
+def RewriteProps(data):
+  output = []
+  for line in data.split("\n"):
+    line = line.strip()
+    original_line = line
+    if line and line[0] != '#':
+      key, value = line.split("=", 1)
+      if key == "ro.build.fingerprint":
+        pieces = line.split("/")
+        tags = set(pieces[-1].split(","))
+        for ch in OPTIONS.tag_changes:
+          if ch[0] == "-":
+            tags.discard(ch[1:])
+          elif ch[0] == "+":
+            tags.add(ch[1:])
+        line = "/".join(pieces[:-1] + [",".join(sorted(tags))])
+      elif key == "ro.build.description":
+        pieces = line.split(" ")
+        assert len(pieces) == 5
+        tags = set(pieces[-1].split(","))
+        for ch in OPTIONS.tag_changes:
+          if ch[0] == "-":
+            tags.discard(ch[1:])
+          elif ch[0] == "+":
+            tags.add(ch[1:])
+        line = " ".join(pieces[:-1] + [",".join(sorted(tags))])
+    if line != original_line:
+      print "  replace: ", original_line
+      print "     with: ", line
+    output.append(line)
+  return "\n".join(output) + "\n"
+
+
+def ReplaceOtaKeys(input_tf_zip, output_tf_zip):
+  try:
+    keylist = input_tf_zip.read("META/otakeys.txt").split()
+  except KeyError:
+    raise ExternalError("can't read META/otakeys.txt from input")
+
+  mapped_keys = []
+  for k in keylist:
+    m = re.match(r"^(.*)\.x509\.pem$", k)
+    if not m:
+      raise ExternalError("can't parse \"%s\" from META/otakeys.txt" % (k,))
+    k = m.group(1)
+    mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
+
+  print "using:\n   ", "\n   ".join(mapped_keys)
+  print "for OTA package verification"
+
+  # recovery uses a version of the key that has been slightly
+  # predigested (by DumpPublicKey.java) and put in res/keys.
+
+  p = common.Run(["java", "-jar",
+                  os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar")]
+                 + mapped_keys,
+                 stdout=subprocess.PIPE)
+  data, _ = p.communicate()
+  if p.returncode != 0:
+    raise ExternalError("failed to run dumpkeys")
+  common.ZipWriteStr(output_tf_zip, "RECOVERY/RAMDISK/res/keys", data)
+
+  # SystemUpdateActivity uses the x509.pem version of the keys, but
+  # put into a zipfile system/etc/security/otacerts.zip.
+
+  tempfile = cStringIO.StringIO()
+  certs_zip = zipfile.ZipFile(tempfile, "w")
+  for k in mapped_keys:
+    certs_zip.write(k)
+  certs_zip.close()
+  common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip",
+                     tempfile.getvalue())
 
 
 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:
@@ -160,16 +313,27 @@
     elif o in ("-k", "--key_mapping"):
       s, d = a.split("=")
       OPTIONS.key_map[s] = d
+    elif o in ("-o", "--replace_ota_keys"):
+      OPTIONS.replace_ota_keys = True
+    elif o in ("-t", "--tag_changes"):
+      new = []
+      for i in a.split(","):
+        i = i.strip()
+        if not i or i[0] not in "-+":
+          raise ValueError("Bad tag change '%s'" % (i,))
+        new.append(i[0] + i[1:].strip())
+      OPTIONS.tag_changes = tuple(new)
     else:
       return False
     return True
 
   args = common.ParseOptions(argv, __doc__,
-                             extra_opts="s:e:d:k:",
-                             extra_long_opts=["signapk_jar=",
-                                              "extra_apks=",
+                             extra_opts="e:d:k:ot:",
+                             extra_long_opts=["extra_apks=",
                                               "default_key_mappings=",
-                                              "key_mapping="],
+                                              "key_mapping=",
+                                              "replace_ota_keys",
+                                              "tag_changes="],
                              extra_option_handler=option_handler)
 
   if len(args) != 2:
@@ -179,7 +343,15 @@
   input_zip = zipfile.ZipFile(args[0], "r")
   output_zip = zipfile.ZipFile(args[1], "w")
 
-  SignApks(input_zip, output_zip)
+  apk_key_map = GetApkCerts(input_zip)
+  CheckAllApksSigned(input_zip, apk_key_map)
+  CheckSharedUserIdsConsistent(input_zip, apk_key_map)
+
+  key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
+  SignApks(input_zip, output_zip, apk_key_map, key_passwords)
+
+  if OPTIONS.replace_ota_keys:
+    ReplaceOtaKeys(input_zip, output_zip)
 
   input_zip.close()
   output_zip.close()
diff --git a/tools/signapk/SignApk.java b/tools/signapk/SignApk.java
index 340a9f5..fb55028 100644
--- a/tools/signapk/SignApk.java
+++ b/tools/signapk/SignApk.java
@@ -62,6 +62,7 @@
 import java.util.jar.JarFile;
 import java.util.jar.JarOutputStream;
 import java.util.jar.Manifest;
+import java.util.regex.Pattern;
 import javax.crypto.Cipher;
 import javax.crypto.EncryptedPrivateKeyInfo;
 import javax.crypto.SecretKeyFactory;
@@ -75,6 +76,10 @@
     private static final String CERT_SF_NAME = "META-INF/CERT.SF";
     private static final String CERT_RSA_NAME = "META-INF/CERT.RSA";
 
+    // Files matching this pattern are not copied to the output.
+    private static Pattern stripPattern =
+            Pattern.compile("^META-INF/(.*)[.](SF|RSA|DSA)$");
+
     private static X509Certificate readPublicKey(File file)
             throws IOException, GeneralSecurityException {
         FileInputStream input = new FileInputStream(file);
@@ -193,7 +198,9 @@
         for (JarEntry entry: byName.values()) {
             String name = entry.getName();
             if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
-                !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME)) {
+                !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&
+                (stripPattern == null ||
+                 !stripPattern.matcher(name).matches())) {
                 InputStream data = jar.getInputStream(entry);
                 while ((num = data.read(buffer)) > 0) {
                     md.update(buffer, 0, num);
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 9e3cb66..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>
@@ -30,7 +30,8 @@
 {
     fprintf(stderr, "Zip alignment utility\n");
     fprintf(stderr,
-        "Usage: zipalign [-f] [-v] <align> infile.zip outfile.zip\n");
+        "Usage: zipalign [-f] [-v] <align> infile.zip outfile.zip\n"
+        "       zipalign -c [-v] <align> infile.zip\n" );
 }
 
 /*
@@ -152,14 +153,14 @@
         pEntry = zipFile.getEntryByIndex(i);
         if (pEntry->isCompressed()) {
             if (verbose) {
-                printf("%8ld %s (OK - compressed)\n", 
+                printf("%8ld %s (OK - compressed)\n",
                     (long) pEntry->getFileOffset(), pEntry->getFileName());
             }
         } else {
             long offset = pEntry->getFileOffset();
             if ((offset % alignment) != 0) {
                 if (verbose) {
-                    printf("%8ld %s (BAD - %ld)\n", 
+                    printf("%8ld %s (BAD - %ld)\n",
                         (long) offset, pEntry->getFileName(),
                         offset % alignment);
                 }
@@ -185,6 +186,7 @@
 int main(int argc, char* const argv[])
 {
     bool wantUsage = false;
+    bool check = false;
     bool force = false;
     bool verbose = false;
     int result = 1;
@@ -204,6 +206,9 @@
 
         while (*cp != '\0') {
             switch (*cp) {
+            case 'c':
+                check = true;
+                break;
             case 'f':
                 force = true;
                 break;
@@ -223,7 +228,7 @@
         argv++;
     }
 
-    if (argc != 3) {
+    if (!((check && argc == 2) || (!check && argc == 3))) {
         wantUsage = true;
         goto bail;
     }
@@ -235,12 +240,17 @@
         goto bail;
     }
 
-    /* create the new archive */
-    result = process(argv[1], argv[2], alignment, force);
+    if (check) {
+        /* check existing archive for correct alignment */
+        result = verify(argv[1], alignment, verbose);
+    } else {
+        /* create the new archive */
+        result = process(argv[1], argv[2], alignment, force);
 
-    /* trust, but verify */
-    if (result == 0)
-        result = verify(argv[2], alignment, verbose);
+        /* trust, but verify */
+        if (result == 0)
+            result = verify(argv[2], alignment, verbose);
+    }
 
 bail:
     if (wantUsage) {
@@ -250,4 +260,3 @@
 
     return result;
 }
-
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