Merge change 10642

* changes:
  added linux-sh.mk to support SuperH architecture
diff --git a/cleanspec.mk b/cleanspec.mk
index 22d9fe1..c244c46 100644
--- a/cleanspec.mk
+++ b/cleanspec.mk
@@ -75,7 +75,17 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PinyinIMEGoogleService_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.inputmethod.pinyin.lib_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PinyinIMEGoogleService_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/tcpdump)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/location)
 
+$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/SHARED_LIBRARIES/lib?camera_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/STATIC_LIBRARIES/lib?camera_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/Silence.ogg)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/notifications/Silence.ogg)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/core/Makefile b/core/Makefile
index 58a9695..2c4d133 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.
@@ -129,10 +126,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 +226,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 +258,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 +279,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)
@@ -286,7 +299,7 @@
 $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
 	$(call pretty,"Target boot image: $@")
 	$(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) --output $@
-	$(hide) $(call assert-max-file-size,$@,$(BOARD_BOOTIMAGE_MAX_SIZE))
+	$(hide) $(call assert-max-file-size,$@,$(BOARD_BOOTIMAGE_MAX_SIZE),raw)
 endif # TARGET_BOOTIMAGE_USE_EXT2
 
 else	# TARGET_NO_KERNEL
@@ -434,12 +447,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 +469,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
 # #################################################################
@@ -464,6 +498,95 @@
 endif
 
 # -----------------------------------------------------------------
+# Recovery image
+
+# If neither TARGET_NO_KERNEL nor TARGET_NO_RECOVERY are true
+ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))
+
+INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
+
+recovery_initrc := $(call include-path-for, recovery)/etc/init.rc
+recovery_kernel := $(INSTALLED_KERNEL_TARGET) # same as a non-recovery system
+recovery_ramdisk := $(PRODUCT_OUT)/ramdisk-recovery.img
+recovery_build_prop := $(INSTALLED_BUILD_PROP_TARGET)
+recovery_binary := $(call intermediates-dir-for,EXECUTABLES,recovery)/recovery
+recovery_resources_common := $(call include-path-for, recovery)/res
+recovery_resources_private := $(strip $(wildcard $(TARGET_DEVICE_DIR)/recovery/res))
+recovery_resource_deps := $(shell find $(recovery_resources_common) \
+  $(recovery_resources_private) -type f)
+
+ifeq ($(recovery_resources_private),)
+  $(info No private recovery resources for TARGET_DEVICE $(TARGET_DEVICE))
+endif
+
+INTERNAL_RECOVERYIMAGE_ARGS := \
+	$(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
+	--kernel $(recovery_kernel) \
+	--ramdisk $(recovery_ramdisk)
+
+# Assumes this has already been stripped
+ifdef BOARD_KERNEL_CMDLINE
+  INTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)"
+endif
+ifdef BOARD_KERNEL_BASE
+  INTERNAL_RECOVERYIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)
+endif
+
+# 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_INSTALL_OTA_KEYS)
+	@echo ----- Making recovery image ------
+	rm -rf $(TARGET_RECOVERY_OUT)
+	mkdir -p $(TARGET_RECOVERY_OUT)
+	mkdir -p $(TARGET_RECOVERY_ROOT_OUT)
+	mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/etc
+	mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/tmp
+	echo Copying baseline ramdisk...
+	cp -R $(TARGET_ROOT_OUT) $(TARGET_RECOVERY_OUT)
+	echo Modifying ramdisk contents...
+	cp -f $(recovery_initrc) $(TARGET_RECOVERY_ROOT_OUT)/
+	cp -f $(recovery_binary) $(TARGET_RECOVERY_ROOT_OUT)/sbin/
+	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) | $(MINIGZIP) > $(recovery_ramdisk)
+	$(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) --output $@
+	@echo ----- Made recovery image -------- $@
+	$(hide) $(call assert-max-file-size,$@,$(BOARD_RECOVERYIMAGE_MAX_SIZE),raw)
+
+else
+INSTALLED_RECOVERYIMAGE_TARGET :=
+endif
+
+.PHONY: recoveryimage
+recoveryimage: $(INSTALLED_RECOVERYIMAGE_TARGET)
+
+# -----------------------------------------------------------------
 # system yaffs image
 #
 # First, the "unoptimized" image, which contains .apk/.jar files
@@ -518,10 +641,28 @@
   SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)
 endif
 
-$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) | $(ACP)
+# The system partition needs room for the recovery image as well.  We
+# now store the recovery image as a binary patch using the boot image
+# as the source (since they are very similar).  Generate the patch so
+# we can see how big it's going to be, and include that in the system
+# image size check calculation.
+ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
+intermediates := $(call intermediates-dir-for,PACKAGING,recovery_patch)
+RECOVERY_FROM_BOOT_PATCH := $(intermediates)/recovery_from_boot.p
+$(RECOVERY_FROM_BOOT_PATCH): $(INSTALLED_RECOVERYIMAGE_TARGET) \
+                             $(INSTALLED_BOOTIMAGE_TARGET) \
+			     $(HOST_OUT_EXECUTABLES)/imgdiff \
+	                     $(HOST_OUT_EXECUTABLES)/bsdiff
+	@echo "Construct recovery from boot"
+	mkdir -p $(dir $@)
+	PATH=$(HOST_OUT_EXECUTABLES):$$PATH $(HOST_OUT_EXECUTABLES)/imgdiff $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_RECOVERYIMAGE_TARGET) $@
+endif
+
+
+$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP)
 	@echo "Install system fs image: $@"
 	$(copy-file-to-target)
-	$(hide) $(call assert-max-file-size,$@,$(BOARD_SYSTEMIMAGE_MAX_SIZE))
+	$(hide) $(call assert-max-file-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_MAX_SIZE),yaffs)
 
 systemimage: $(INSTALLED_SYSTEMIMAGE)
 
@@ -530,7 +671,7 @@
 	            | $(INTERNAL_MKUSERFS)
 	@echo "make $@: ignoring dependencies"
 	$(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE))
-	$(hide) $(call assert-max-file-size,$(INSTALLED_SYSTEMIMAGE),$(BOARD_SYSTEMIMAGE_MAX_SIZE))
+	$(hide) $(call assert-max-file-size,$(INSTALLED_SYSTEMIMAGE),$(BOARD_SYSTEMIMAGE_MAX_SIZE),yaffs)
 
 #######
 ## system tarball
@@ -567,7 +708,7 @@
     $(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)")
     @mkdir -p $(TARGET_OUT_DATA)
     $(call build-userimage-ext2-target,$(TARGET_OUT_DATA),$(INSTALLED_USERDATAIMAGE_TARGET),userdata,)
-    $(hide) $(call assert-max-file-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_MAX_SIZE))
+    $(hide) $(call assert-max-file-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_MAX_SIZE),yaffs)
 endef
 
 else # TARGET_USERIMAGES_USE_EXT2 != true
@@ -577,7 +718,7 @@
     $(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)")
     @mkdir -p $(TARGET_OUT_DATA)
     $(hide) $(MKYAFFS2) -f $(TARGET_OUT_DATA) $(INSTALLED_USERDATAIMAGE_TARGET)
-    $(hide) $(call assert-max-file-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_MAX_SIZE))
+    $(hide) $(call assert-max-file-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_MAX_SIZE),yaffs)
 endef
 endif # TARGET_USERIMAGES_USE_EXT2
 
@@ -614,72 +755,6 @@
 	$(build-userdatatarball-target)
 
 
-# If neither TARGET_NO_KERNEL nor TARGET_NO_RECOVERY are true
-ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))
-
-# -----------------------------------------------------------------
-# Recovery image
-INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
-
-recovery_initrc := $(call include-path-for, recovery)/etc/init.rc
-recovery_kernel := $(INSTALLED_KERNEL_TARGET) # same as a non-recovery system
-recovery_ramdisk := $(PRODUCT_OUT)/ramdisk-recovery.img
-recovery_build_prop := $(INSTALLED_BUILD_PROP_TARGET)
-recovery_binary := $(call intermediates-dir-for,EXECUTABLES,recovery)/recovery
-recovery_resources_common := $(call include-path-for, recovery)/res
-recovery_resources_private := $(strip $(wildcard $(TARGET_DEVICE_DIR)/recovery/res))
-recovery_resource_deps := $(shell find $(recovery_resources_common) \
-  $(recovery_resources_private) -type f)
-
-ifeq ($(recovery_resources_private),)
-  $(info No private recovery resources for TARGET_DEVICE $(TARGET_DEVICE))
-endif
-
-INTERNAL_RECOVERYIMAGE_ARGS := \
-	$(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
-	--kernel $(recovery_kernel) \
-	--ramdisk $(recovery_ramdisk)
-
-# Assumes this has already been stripped
-ifdef BOARD_KERNEL_CMDLINE
-  INTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)"
-endif
-
-$(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) \
-		$(INSTALLED_RAMDISK_TARGET) \
-		$(INSTALLED_BOOTIMAGE_TARGET) \
-		$(recovery_binary) \
-		$(recovery_initrc) $(recovery_kernel) \
-		$(INSTALLED_2NDBOOTLOADER_TARGET) \
-		$(recovery_build_prop) $(recovery_resource_deps)
-	@echo ----- Making recovery image ------
-	rm -rf $(TARGET_RECOVERY_OUT)
-	mkdir -p $(TARGET_RECOVERY_OUT)
-	mkdir -p $(TARGET_RECOVERY_ROOT_OUT)
-	mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/etc
-	mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/tmp
-	echo Copying baseline ramdisk...
-	cp -R $(TARGET_ROOT_OUT) $(TARGET_RECOVERY_OUT)
-	echo Modifying ramdisk contents...
-	cp -f $(recovery_initrc) $(TARGET_RECOVERY_ROOT_OUT)/
-	cp -f $(recovery_binary) $(TARGET_RECOVERY_ROOT_OUT)/sbin/
-	cp -rf $(recovery_resources_common) $(TARGET_RECOVERY_ROOT_OUT)/
-	$(foreach item,$(recovery_resources_private), \
-	  cp -rf $(item) $(TARGET_RECOVERY_ROOT_OUT)/)
-	cat $(INSTALLED_DEFAULT_PROP_TARGET) $(recovery_build_prop) \
-	        > $(TARGET_RECOVERY_ROOT_OUT)/default.prop
-	$(MKBOOTFS) $(TARGET_RECOVERY_ROOT_OUT) | gzip > $(recovery_ramdisk)
-	$(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) --output $@
-	@echo ----- Made recovery image -------- $@
-	$(hide) $(call assert-max-file-size,$@,$(BOARD_RECOVERYIMAGE_MAX_SIZE))
-
-else
-INSTALLED_RECOVERYIMAGE_TARGET :=
-endif
-
-.PHONY: recoveryimage
-recoveryimage: $(INSTALLED_RECOVERYIMAGE_TARGET)
-
 # -----------------------------------------------------------------
 # bring in the installer image generation defines if necessary
 ifeq ($(TARGET_USE_DISKINSTALLER),true)
@@ -687,123 +762,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 +805,23 @@
 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,applypatch_static)/applypatch_static \
+	$(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_SYSTEMIMAGE) \
 		$(INSTALLED_USERDATAIMAGE_TARGET) \
 		$(INSTALLED_ANDROID_INFO_TXT_TARGET) \
-		$(INTERNAL_OTA_SCRIPT_TARGET) \
 		$(built_ota_tools) \
 		$(APKCERTS_FILE) \
 		| $(ACP)
@@ -895,20 +869,56 @@
 		$(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
+	$(hide) echo "blocksize $(BOARD_FLASH_BLOCK_SIZE)" > $(zip_root)/META/imagesizes.txt
+	$(hide) echo "boot $(BOARD_BOOTIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt
+	$(hide) echo "recovery $(BOARD_RECOVERYIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt
+	$(hide) echo "system $(BOARD_SYSTEMIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt
+	$(hide) echo "userdata $(BOARD_USERDATAIMAGE_MAX_SIZE)" >> $(zip_root)/META/imagesizes.txt
 	@# 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)
+ifneq ($(TARGET_PRODUCT),generic)
+
+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)
+
+$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) otatools
+	@echo "Package OTA: $@"
+	$(hide) ./build/tools/releasetools/ota_from_target_files \
+	   -p $(HOST_OUT) \
+           -k $(KEY_CERT_PAIR) \
+           $(BUILT_TARGET_FILES_PACKAGE) $@
+
+.PHONY: otapackage
+otapackage: $(INTERNAL_OTA_PACKAGE_TARGET)
+
+endif    # TARGET_PRODUCT != generic
+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 +1019,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,13 +1030,17 @@
 
 INTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 
-$(INTERNAL_UPDATE_PACKAGE_TARGET): $(INTERNAL_UPDATE_PACKAGE_FILES)
+$(INTERNAL_UPDATE_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) otatools
 	@echo "Package: $@"
-	$(hide) zip -qj $@ $(INTERNAL_UPDATE_PACKAGE_FILES)
+	$(hide) ./build/tools/releasetools/img_from_target_files \
+	   -p $(HOST_OUT) \
+	   $(BUILT_TARGET_FILES_PACKAGE) $@
 
-else
-INTERNAL_UPDATE_PACKAGE_TARGET :=
-endif
+.PHONY: updatepackage
+updatepackage: $(INTERNAL_UPDATE_PACKAGE_TARGET)
+
+endif    # TARGET_PRODUCT != sdk
+endif    # TARGET_SIMULATOR != true
 
 # -----------------------------------------------------------------
 # The emulator package
@@ -1116,7 +1124,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..a6bf504 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)
@@ -357,6 +356,8 @@
 $(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)
 
@@ -391,6 +392,25 @@
 
 endif # !LOCAL_UNINSTALLABLE_MODULE
 
+
+###########################################################
+## CHECK_BUILD goals
+###########################################################
+
+# If nobody has defined a more specific module for the
+# checked modules, use LOCAL_BUILT_MODULE.  This was old
+# behavior, so it should be a safe default.
+ifndef LOCAL_CHECKED_MODULE
+  LOCAL_CHECKED_MODULE := $(LOCAL_BUILT_MODULE)
+endif
+
+# If they request that this module not be checked, then don't.
+# PLEASE DON'T SET THIS.  ANY PLACES THAT SET THIS WITHOUT
+# GOOD REASON WILL HAVE IT REMOVED.
+ifdef LOCAL_DONT_CHECK_MODULE
+  LOCAL_CHECKED_MODULE :=
+endif
+
 ###########################################################
 ## Register with ALL_MODULES
 ###########################################################
@@ -403,6 +423,8 @@
     $(ALL_MODULES.$(LOCAL_MODULE).PATH) $(LOCAL_PATH)
 ALL_MODULES.$(LOCAL_MODULE).TAGS := \
     $(ALL_MODULES.$(LOCAL_MODULE).TAGS) $(LOCAL_MODULE_TAGS)
+ALL_MODULES.$(LOCAL_MODULE).CHECKED := \
+    $(ALL_MODULES.$(LOCAL_MODULE).CHECKED) $(LOCAL_CHECKED_MODULE)
 ALL_MODULES.$(LOCAL_MODULE).BUILT := \
     $(ALL_MODULES.$(LOCAL_MODULE).BUILT) $(LOCAL_BUILT_MODULE)
 ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
@@ -429,9 +451,6 @@
 $(foreach tag,$(LOCAL_MODULE_TAGS),\
     $(eval ALL_MODULE_NAME_TAGS.$(tag) += $(LOCAL_MODULE)))
 
-# Always build everything, but only install a subset.
-ALL_BUILT_MODULES += $(LOCAL_BUILT_MODULE)
-
 ###########################################################
 ## NOTICE files
 ###########################################################
diff --git a/core/binary.mk b/core/binary.mk
index 0f35d3f..ddcdc6f 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
@@ -212,6 +212,19 @@
 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.
 ###########################################################
 
diff --git a/core/build_id.mk b/core/build_id.mk
index cb18bc4..060c9b5 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 := Donut
 
 # DISPLAY_BUILD_NUMBER should only be set for development branches,
 # If set, the BUILD_NUMBER (cl) is appended to the BUILD_ID for
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index b2e95b4..28044c4 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -5,6 +5,8 @@
 LOCAL_MODULE:=
 LOCAL_MODULE_PATH:=
 LOCAL_MODULE_STEM:=
+LOCAL_DONT_CHECK_MODULE:=
+LOCAL_CHECKED_MODULE:=
 LOCAL_BUILT_MODULE:=
 LOCAL_BUILT_MODULE_STEM:=
 OVERRIDE_BUILT_MODULE_PATH:=
@@ -45,6 +47,7 @@
 LOCAL_PREBUILT_EXECUTABLES:=
 LOCAL_PREBUILT_JAVA_LIBRARIES:=
 LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES:=
+LOCAL_PREBUILT_STRIP_COMMENTS:=
 LOCAL_INTERMEDIATE_SOURCES:=
 LOCAL_JAVA_LIBRARIES:=
 LOCAL_NO_STANDARD_LIBRARIES:=
diff --git a/core/combo/linux-arm.mk b/core/combo/linux-arm.mk
index d2e0672..11a8ac7 100644
--- a/core/combo/linux-arm.mk
+++ b/core/combo/linux-arm.mk
@@ -73,40 +73,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 
 # 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
+  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))
diff --git a/core/combo/linux-x86.mk b/core/combo/linux-x86.mk
index 372c63e..f466147 100644
--- a/core/combo/linux-x86.mk
+++ b/core/combo/linux-x86.mk
@@ -10,7 +10,7 @@
 ifeq ($(combo_target),HOST_)
 # $(1): The file to check
 define get-file-size
-stat --format "%s" "$(1)"
+stat --format "%s" "$(1)" | tr -d '\n'
 endef
 endif
 
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..b705de5 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -76,12 +76,10 @@
 
 # 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:=
+COMMON_GLOBAL_CPPFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -Wnon-virtual-dtor
+COMMON_RELEASE_CPPFLAGS:= -DNDEBUG -UDEBUG
 
 # Set the extensions used for various packages
 COMMON_PACKAGE_SUFFIX := .zip
@@ -158,6 +156,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 +226,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 +245,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,21 +253,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
+TARGET_GLOBAL_CFLAGS += $(TARGET_RELEASE_CFLAGS)
+TARGET_GLOBAL_CPPFLAGS += $(TARGET_RELEASE_CPPFLAGS)
 
 # TODO: do symbol compression
 TARGET_COMPRESS_MODULE_SYMBOLS := false
@@ -290,7 +275,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..9b3a03d 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -41,9 +41,6 @@
 # set of installed targets.
 ALL_DEFAULT_INSTALLED_MODULES:=
 
-# Full paths to all targets that will be built.
-ALL_BUILT_MODULES:=
-
 # The list of tags that have been defined by
 # LOCAL_MODULE_TAGS.  Each word in this variable maps
 # to a corresponding ALL_MODULE_TAGS.<tagname> variable
@@ -794,6 +791,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 +884,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 +1165,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 +1221,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 +1236,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),$(dir $(PRIVATE_CLASS_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' >> $(dir $(PRIVATE_CLASS_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' < $(dir $(PRIVATE_CLASS_INTERMEDIATES_DIR))/java-source-list \
+    | sort -u > $(dir $(PRIVATE_CLASS_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 \
+    \@$(dir $(PRIVATE_CLASS_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 $(dir $(PRIVATE_CLASS_INTERMEDIATES_DIR))/java-source-list
+$(hide) rm -f $(dir $(PRIVATE_CLASS_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 +1297,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 +1307,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 +1382,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) .
@@ -1413,6 +1469,19 @@
 $(hide) cp -fp $< $@
 endef
 
+# The same as copy-file-to-target, but use the zipalign tool to do so.
+define copy-file-to-target-with-zipalign
+@mkdir -p $(dir $@)
+$(hide) $(ZIPALIGN) -f 4 $< $@
+endef
+
+# The same as copy-file-to-target, but strip out "# comment"-style
+# comments (for config files and such).
+define copy-file-to-target-strip-comments
+@mkdir -p $(dir $@)
+$(hide) sed -e 's/#.*$$//' -e 's/[ \t]*$$//' -e '/^$$/d' < $< > $@
+endef
+
 # The same as copy-file-to-target, but don't preserve
 # the old modification time.
 define copy-file-to-new-target
@@ -1433,6 +1502,18 @@
 $(copy-file-to-target)
 endef
 
+# Copy a prebuilt file to a target location, using zipalign on it.
+define transform-prebuilt-to-target-with-zipalign
+@echo "$(if $(PRIVATE_IS_HOST_MODULE),host,target) Prebuilt APK: $(PRIVATE_MODULE) ($@)"
+$(copy-file-to-target-with-zipalign)
+endef
+
+# Copy a prebuilt file to a target location, stripping "# comment" comments.
+define transform-prebuilt-to-target-strip-comments
+@echo "$(if $(PRIVATE_IS_HOST_MODULE),host,target) Prebuilt: $(PRIVATE_MODULE) ($@)"
+$(copy-file-to-target-strip-comments)
+endef
+
 
 ###########################################################
 ## On some platforms (MacOS), after copying a static
@@ -1481,8 +1562,17 @@
 $(error HOST_OS must define get-file-size)
 endif
 
-# $(1): The file to check (often $@)
-# $(2): The maximum size, in decimal bytes
+# Convert a partition data size (eg, as reported in /proc/mtd) to the
+# size of the image used to flash that partition (which includes a
+# 64-byte spare area for each 2048-byte page).
+# $(1): the partition data size
+define image-size-from-data-size
+$(shell echo $$(($(1) / 2048 * (2048+64))))
+endef
+
+# $(1): The file(s) to check (often $@)
+# $(2): The maximum total image size, in decimal bytes
+# $(3): the type of filesystem "yaffs" or "raw"
 #
 # If $(2) is empty, evaluates to "true"
 #
@@ -1491,19 +1581,26 @@
 # next whole flash block size.
 define assert-max-file-size
 $(if $(2), \
-  fileSize=`$(call get-file-size,$(1))`; \
-  maxSize=$(2); \
-  onePct=`expr "(" $$maxSize + 99 ")" / 100`; \
-  onePct=`expr "(" "(" $$onePct + $(BOARD_FLASH_BLOCK_SIZE) - 1 ")" / \
-          $(BOARD_FLASH_BLOCK_SIZE) ")" "*" $(BOARD_FLASH_BLOCK_SIZE)`; \
-  reserve=`expr 2 "*" $(BOARD_FLASH_BLOCK_SIZE)`; \
-  if [ "$$onePct" -gt "$$reserve" ]; then \
-      reserve="$$onePct"; \
+  size=$$(for i in $(1); do $(call get-file-size,$$i); echo +; done; echo 0); \
+  total=$$(( $$( echo "$$size" ) )); \
+  printname=$$(echo -n "$(1)" | tr " " +); \
+  echo "$$printname total size is $$total"; \
+  img_blocksize=$(call image-size-from-data-size,$(BOARD_FLASH_BLOCK_SIZE)); \
+  if [ "$(3)" == "yaffs" ]; then \
+    reservedblocks=5; \
+  else \
+    reservedblocks=0; \
   fi; \
-  maxSize=`expr $$maxSize - $$reserve`; \
-  if [ "$$fileSize" -gt "$$maxSize" ]; then \
-      echo "error: $(1) too large ($$fileSize > [$(2) - $$reserve])"; \
-      false; \
+  twoblocks=$$((img_blocksize * 2)); \
+  onepct=$$((((($(2) / 100) - 1) / img_blocksize + 1) * img_blocksize)); \
+  reserve=$$(((twoblocks > onepct ? twoblocks : onepct) + \
+               reservedblocks * img_blocksize)); \
+  maxsize=$$(($(2) - reserve)); \
+  if [ "$$total" -gt "$$maxsize" ]; then \
+    echo "error: $$printname too large ($$total > [$(2) - $$reserve])"; \
+    false; \
+  elif [ "$$total" -gt $$((maxsize - 32768)) ]; then \
+    echo "WARNING: $$printname approaching size limit ($$total now; limit $$maxsize)"; \
   fi \
  , \
   true \
diff --git a/core/droiddoc.mk b/core/droiddoc.mk
index 30bd918..03ffa55 100644
--- a/core/droiddoc.mk
+++ b/core/droiddoc.mk
@@ -127,7 +127,7 @@
 	$(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX) \
 	$(HOST_OUT_SHARED_LIBRARIES)/libclearsilver-jni$(HOST_JNILIB_SUFFIX)
 
-$(full_target): PRIVATE_DOCLETPATH := $(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(full_target): PRIVATE_DOCLETPATH := $(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/apicheck$(COMMON_JAVA_PACKAGE_SUFFIX)
 $(full_target): PRIVATE_CURRENT_BUILD := -hdf page.build $(BUILD_ID)-$(BUILD_NUMBER)
 $(full_target): PRIVATE_CURRENT_TIME :=  -hdf page.now "$(shell date "+%d %b %Y %k:%M")"
 $(full_target): PRIVATE_TEMPLATE_DIR := $(LOCAL_DROIDDOC_TEMPLATE_DIR)
diff --git a/core/envsetup.mk b/core/envsetup.mk
index ba93549..31901e9 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
@@ -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))
diff --git a/core/java.mk b/core/java.mk
index 9150a5c..658b173 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -37,6 +37,54 @@
 $(error LOCAL_BUILT_MODULE_STEM may not be "$(LOCAL_BUILT_MODULE_STEM)")
 endif
 
+
+##############################################################################
+# Define the intermediate targets before including base_rules so they get
+# the correct environment.
+##############################################################################
+
+intermediates := $(call local-intermediates-dir)
+intermediates.COMMON := $(call local-intermediates-dir,COMMON)
+
+# This is cleared below, and re-set if we really need it.
+full_classes_jar := $(intermediates.COMMON)/classes.jar
+
+# Emma source code coverage
+ifneq ($(EMMA_INSTRUMENT),true)
+LOCAL_NO_EMMA_INSTRUMENT := true
+LOCAL_NO_EMMA_COMPILE := true
+endif
+
+# Choose leaf name for the compiled jar file.
+ifneq ($(LOCAL_NO_EMMA_COMPILE),true)
+full_classes_compiled_jar_leaf := classes-no-debug-var.jar
+else
+full_classes_compiled_jar_leaf := classes-full-debug.jar
+endif
+full_classes_compiled_jar := $(intermediates.COMMON)/$(full_classes_compiled_jar_leaf)
+
+emma_intermediates_dir := $(intermediates.COMMON)/emma_out
+# the 'lib/$(full_classes_compiled_jar_leaf)' portion of this path is fixed in
+# the emma tool
+full_classes_emma_jar := $(emma_intermediates_dir)/lib/$(full_classes_compiled_jar_leaf)
+full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar
+full_classes_jarjar_jar := $(full_classes_jar)
+built_dex := $(intermediates.COMMON)/classes.dex
+
+LOCAL_INTERMEDIATE_TARGETS += \
+    $(full_classes_jar) \
+    $(full_classes_compiled_jar) \
+    $(full_classes_emma_jar) \
+    $(full_classes_stubs_jar) \
+    $(full_classes_jarjar_jar) \
+    $(built_dex)
+
+
+# TODO: It looks like the only thing we need from base_rules is
+# all_java_sources.  See if we can get that by adding a
+# common_java.mk, and moving the include of base_rules.mk to
+# after all the declarations.
+
 #######################################
 include $(BUILD_SYSTEM)/base_rules.mk
 #######################################
@@ -65,6 +113,7 @@
 # LOCAL_BUILT_MODULE, so it will inherit the necessary PRIVATE_*
 # variable definitions.
 full_classes_jar := $(intermediates.COMMON)/classes.jar
+built_dex := $(intermediates.COMMON)/classes.dex
 
 # Droiddoc isn't currently able to generate stubs for modules, so we're just
 # allowing it to use the classes.jar as the "stubs" that would be use to link
@@ -74,34 +123,28 @@
 #   it, so it's closest to what's on the device.
 # - This extra copy, with the dependency on LOCAL_BUILT_MODULE allows the
 #   PRIVATE_ vars to be preserved.
-full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar
 $(full_classes_stubs_jar): PRIVATE_SOURCE_FILE := $(full_classes_jar)
 $(full_classes_stubs_jar) : $(LOCAL_BUILT_MODULE) | $(ACP)
 	@echo Copying $(PRIVATE_SOURCE_FILE)
 	$(hide) $(ACP) -fp $(PRIVATE_SOURCE_FILE) $@
 ALL_MODULES.$(LOCAL_MODULE).STUBS := $(full_classes_stubs_jar)
 
-# Emma source code coverage
-ifneq ($(EMMA_INSTRUMENT),true) 
-LOCAL_NO_EMMA_INSTRUMENT := true
-LOCAL_NO_EMMA_COMPILE := true
-endif
-
-# Choose leaf name for the compiled jar file.
-ifneq ($(LOCAL_NO_EMMA_COMPILE),true) 
-full_classes_compiled_jar_leaf := classes-no-debug-var.jar
-else
-full_classes_compiled_jar_leaf := classes-full-debug.jar
-endif
-
 # Compile the java files to a .jar file.
 # This intentionally depends on java_sources, not all_java_sources.
 # Deps for generated source files must be handled separately,
 # via deps on the target that generates the sources.
-full_classes_compiled_jar := $(intermediates.COMMON)/$(full_classes_compiled_jar_leaf)
 $(full_classes_compiled_jar): $(java_sources) $(full_java_lib_deps)
 	$(transform-java-to-classes.jar)
 
+# All of the rules after full_classes_compiled_jar are very unlikely
+# to fail except for bugs in their respective tools.  If you would
+# like to run these rules, add the "all" modifier goal to the make
+# command line.
+# This overwrites the value defined in base_rules.mk.  That's a little
+# dirty.  It's preferable to set LOCAL_CHECKED_MODULE, but this has to
+# be done after the inclusion of base_rules.mk.
+ALL_MODULES.$(LOCAL_MODULE).CHECKED := $(full_classes_compiled_jar)
+
 ifneq ($(LOCAL_NO_EMMA_COMPILE),true) 
 # If you instrument class files that have local variable debug information in
 # them emma does not correctly maintain the local variable table.
@@ -115,11 +158,6 @@
 $(full_classes_compiled_jar): PRIVATE_JAVAC_DEBUG_FLAGS := -g
 endif
 
-emma_intermediates_dir := $(intermediates.COMMON)/emma_out
-# the 'lib/$(full_classes_compiled_jar_leaf)' portion of this path is fixed in 
-# the emma tool
-full_classes_emma_jar := $(emma_intermediates_dir)/lib/$(full_classes_compiled_jar_leaf)
-
 ifeq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true)
 # Skip adding emma instrumentation to class files if this is a static library,
 # since it will be instrumented by the package that includes it
@@ -142,7 +180,6 @@
 
 # Run jarjar if necessary, otherwise just copy the file.  This is the last
 # part of this step, so the output of this command is full_classes_jar.
-full_classes_jarjar_jar := $(full_classes_jar)
 ifneq ($(strip $(LOCAL_JARJAR_RULES)),)
 $(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES)
 $(full_classes_jarjar_jar): $(full_classes_emma_jar) | jarjar
@@ -154,9 +191,6 @@
 	$(hide) $(ACP) $< $@
 endif
 
-
-built_dex := $(intermediates.COMMON)/classes.dex
-
 # Override PRIVATE_INTERMEDIATES_DIR so that install-dex-debug
 # will work even when intermediates != intermediates.COMMON.
 $(built_dex): PRIVATE_INTERMEDIATES_DIR := $(intermediates.COMMON)
@@ -188,9 +222,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..e0489fb 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -85,13 +85,48 @@
 $(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.
 # (must be defined before including definitions.make)
-INTERNAL_MODIFIER_TARGETS := showcommands
+INTERNAL_MODIFIER_TARGETS := showcommands checkbuild
 
 # Bring in standard build system definitions.
 include $(BUILD_SYSTEM)/definitions.mk
@@ -265,11 +300,11 @@
 endif
 
 
-# If all they typed was make showcommands, we'll actually build
-# the default target.
-ifeq ($(MAKECMDGOALS),showcommands)
-.PHONY: showcommands
-showcommands: $(DEFAULT_GOAL)
+# If they only used the modifier goals (showcommands, checkbuild), we'll actually
+# build the default target.
+ifeq ($(filter-out $(INTERNAL_MODIFIER_TARGETS),$(MAKECMDGOALS)),)
+.PHONY: $(INTERNAL_MODIFIER_TARGETS)
+$(INTERNAL_MODIFIER_TARGETS): $(DEFAULT_GOAL)
 endif
 
 # These targets are going to delete stuff, don't bother including
@@ -304,12 +339,12 @@
 subdirs := \
 	prebuilt \
 	build/libs/host \
+	build/tools/zipalign \
 	dalvik/dexdump \
 	dalvik/libdex \
 	dalvik/tools/dmtracedump \
 	dalvik/tools/hprof-conv \
 	development/emulator/mksdcard \
-	development/tools/activitycreator \
 	development/tools/line_endings \
 	development/host \
 	external/expat \
@@ -332,10 +367,10 @@
 $(warning sdk-only: javac available.)
 subdirs += \
 	build/tools/signapk \
-	build/tools/zipalign \
 	dalvik/dx \
 	dalvik/libcore \
 	development/apps \
+	development/tools/archquery \
 	development/tools/androidprefs \
 	development/tools/apkbuilder \
 	development/tools/jarutils \
@@ -418,6 +453,10 @@
 
 # Clean up/verify variables defined by the board config file.
 TARGET_BOOTLOADER_BOARD_NAME := $(strip $(TARGET_BOOTLOADER_BOARD_NAME))
+TARGET_CPU_ABI := $(strip $(TARGET_CPU_ABI))
+ifeq ($(TARGET_CPU_ABI),)
+  $(error No TARGET_CPU_ABI defined by board config: $(board_config_mk))
+endif
 
 #
 # Include all of the makefiles in the system
@@ -499,7 +538,6 @@
 # poisons the rest of the tags and shouldn't appear
 # on any list.
 Default_MODULES := $(sort $(ALL_DEFAULT_INSTALLED_MODULES) \
-                          $(ALL_BUILT_MODULES) \
                           $(CUSTOM_MODULES))
 # TODO: Remove the 3 places in the tree that use
 # ALL_DEFAULT_INSTALLED_MODULES and get rid of it from this list.
@@ -565,7 +603,7 @@
 endif
 
 
-# config/Makefile contains extra stuff that we don't want to pollute this
+# build/core/Makefile contains extra stuff that we don't want to pollute this
 # top-level makefile with.  It expects that ALL_DEFAULT_INSTALLED_MODULES
 # contains everything that's built during the current make, but it also further
 # extends ALL_DEFAULT_INSTALLED_MODULES.
@@ -576,6 +614,20 @@
 
 endif # dont_bother
 
+# These are additional goals that we build, in order to make sure that there
+# is as little code as possible in the tree that doesn't build.
+modules_to_check := $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).CHECKED))
+
+# If you would like to build all goals, and not skip any intermediate
+# steps, you can pass the "all" modifier goal on the commandline.
+ifneq ($(filter all,$(MAKECMDGOALS)),)
+modules_to_check += $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).BUILT))
+endif
+
+# for easier debugging
+modules_to_check := $(sort $(modules_to_check))
+#$(error modules_to_check $(modules_to_check))
+
 # -------------------------------------------------------------------
 # This is used to to get the ordering right, you can also use these,
 # but they're considered undocumented, so don't complain if their
@@ -593,10 +645,16 @@
 
 # All the droid stuff, in directories
 .PHONY: files
-files: prebuilt $(modules_to_install) $(INSTALLED_ANDROID_INFO_TXT_TARGET)
+files: prebuilt \
+        $(modules_to_install) \
+        $(modules_to_check) \
+        $(INSTALLED_ANDROID_INFO_TXT_TARGET)
 
 # -------------------------------------------------------------------
 
+.PHONY: checkbuild
+checkbuild: $(modules_to_check)
+
 .PHONY: ramdisk
 ramdisk: $(INSTALLED_RAMDISK_TARGET)
 
diff --git a/core/pathmap.mk b/core/pathmap.mk
index 13cb80d..e281b9d 100644
--- a/core/pathmap.mk
+++ b/core/pathmap.mk
@@ -83,6 +83,8 @@
 	    sax \
 	    telephony \
 	    wifi \
+	    vpn \
+	    keystore \
 	 )
 
 #
diff --git a/core/prebuilt.mk b/core/prebuilt.mk
index 2d93162..4cbbb99 100644
--- a/core/prebuilt.mk
+++ b/core/prebuilt.mk
@@ -3,7 +3,7 @@
 ##
 ## Additional inputs from base_rules.make:
 ## None.
-## 
+##
 ###########################################################
 
 ifneq ($(LOCAL_PREBUILT_LIBS),)
@@ -26,8 +26,35 @@
   prebuilt_module_is_a_library :=
 endif
 
+# Ensure that prebuilt .apks have been aligned.
+ifneq ($(filter APPS,$(LOCAL_MODULE_CLASS)),)
+$(LOCAL_BUILT_MODULE) : $(LOCAL_PATH)/$(LOCAL_SRC_FILES) | $(ZIPALIGN)
+	$(transform-prebuilt-to-target-with-zipalign)
+else
+ifneq ($(LOCAL_PREBUILT_STRIP_COMMENTS),)
+$(LOCAL_BUILT_MODULE) : $(LOCAL_PATH)/$(LOCAL_SRC_FILES)
+	$(transform-prebuilt-to-target-strip-comments)
+else
 $(LOCAL_BUILT_MODULE) : $(LOCAL_PATH)/$(LOCAL_SRC_FILES) | $(ACP)
 	$(transform-prebuilt-to-target)
+endif
+endif
+
+ifeq ($(LOCAL_CERTIFICATE),)
+  # can't re-sign this package, so predexopt is not available.
+else
+
+# If this is not an absolute certificate, assign it to a generic one.
+ifeq ($(dir $(strip $(LOCAL_CERTIFICATE))),./)
+    LOCAL_CERTIFICATE := $(SRC_TARGET_DIR)/product/security/$(LOCAL_CERTIFICATE)
+endif
+
+PACKAGES.$(LOCAL_MODULE).PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8
+PACKAGES.$(LOCAL_MODULE).CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem
+PACKAGES := $(PACKAGES) $(LOCAL_MODULE)
+
+endif
+
 ifneq ($(prebuilt_module_is_a_library),)
   ifneq ($(LOCAL_IS_HOST_MODULE),)
 	$(transform-host-ranlib-copy-hack)
diff --git a/core/prelink-linux-arm.map b/core/prelink-linux-arm.map
index c32566f..c8e0db5 100644
--- a/core/prelink-linux-arm.map
+++ b/core/prelink-linux-arm.map
@@ -21,6 +21,7 @@
 libevent.so             0xAF800000
 libssl.so               0xAF700000
 libcrypto.so            0xAF500000
+libsysutils.so          0xAF400000
 
 # bluetooth
 liba2dp.so              0xAEE00000
@@ -59,6 +60,8 @@
 libOpenVGU_CM.so        0xAC800000
 libEGL.so               0xAC700000
 
+libacc.so               0xAC600000
+
 libexif.so              0xAC500000
 libui.so                0xAC400000
 libsgl.so               0xAC000000
@@ -104,6 +107,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 +135,4 @@
 librpc.so               0x9A400000
 libtrace_test.so        0x9A300000
 libsrec_jni.so          0x9A200000
+libcerttool_jni.so      0x9A100000
diff --git a/core/product_config.mk b/core/product_config.mk
index 64488d8..7cfa5f4 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 := \
@@ -245,32 +248,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..96ce0c1 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -17,21 +17,23 @@
 
 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
+HOSTTESTLIB_JAR := $(HOST_OUT_JAVA_LIBRARIES)/hosttestlib.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 +47,20 @@
 	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_SECURITY_APPS_LIST := \
+	CtsAppAccessData \
+	CtsAppWithData \
+	CtsInstrumentationAppDiffCert \
+	CtsPermissionDeclareApp \
+	CtsSharedUidInstall \
+	CtsSharedUidInstallDiffCert \
+	CtsSimpleAppInstall \
+	CtsSimpleAppInstallDiffCert \
+	CtsTargetInstrumentationApp \
+	CtsUsePermissionDiffCert
 
 CTS_CASE_LIST := \
 	DeviceInfoCollector \
@@ -53,23 +68,38 @@
 	CtsAppTestCases \
 	CtsContentTestCases \
 	CtsDatabaseTestCases \
+        CtsGestureTestCases \
 	CtsGraphicsTestCases \
 	CtsHardwareTestCases \
 	CtsLocationTestCases \
+	CtsMediaTestCases \
 	CtsOsTestCases \
 	CtsPermissionTestCases \
+	CtsPermission2TestCases \
 	CtsProviderTestCases \
+	CtsTelephonyTestCases \
 	CtsTextTestCases \
 	CtsUtilTestCases \
 	CtsViewTestCases \
+	CtsWebkitTestCases \
 	CtsWidgetTestCases \
 	CtsNetTestCases \
 	SignatureTest \
-	$(CTS_CORE_CASE_LIST)
+	CtsPerformanceTestCases \
+	CtsPerformance2TestCases \
+	CtsPerformance3TestCases \
+	CtsPerformance4TestCases \
+	CtsPerformance5TestCases \
+	ApiDemos \
+	ApiDemosReferenceTest \
+	$(CTS_CORE_CASE_LIST) \
+	$(CTS_SECURITY_APPS_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) $(HOSTTESTLIB_JAR) $(ACP)
 # Make necessary directory for CTS
 	@rm -rf $(PRIVATE_CTS_DIR)
 	@mkdir -p $(TMP_DIR)
@@ -80,23 +110,30 @@
 # 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
+# Copy hosttestlib jar
+	$(hide) $(ACP) -fp $(HOSTTESTLIB_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
-
+# Parameters:
+# $1 : The output file where the description should be written (without the '.xml' extension)
+# $2 : The AndroidManifest.xml corresponding to the test package
+# $3 : The name of the TestSuite generator class to use
+# $4 : The Android.mk corresponding to the test package (required for host-side tests only)
 define generate-core-test-description
 @echo "Generate core-test description ("$(notdir $(1))")"
 $(hide) java $(PRIVATE_JAVAOPTS) \
 	-classpath $(PRIVATE_CLASSPATH) \
 	$(PRIVATE_PARAMS) CollectAllTests $(1) \
-	$(2) $(3)
+	$(2) $(3) $(4)
 endef
 
 CORE_INTERMEDIATES :=$(call intermediates-dir-for,JAVA_LIBRARIES,core,,COMMON)
@@ -112,7 +149,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 +168,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)
@@ -201,10 +235,17 @@
 		dot.junit.AllJunitHostTests, cts/tools/vm-tests/Android.mk)
 	$(ACP) -fv $(VMTESTS_INTERMEDIATES)/android.core.vm-tests.jar $(PRIVATE_DIR)/repository/testcases/android.core.vm-tests.jar
 
+# Move app security host-side tests to the repository
+APP_SECURITY_LIB := $(cts_dir)/$(cts_name)/repository/testcases/CtsAppSecurityTests.jar
+
+$(APP_SECURITY_LIB): $(HOST_OUT_JAVA_LIBRARIES)/CtsAppSecurityTests.jar $(cts_dir)/all_cts_files_stamp $(ACP)
+	$(ACP) -fv $(HOST_OUT_JAVA_LIBRARIES)/CtsAppSecurityTests.jar $(APP_SECURITY_LIB)
+
 # Generate the default test plan for User.
-$(DEFAULT_TEST_PLAN): $(cts_dir)/all_cts_files_stamp $(cts_dir)/all_cts_core_files_stamp $(cts_tools_src_dir)/utils/genDefaultTestPlan.sh $(CORE_VM_TEST_DESC)
-	$(hide) bash $(cts_tools_src_dir)/utils/genDefaultTestPlan.sh cts/tests/tests/ \
-     $(PRIVATE_DIR) $(TMP_DIR) $(TOP) $(TARGET_COMMON_OUT_ROOT) $(OUT_DIR)
+# Usage: buildCts.py <testRoot> <ctsOutputDir> <tempDir> <androidRootDir> <docletPath>
+$(DEFAULT_TEST_PLAN): $(cts_dir)/all_cts_files_stamp $(cts_dir)/all_cts_core_files_stamp $(cts_tools_src_dir)/utils/buildCts.py $(CORE_VM_TEST_DESC) $(APP_SECURITY_LIB) $(HOST_OUT_JAVA_LIBRARIES)/descGen.jar
+	$(hide) $(cts_tools_src_dir)/utils/buildCts.py cts/tests/tests/ $(PRIVATE_DIR) $(TMP_DIR) \
+		$(TOP) $(HOST_OUT_JAVA_LIBRARIES)/descGen.jar
 
 # Package CTS and clean up.
 #
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..0e6cf5c 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,15 +41,38 @@
   # 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 := 1.6
 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.
-  PLATFORM_SDK_VERSION := 3
+  # 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 := 4
+endif
+
+ifeq "" "$(PLATFORM_VERSION_CODENAME)"
+  # If the build is not a final release build, then this is the current
+  # development code-name.  If this is a final release build, it is simply "REL".
+  PLATFORM_VERSION_CODENAME := REL
+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)"
diff --git a/envsetup.sh b/envsetup.sh
index ad8e2ff..6325c3d 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -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
@@ -976,18 +944,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 +1009,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/generic/BoardConfig.mk b/target/board/generic/BoardConfig.mk
index a874742..6ec2de3 100644
--- a/target/board/generic/BoardConfig.mk
+++ b/target/board/generic/BoardConfig.mk
@@ -7,5 +7,6 @@
 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..347b127 100644
--- a/target/product/core.mk
+++ b/target/product/core.mk
@@ -12,12 +12,16 @@
     Launcher \
     HTMLViewer \
     Phone \
+    ApplicationsProvider \
     ContactsProvider \
     DownloadProvider \
     GoogleSearch \
     MediaProvider \
+    PicoTts \
     SettingsProvider \
     TelephonyProvider \
+    TtsService \
+    VpnServices \
     UserDictionaryProvider \
     PackageInstaller \
     Bugreport
diff --git a/target/product/sdk.mk b/target/product/sdk.mk
index 4b24cf5..d847377 100644
--- a/target/product/sdk.mk
+++ b/target/product/sdk.mk
@@ -3,6 +3,7 @@
 PRODUCT_PACKAGES := \
 	AlarmClock \
 	Camera \
+	Calculator \
 	Development \
 	DrmProvider \
 	Email \
@@ -18,7 +19,12 @@
 	sqlite3 \
 	LatinIME \
 	PinyinIME \
+	OpenWnn \
+	libWnnEngDic \
+	libWnnJpnDic \
+	libwnndict \
 	ApiDemos \
+	GestureBuilder \
 	SoftKeyboard
 
 PRODUCT_COPY_FILES := \
diff --git a/tools/apicheck/src/com/android/apicheck/ApiCheck.java b/tools/apicheck/src/com/android/apicheck/ApiCheck.java
index f78117c..20a98ce 100644
--- a/tools/apicheck/src/com/android/apicheck/ApiCheck.java
+++ b/tools/apicheck/src/com/android/apicheck/ApiCheck.java
@@ -20,7 +20,6 @@
 import org.xml.sax.helpers.*;
 import java.io.*;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Stack;
 
 public class ApiCheck {
@@ -83,62 +82,62 @@
                 }
             }
 
-            String xmlFileName = args.get(0);
-            String xmlFileNameNew = args.get(1);
-            XMLReader xmlreader = null;
-            try {
-                // parse the XML files into our data structures
-                xmlreader = XMLReaderFactory.createXMLReader();
-                ApiCheck acheck = new ApiCheck();
-                MakeHandler handler = acheck.new MakeHandler();
-                xmlreader.setContentHandler(handler);
-                xmlreader.setErrorHandler(handler);
-                FileReader filereader = new FileReader(xmlFileName);
-                xmlreader.parse(new InputSource(filereader));
-                FileReader filereaderNew = new FileReader(xmlFileNameNew);
-                xmlreader.parse(new InputSource(filereaderNew));
+            ApiCheck acheck = new ApiCheck();
 
-                // establish the superclass relationships
-                handler.getOldApi().resolveSuperclasses();
-                handler.getNewApi().resolveSuperclasses();
-                
-                // finally, run the consistency check
-                handler.getOldApi().isConsistent(handler.getNewApi());
+            ApiInfo oldApi = acheck.parseApi(args.get(0));
+            ApiInfo newApi = acheck.parseApi(args.get(1));
 
-            } catch (SAXParseException e) {
-                Errors.error(Errors.PARSE_ERROR,
-                        new SourcePositionInfo(xmlFileName, e.getLineNumber(), 0),
-                        e.getMessage());
-            } catch (Exception e) {
-                e.printStackTrace();
-                Errors.error(Errors.PARSE_ERROR,
-                        new SourcePositionInfo(xmlFileName, 0, 0),
-                        e.getMessage());
-            } 
+            // only run the consistency check if we haven't had XML parse errors
+            if (!Errors.hadError) {
+                oldApi.isConsistent(newApi);
+            }
 
             Errors.printErrors();
             System.exit(Errors.hadError ? 1 : 0);
         }
 
-        private class MakeHandler extends DefaultHandler {
+    public ApiInfo parseApi(String xmlFile) {
+        FileReader fileReader = null;
+        try {
+            XMLReader xmlreader = XMLReaderFactory.createXMLReader();
+            MakeHandler handler = new MakeHandler();
+            xmlreader.setContentHandler(handler);
+            xmlreader.setErrorHandler(handler);
+            fileReader = new FileReader(xmlFile);
+            xmlreader.parse(new InputSource(fileReader));
+            ApiInfo apiInfo = handler.getApi();
+            apiInfo.resolveSuperclasses();
+            return apiInfo;
+        } catch (SAXParseException e) {
+            Errors.error(Errors.PARSE_ERROR,
+                    new SourcePositionInfo(xmlFile, e.getLineNumber(), 0),
+                    e.getMessage());
+        } catch (Exception e) {
+            e.printStackTrace();
+            Errors.error(Errors.PARSE_ERROR,
+                    new SourcePositionInfo(xmlFile, 0, 0), e.getMessage());
+        } finally {
+            if (fileReader != null) {
+                try {
+                    fileReader.close();
+                } catch (IOException ignored) {}
+            }
+        }
+        return null;
+    }
+
+    private static class MakeHandler extends DefaultHandler {
             
-            private Integer mWarningCount;
-            private ApiInfo mOriginalApi;
-            private ApiInfo mNewApi;
-            private boolean mOldApi;
+            private ApiInfo mApi;
             private PackageInfo mCurrentPackage;
             private ClassInfo mCurrentClass;
             private AbstractMethodInfo mCurrentMethod;
-            private ConstructorInfo mCurrentConstructor;
             private Stack<ClassInfo> mClassScope = new Stack<ClassInfo>();
-            
-            
+
+
             public MakeHandler() {
                 super();
-                mOriginalApi = new ApiInfo();
-                mNewApi = new ApiInfo();
-                mOldApi = true;
-                
+                mApi = new ApiInfo();
             }
             
             public void startElement(String uri, String localName, String qName, 
@@ -229,25 +228,11 @@
                     mCurrentPackage.addClass(mCurrentClass);
                     mCurrentClass = mClassScope.pop();
                 } else if (qName.equals("package")){
-                    if (mOldApi) {
-                        mOriginalApi.addPackage(mCurrentPackage);
-                    } else {
-                        mNewApi.addPackage(mCurrentPackage);
-                    }
+                    mApi.addPackage(mCurrentPackage);
                 }
             }
-            public void endDocument() {
-                mOldApi = !mOldApi;
+            public ApiInfo getApi() {
+                return mApi;
             }
-            
-            public ApiInfo getOldApi() {
-                return mOriginalApi;
-            }
-            
-            public ApiInfo getNewApi() {
-                return mNewApi;
-            }
-
-
-            }
+        }
 }
diff --git a/tools/apicheck/src/com/android/apicheck/ClassInfo.java b/tools/apicheck/src/com/android/apicheck/ClassInfo.java
index 4bbf78b..5405ad2 100644
--- a/tools/apicheck/src/com/android/apicheck/ClassInfo.java
+++ b/tools/apicheck/src/com/android/apicheck/ClassInfo.java
@@ -187,8 +187,8 @@
         }
         
         for (FieldInfo mInfo : mFields.values()) {
-          if (cl.mFields.containsKey(mInfo.qualifiedName())) {
-              if (!mInfo.isConsistent(cl.mFields.get(mInfo.qualifiedName()))) {
+          if (cl.mFields.containsKey(mInfo.name())) {
+              if (!mInfo.isConsistent(cl.mFields.get(mInfo.name()))) {
                   consistent = false;
               }
           } else {
@@ -267,7 +267,7 @@
     }
     
     public void addField(FieldInfo fInfo) {
-        mFields.put(fInfo.qualifiedName(), fInfo);
+        mFields.put(fInfo.name(), fInfo);
       
     }
     
@@ -279,4 +279,26 @@
         return mExistsInBoth;
     }
 
+    public Map<String, ConstructorInfo> allConstructors() {
+        return mConstructors;
+    }
+
+    public Map<String, FieldInfo> allFields() {
+        return mFields;
+    }
+
+    public Map<String, MethodInfo> allMethods() {
+        return mMethods;
+    }
+
+    /**
+     * Returns the class hierarchy for this class, starting with this class.
+     */
+    public Iterable<ClassInfo> hierarchy() {
+        List<ClassInfo> result = new ArrayList<ClassInfo>(4);
+        for (ClassInfo c  = this; c != null; c = c.mSuperClass) {
+            result.add(c);
+        }
+        return result;
+    }
 }
diff --git a/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java b/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java
index 57d7617..f36c7cd 100644
--- a/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java
+++ b/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java
@@ -55,11 +55,12 @@
     }
     
     public String getHashableName() {
-      String returnString = qualifiedName();
+      StringBuilder result = new StringBuilder();
+      result.append(name());
       for (ParameterInfo pInfo : mParameters) {
-          returnString += ":" + pInfo.getType();
+          result.append(":").append(pInfo.getType());
       }
-      return returnString;
+      return result.toString();
     }
     
     public boolean isInBoth() {
diff --git a/tools/apicheck/src/com/android/apicheck/MethodInfo.java b/tools/apicheck/src/com/android/apicheck/MethodInfo.java
index 86e20de..e4e4537 100644
--- a/tools/apicheck/src/com/android/apicheck/MethodInfo.java
+++ b/tools/apicheck/src/com/android/apicheck/MethodInfo.java
@@ -195,7 +195,7 @@
     }
     
     public String getHashableName() {
-        return qualifiedName() + getParameterHash();
+        return name() + getParameterHash();
     }
     
     public String getSignature() {
diff --git a/tools/applypatch/Android.mk b/tools/applypatch/Android.mk
index 09f9862..5796cef 100644
--- a/tools/applypatch/Android.mk
+++ b/tools/applypatch/Android.mk
@@ -12,18 +12,48 @@
 # 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
-LOCAL_MODULE := applypatch
-LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SRC_FILES := applypatch.c bsdiff.c freecache.c imgpatch.c utils.c
+LOCAL_MODULE := libapplypatch
 LOCAL_MODULE_TAGS := eng
-LOCAL_C_INCLUDES += external/bzip2
-LOCAL_STATIC_LIBRARIES += libmincrypt libbz libc
+LOCAL_C_INCLUDES += external/bzip2 external/zlib bootable/recovery
+LOCAL_STATIC_LIBRARIES += libmtdutils libmincrypt libbz libz
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := main.c
+LOCAL_MODULE := applypatch
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
+LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
 
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := main.c
+LOCAL_MODULE := applypatch_static
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_TAGS := eng
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
+LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := imgdiff.c utils.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..394c584 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,64 @@
   return 0;
 }
 
+// Write a memory buffer to target_mtd partition, a string of the form
+// "MTD:<partition>[:...]".  Return 0 on success.
+int WriteToMTDPartition(unsigned char* data, size_t len,
+                        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';
+
+  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;
+  }
+
+  size_t written = mtd_write_data(ctx, (char*)data, len);
+  if (written != len) {
+    fprintf(stderr, "only wrote %d of %d bytes to MTD %s\n",
+            written, len, partition);
+    mtd_write_close(ctx);
+    return -1;
+  }
+
+  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 +425,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]);
 
@@ -210,6 +464,26 @@
   return 0;
 }
 
+size_t FileSink(unsigned char* data, size_t len, void* token) {
+  return fwrite(data, 1, len, (FILE*)token);
+}
+
+typedef struct {
+  unsigned char* buffer;
+  size_t size;
+  size_t pos;
+} MemorySinkInfo;
+
+size_t MemorySink(unsigned char* data, size_t len, void* token) {
+  MemorySinkInfo* msi = (MemorySinkInfo*)token;
+  if (msi->size - msi->pos < len) {
+    return -1;
+  }
+  memcpy(msi->buffer + msi->pos, data, len);
+  msi->pos += len;
+  return len;
+}
+
 // Return the amount of free space (in bytes) on the filesystem
 // containing filename.  filename must exist.  Return -1 on error.
 size_t FreeSpaceForFile(const char* filename) {
@@ -226,27 +500,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 +563,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 +577,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 +600,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 +651,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,15 +727,37 @@
     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");
-  FILE* output = fopen(outname, "wb");
-  if (output == NULL) {
-    fprintf(stderr, "failed to patch file %s: %s\n",
-            source_filename, strerror(errno));
-    return 1;
+  char* outname = NULL;
+  FILE* output = NULL;
+  MemorySinkInfo msi;
+  SinkFn sink = NULL;
+  void* token = NULL;
+  if (strncmp(target_filename, "MTD:", 4) == 0) {
+    // We store the decoded output in memory.
+    msi.buffer = malloc(target_size);
+    if (msi.buffer == NULL) {
+      fprintf(stderr, "failed to alloc %ld bytes for output\n",
+              (long)target_size);
+      return 1;
+    }
+    msi.pos = 0;
+    msi.size = target_size;
+    sink = MemorySink;
+    token = &msi;
+  } else {
+    // We write the decoded output to "<tgt-file>.patch".
+    outname = (char*)malloc(strlen(target_filename) + 10);
+    strcpy(outname, target_filename);
+    strcat(outname, ".patch");
+
+    output = fopen(outname, "wb");
+    if (output == NULL) {
+      fprintf(stderr, "failed to open output file %s: %s\n",
+              outname, strerror(errno));
+      return 1;
+    }
+    sink = FileSink;
+    token = output;
   }
 
 #define MAX_HEADER_LENGTH 8
@@ -410,19 +784,30 @@
   } 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, sink, token, &ctx);
     if (result != 0) {
       fprintf(stderr, "ApplyBSDiffPatch failed\n");
       return result;
     }
+  } else if (header_bytes_read >= 8 &&
+             memcmp(header, "IMGDIFF", 7) == 0 &&
+             (header[7] == '1' || header[7] == '2')) {
+    int result = ApplyImagePatch(source_to_use->data, source_to_use->size,
+                                 patch_filename, sink, token, &ctx);
+    if (result != 0) {
+      fprintf(stderr, "ApplyImagePatch failed\n");
+      return result;
+    }
   } else {
-    fprintf(stderr, "Unknown patch file format");
+    fprintf(stderr, "Unknown patch file format\n");
     return 1;
   }
 
-  fflush(output);
-  fsync(fileno(output));
-  fclose(output);
+  if (output != NULL) {
+    fflush(output);
+    fsync(fileno(output));
+    fclose(output);
+  }
 
   const uint8_t* current_target_sha1 = SHA_final(&ctx);
   if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
@@ -430,22 +815,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 (output == NULL) {
+    // Copy the temp file to the MTD partition.
+    if (WriteToMTDPartition(msi.buffer, msi.pos, target_filename) != 0) {
+      fprintf(stderr, "write of patched data to %s failed\n", target_filename);
+      return 1;
+    }
+    free(msi.buffer);
+  } 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..3701087 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,14 +39,25 @@
 // and use it as the source instead.
 #define CACHE_TEMP_SOURCE "/cache/saved.file"
 
+typedef size_t (*SinkFn)(unsigned char*, size_t, void*);
+
 // 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,
-                     FILE* output, SHA_CTX* ctx);
+                     const char* patch_filename, ssize_t offset,
+                     SinkFn sink, void* token, 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,
+                    SinkFn sink, void* token, SHA_CTX* ctx);
 
 // freecache.c
 int MakeFreeSpaceOnCache(size_t bytes_needed);
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..d5cd617 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,9 +81,33 @@
   return y;
 }
 
+
 int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
-                     const char* patch_filename,
-                     FILE* output, SHA_CTX* ctx) {
+                     const char* patch_filename, ssize_t patch_offset,
+                     SinkFn sink, void* token, SHA_CTX* ctx) {
+
+  unsigned char* new_data;
+  ssize_t new_size;
+  if (ApplyBSDiffPatchMem(old_data, old_size, patch_filename, patch_offset,
+                          &new_data, &new_size) != 0) {
+    return -1;
+  }
+
+  if (sink(new_data, new_size, token) < new_size) {
+    fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno));
+    return 1;
+  }
+  if (ctx) {
+    SHA_update(ctx, new_data, new_size);
+  }
+  free(new_data);
+
+  return 0;
+}
+
+int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
+                        const char* patch_filename, ssize_t patch_offset,
+                        unsigned char** new_data, ssize_t* new_size) {
 
   FILE* f;
   if ((f = fopen(patch_filename, "rb")) == NULL) {
@@ -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..51835b4
--- /dev/null
+++ b/tools/applypatch/imgdiff.c
@@ -0,0 +1,1008 @@
+/*
+ * 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, GZIP, DEFLATE, RAW}]
+ *        if chunk type == CHUNK_NORMAL:
+ *           source start         (8)
+ *           source len           (8)
+ *           bsdiff patch offset  (8)   [from start of patch file]
+ *        if chunk type == CHUNK_GZIP:      (version 1 only)
+ *           source start         (8)
+ *           source len           (8)
+ *           bsdiff patch offset  (8)   [from start of patch file]
+ *           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)
+ *        if chunk type == CHUNK_DEFLATE:   (version 2 only)
+ *           source start         (8)
+ *           source len           (8)
+ *           bsdiff patch offset  (8)   [from start of patch file]
+ *           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)
+ *        if chunk type == RAW:             (version 2 only)
+ *           target len           (4)
+ *           data                 (target len)
+ *
+ * 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"
+#include "utils.h"
+
+typedef struct {
+  int type;             // CHUNK_NORMAL, CHUNK_DEFLATE
+  size_t start;         // offset of chunk in original image file
+
+  size_t len;
+  unsigned char* data;  // data to be patched (uncompressed, for deflate chunks)
+
+  size_t source_start;
+  size_t source_len;
+
+  // --- for CHUNK_DEFLATE chunks only: ---
+
+  // original (compressed) deflate data
+  size_t deflate_len;
+  unsigned char* deflate_data;
+
+  char* filename;       // used for zip entries
+
+  // deflate encoder parameters
+  int level, method, windowBits, memLevel, strategy;
+
+  size_t source_uncompressed_len;
+} ImageChunk;
+
+typedef struct {
+  int data_offset;
+  int deflate_len;
+  int uncomp_len;
+  char* filename;
+} ZipFileEntry;
+
+static int fileentry_compare(const void* a, const void* b) {
+  int ao = ((ZipFileEntry*)a)->data_offset;
+  int bo = ((ZipFileEntry*)b)->data_offset;
+  if (ao < bo) {
+    return -1;
+  } else if (ao > bo) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+unsigned char* ReadZip(const char* filename,
+                       int* num_chunks, ImageChunk** chunks,
+                       int include_pseudo_chunk) {
+  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);
+  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);
+
+  // look for the end-of-central-directory record.
+
+  int i;
+  for (i = st.st_size-20; i >= 0 && i > st.st_size - 65600; --i) {
+    if (img[i] == 0x50 && img[i+1] == 0x4b &&
+        img[i+2] == 0x05 && img[i+3] == 0x06) {
+      break;
+    }
+  }
+  // double-check: this archive consists of a single "disk"
+  if (!(img[i+4] == 0 && img[i+5] == 0 && img[i+6] == 0 && img[i+7] == 0)) {
+    fprintf(stderr, "can't process multi-disk archive\n");
+    return NULL;
+  }
+
+  int cdcount = Read2(img+i+8);
+  int cdoffset = Read4(img+i+16);
+
+  ZipFileEntry* temp_entries = malloc(cdcount * sizeof(ZipFileEntry));
+  int entrycount = 0;
+
+  unsigned char* cd = img+cdoffset;
+  for (i = 0; i < cdcount; ++i) {
+    if (!(cd[0] == 0x50 && cd[1] == 0x4b && cd[2] == 0x01 && cd[3] == 0x02)) {
+      fprintf(stderr, "bad central directory entry %d\n", i);
+      return NULL;
+    }
+
+    int clen = Read4(cd+20);   // compressed len
+    int ulen = Read4(cd+24);   // uncompressed len
+    int nlen = Read2(cd+28);   // filename len
+    int xlen = Read2(cd+30);   // extra field len
+    int mlen = Read2(cd+32);   // file comment len
+    int hoffset = Read4(cd+42);   // local header offset
+
+    char* filename = malloc(nlen+1);
+    memcpy(filename, cd+46, nlen);
+    filename[nlen] = '\0';
+
+    int method = Read2(cd+10);
+
+    cd += 46 + nlen + xlen + mlen;
+
+    if (method != 8) {  // 8 == deflate
+      free(filename);
+      continue;
+    }
+
+    unsigned char* lh = img + hoffset;
+
+    if (!(lh[0] == 0x50 && lh[1] == 0x4b && lh[2] == 0x03 && lh[3] == 0x04)) {
+      fprintf(stderr, "bad local file header entry %d\n", i);
+      return NULL;
+    }
+
+    if (Read2(lh+26) != nlen || memcmp(lh+30, filename, nlen) != 0) {
+      fprintf(stderr, "central dir filename doesn't match local header\n");
+      return NULL;
+    }
+
+    xlen = Read2(lh+28);   // extra field len; might be different from CD entry?
+
+    temp_entries[entrycount].data_offset = hoffset+30+nlen+xlen;
+    temp_entries[entrycount].deflate_len = clen;
+    temp_entries[entrycount].uncomp_len = ulen;
+    temp_entries[entrycount].filename = filename;
+    ++entrycount;
+  }
+
+  qsort(temp_entries, entrycount, sizeof(ZipFileEntry), fileentry_compare);
+
+#if 0
+  printf("found %d deflated entries\n", entrycount);
+  for (i = 0; i < entrycount; ++i) {
+    printf("off %10d  len %10d unlen %10d   %p %s\n",
+           temp_entries[i].data_offset,
+           temp_entries[i].deflate_len,
+           temp_entries[i].uncomp_len,
+           temp_entries[i].filename,
+           temp_entries[i].filename);
+  }
+#endif
+
+  *num_chunks = 0;
+  *chunks = malloc((entrycount*2+2) * sizeof(ImageChunk));
+  ImageChunk* curr = *chunks;
+
+  if (include_pseudo_chunk) {
+    curr->type = CHUNK_NORMAL;
+    curr->start = 0;
+    curr->len = st.st_size;
+    curr->data = img;
+    curr->filename = NULL;
+    ++curr;
+    ++*num_chunks;
+  }
+
+  int pos = 0;
+  int nextentry = 0;
+
+  while (pos < st.st_size) {
+    if (nextentry < entrycount && pos == temp_entries[nextentry].data_offset) {
+      curr->type = CHUNK_DEFLATE;
+      curr->start = pos;
+      curr->deflate_len = temp_entries[nextentry].deflate_len;
+      curr->deflate_data = img + pos;
+      curr->filename = temp_entries[nextentry].filename;
+
+      curr->len = temp_entries[nextentry].uncomp_len;
+      curr->data = malloc(curr->len);
+
+      z_stream strm;
+      strm.zalloc = Z_NULL;
+      strm.zfree = Z_NULL;
+      strm.opaque = Z_NULL;
+      strm.avail_in = curr->deflate_len;
+      strm.next_in = curr->deflate_data;
+
+      // -15 means we are decoding a 'raw' deflate stream; zlib will
+      // not expect zlib headers.
+      int ret = inflateInit2(&strm, -15);
+
+      strm.avail_out = curr->len;
+      strm.next_out = curr->data;
+      ret = inflate(&strm, Z_NO_FLUSH);
+      if (ret != Z_STREAM_END) {
+        fprintf(stderr, "failed to inflate \"%s\"; %d\n", curr->filename, ret);
+        return NULL;
+      }
+
+      inflateEnd(&strm);
+
+      pos += curr->deflate_len;
+      ++nextentry;
+      ++*num_chunks;
+      ++curr;
+      continue;
+    }
+
+    // use a normal chunk to take all the data up to the start of the
+    // next deflate section.
+
+    curr->type = CHUNK_NORMAL;
+    curr->start = pos;
+    if (nextentry < entrycount) {
+      curr->len = temp_entries[nextentry].data_offset - pos;
+    } else {
+      curr->len = st.st_size - pos;
+    }
+    curr->data = img + pos;
+    curr->filename = NULL;
+    pos += curr->len;
+
+    ++*num_chunks;
+    ++curr;
+  }
+
+  free(temp_entries);
+  return img;
+}
+
+/*
+ * 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;
+
+    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.
+
+      *num_chunks += 3;
+      *chunks = realloc(*chunks, *num_chunks * sizeof(ImageChunk));
+      ImageChunk* curr = *chunks + (*num_chunks-3);
+
+      // create a normal chunk for the header.
+      curr->start = pos;
+      curr->type = CHUNK_NORMAL;
+      curr->len = GZIP_HEADER_LEN;
+      curr->data = p;
+
+      pos += curr->len;
+      p += curr->len;
+      ++curr;
+
+      curr->type = CHUNK_DEFLATE;
+      curr->filename = NULL;
+
+      // 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->start = pos;
+      curr->deflate_data = p;
+
+      z_stream strm;
+      strm.zalloc = Z_NULL;
+      strm.zfree = Z_NULL;
+      strm.opaque = Z_NULL;
+      strm.avail_in = st.st_size - pos;
+      strm.next_in = p;
+
+      // -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->deflate_len = st.st_size - strm.avail_in - pos;
+      inflateEnd(&strm);
+      pos += curr->deflate_len;
+      p += curr->deflate_len;
+      ++curr;
+
+      // create a normal chunk for the footer
+
+      curr->type = CHUNK_NORMAL;
+      curr->start = pos;
+      curr->len = GZIP_FOOTER_LEN;
+      curr->data = img+pos;
+
+      pos += curr->len;
+      p += curr->len;
+      ++curr;
+
+      // 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 = Read4(p-4);
+      if (footer_size != curr[-2].len) {
+        fprintf(stderr, "Error: footer size %d != decompressed size %d\n",
+                footer_size, curr[-2].len);
+        free(img);
+        return NULL;
+      }
+    } else {
+      // 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;
+
+      // '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 = 0;
+
+#if 0
+  fprintf(stderr, "trying %d %d %d %d %d\n",
+          chunk->level, chunk->method, chunk->windowBits,
+          chunk->memLevel, chunk->strategy);
+#endif
+
+  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->deflate_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 != chunk->deflate_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 ReconstructDeflateChunk(ImageChunk* chunk) {
+  if (chunk->type != CHUNK_DEFLATE) {
+    fprintf(stderr, "attempt to reconstruct non-deflate 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;
+}
+
+/*
+ * 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) {
+  if (tgt->type == CHUNK_NORMAL) {
+    if (tgt->len <= 160) {
+      tgt->type = CHUNK_RAW;
+      *size = tgt->len;
+      return tgt->data;
+    }
+  }
+
+  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);
+
+  if (tgt->type == CHUNK_NORMAL && tgt->len <= st.st_size) {
+    unlink(stemp);
+    unlink(ttemp);
+    unlink(ptemp);
+
+    tgt->type = CHUNK_RAW;
+    *size = tgt->len;
+    return tgt->data;
+  }
+
+  *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);
+
+  tgt->source_start = src->start;
+  switch (tgt->type) {
+    case CHUNK_NORMAL:
+      tgt->source_len = src->len;
+      break;
+    case CHUNK_DEFLATE:
+      tgt->source_len = src->deflate_len;
+      tgt->source_uncompressed_len = src->len;
+      break;
+  }
+
+  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 ChangeDeflateChunkToNormal(ImageChunk* ch) {
+  if (ch->type != CHUNK_DEFLATE) return;
+  ch->type = CHUNK_NORMAL;
+  free(ch->data);
+  ch->data = ch->deflate_data;
+  ch->len = ch->deflate_len;
+}
+
+/*
+ * Return true if the data in the chunk is identical (including the
+ * compressed representation, for gzip chunks).
+ */
+int AreChunksEqual(ImageChunk* a, ImageChunk* b) {
+    if (a->type != b->type) return 0;
+
+    switch (a->type) {
+        case CHUNK_NORMAL:
+            return a->len == b->len && memcmp(a->data, b->data, a->len) == 0;
+
+        case CHUNK_DEFLATE:
+            return a->deflate_len == b->deflate_len &&
+                memcmp(a->deflate_data, b->deflate_data, a->deflate_len) == 0;
+
+        default:
+            fprintf(stderr, "unknown chunk type %d\n", a->type);
+            return 0;
+    }
+}
+
+/*
+ * Look for runs of adjacent normal chunks and compress them down into
+ * a single chunk.  (Such runs can be produced when deflate chunks are
+ * changed to normal chunks.)
+ */
+void MergeAdjacentNormalChunks(ImageChunk* chunks, int* num_chunks) {
+  int out = 0;
+  int in_start = 0, in_end;
+  while (in_start < *num_chunks) {
+    if (chunks[in_start].type != CHUNK_NORMAL) {
+      in_end = in_start+1;
+    } else {
+      // in_start is a normal chunk.  Look for a run of normal chunks
+      // that constitute a solid block of data (ie, each chunk begins
+      // where the previous one ended).
+      for (in_end = in_start+1;
+           in_end < *num_chunks && chunks[in_end].type == CHUNK_NORMAL &&
+             (chunks[in_end].start ==
+              chunks[in_end-1].start + chunks[in_end-1].len &&
+              chunks[in_end].data ==
+              chunks[in_end-1].data + chunks[in_end-1].len);
+           ++in_end);
+    }
+
+    if (in_end == in_start+1) {
+#if 0
+      printf("chunk %d is now %d\n", in_start, out);
+#endif
+      if (out != in_start) {
+        memcpy(chunks+out, chunks+in_start, sizeof(ImageChunk));
+      }
+    } else {
+#if 0
+      printf("collapse normal chunks %d-%d into %d\n", in_start, in_end-1, out);
+#endif
+
+      // Merge chunks [in_start, in_end-1] into one chunk.  Since the
+      // data member of each chunk is just a pointer into an in-memory
+      // copy of the file, this can be done without recopying (the
+      // output chunk has the first chunk's start location and data
+      // pointer, and length equal to the sum of the input chunk
+      // lengths).
+      chunks[out].type = CHUNK_NORMAL;
+      chunks[out].start = chunks[in_start].start;
+      chunks[out].data = chunks[in_start].data;
+      chunks[out].len = chunks[in_end-1].len +
+        (chunks[in_end-1].start - chunks[in_start].start);
+    }
+
+    ++out;
+    in_start = in_end;
+  }
+  *num_chunks = out;
+}
+
+ImageChunk* FindChunkByName(const char* name,
+                            ImageChunk* chunks, int num_chunks) {
+  int i;
+  for (i = 0; i < num_chunks; ++i) {
+    if (chunks[i].type == CHUNK_DEFLATE && chunks[i].filename &&
+        strcmp(name, chunks[i].filename) == 0) {
+      return chunks+i;
+    }
+  }
+  return NULL;
+}
+
+int main(int argc, char** argv) {
+  if (argc != 4 && argc != 5) {
+    usage:
+    fprintf(stderr, "usage: %s [-z] <src-img> <tgt-img> <patch-file>\n",
+            argv[0]);
+    return 2;
+  }
+
+  int zip_mode = 0;
+
+  if (strcmp(argv[1], "-z") == 0) {
+    zip_mode = 1;
+    --argc;
+    ++argv;
+  }
+
+
+  int num_src_chunks;
+  ImageChunk* src_chunks;
+  int num_tgt_chunks;
+  ImageChunk* tgt_chunks;
+  int i;
+
+  if (zip_mode) {
+    if (ReadZip(argv[1], &num_src_chunks, &src_chunks, 1) == NULL) {
+      fprintf(stderr, "failed to break apart source zip file\n");
+      return 1;
+    }
+    if (ReadZip(argv[2], &num_tgt_chunks, &tgt_chunks, 0) == NULL) {
+      fprintf(stderr, "failed to break apart target zip file\n");
+      return 1;
+    }
+  } else {
+    if (ReadImage(argv[1], &num_src_chunks, &src_chunks) == NULL) {
+      fprintf(stderr, "failed to break apart source image\n");
+      return 1;
+    }
+    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 deflate 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;
+    }
+    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;
+      }
+    }
+  }
+
+  for (i = 0; i < num_tgt_chunks; ++i) {
+    if (tgt_chunks[i].type == CHUNK_DEFLATE) {
+      // 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-deflated chunk.
+      if (ReconstructDeflateChunk(tgt_chunks+i) < 0) {
+        printf("failed to reconstruct target deflate chunk %d [%s]; "
+               "treating as normal\n", i, tgt_chunks[i].filename);
+        ChangeDeflateChunkToNormal(tgt_chunks+i);
+        if (zip_mode) {
+          ImageChunk* src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks);
+          if (src) {
+            ChangeDeflateChunkToNormal(src);
+          }
+        } else {
+          ChangeDeflateChunkToNormal(src_chunks+i);
+        }
+        continue;
+      }
+
+      // If two deflate chunks are identical (eg, the kernel has not
+      // changed between two builds), treat them as normal chunks.
+      // This makes applypatch much faster -- it can apply a trivial
+      // patch to the compressed data, rather than uncompressing and
+      // recompressing to apply the trivial patch to the uncompressed
+      // data.
+      ImageChunk* src;
+      if (zip_mode) {
+        src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks);
+      } else {
+        src = src_chunks+i;
+      }
+
+      if (src == NULL || AreChunksEqual(tgt_chunks+i, src)) {
+        ChangeDeflateChunkToNormal(tgt_chunks+i);
+        if (src) {
+          ChangeDeflateChunkToNormal(src);
+        }
+      }
+    }
+  }
+
+  // Merging neighboring normal chunks.
+  if (zip_mode) {
+    // For zips, we only need to do this to the target:  deflated
+    // chunks are matched via filename, and normal chunks are patched
+    // using the entire source file as the source.
+    MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
+  } else {
+    // For images, we need to maintain the parallel structure of the
+    // chunk lists, so do the merging in both the source and target
+    // lists.
+    MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
+    MergeAdjacentNormalChunks(src_chunks, &num_src_chunks);
+    if (num_src_chunks != num_tgt_chunks) {
+      // This shouldn't happen.
+      fprintf(stderr, "merging normal chunks went awry\n");
+      return 1;
+    }
+  }
+
+  // Compute bsdiff patches for each chunk's data (the uncompressed
+  // data, in the case of deflate chunks).
+
+  printf("Construct patches for %d chunks...\n", num_tgt_chunks);
+  unsigned char** patch_data = malloc(num_tgt_chunks * sizeof(unsigned char*));
+  size_t* patch_size = malloc(num_tgt_chunks * sizeof(size_t));
+  for (i = 0; i < num_tgt_chunks; ++i) {
+    if (zip_mode) {
+      ImageChunk* src;
+      if (tgt_chunks[i].type == CHUNK_DEFLATE &&
+          (src = FindChunkByName(tgt_chunks[i].filename, src_chunks,
+                                 num_src_chunks))) {
+        patch_data[i] = MakePatch(src, tgt_chunks+i, patch_size+i);
+      } else {
+        patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i);
+      }
+    } else {
+      patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i);
+    }
+    printf("patch %3d is %d bytes (of %d)\n",
+           i, patch_size[i], tgt_chunks[i].source_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_tgt_chunks; ++i) {
+    total_header_size += 4;
+    switch (tgt_chunks[i].type) {
+      case CHUNK_NORMAL:
+        total_header_size += 8*3;
+        break;
+      case CHUNK_DEFLATE:
+        total_header_size += 8*5 + 4*5;
+        break;
+      case CHUNK_RAW:
+        total_header_size += 4 + patch_size[i];
+        break;
+    }
+  }
+
+  size_t offset = total_header_size;
+
+  FILE* f = fopen(argv[3], "wb");
+
+  // Write out the headers.
+
+  fwrite("IMGDIFF2", 1, 8, f);
+  Write4(num_tgt_chunks, f);
+  for (i = 0; i < num_tgt_chunks; ++i) {
+    Write4(tgt_chunks[i].type, f);
+
+    switch (tgt_chunks[i].type) {
+      case CHUNK_NORMAL:
+        printf("chunk %3d: normal   (%10d, %10d)  %10d\n", i,
+               tgt_chunks[i].start, tgt_chunks[i].len, patch_size[i]);
+        Write8(tgt_chunks[i].source_start, f);
+        Write8(tgt_chunks[i].source_len, f);
+        Write8(offset, f);
+        offset += patch_size[i];
+        break;
+
+      case CHUNK_DEFLATE:
+        printf("chunk %3d: deflate  (%10d, %10d)  %10d  %s\n", i,
+               tgt_chunks[i].start, tgt_chunks[i].deflate_len, patch_size[i],
+               tgt_chunks[i].filename);
+        Write8(tgt_chunks[i].source_start, f);
+        Write8(tgt_chunks[i].source_len, f);
+        Write8(offset, f);
+        Write8(tgt_chunks[i].source_uncompressed_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);
+        offset += patch_size[i];
+        break;
+
+      case CHUNK_RAW:
+        printf("chunk %3d: raw      (%10d, %10d)\n", i,
+               tgt_chunks[i].start, tgt_chunks[i].len);
+        Write4(patch_size[i], f);
+        fwrite(patch_data[i], 1, patch_size[i], f);
+        break;
+    }
+  }
+
+  // Append each chunk's bsdiff patch, in order.
+
+  for (i = 0; i < num_tgt_chunks; ++i) {
+    if (tgt_chunks[i].type != CHUNK_RAW) {
+      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..f2069b4
--- /dev/null
+++ b/tools/applypatch/imgdiff.h
@@ -0,0 +1,30 @@
+/*
+ * 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   // version 1 only
+#define CHUNK_DEFLATE  2   // version 2 only
+#define CHUNK_RAW      3   // version 2 only
+
+// 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/imgdiff_test.sh b/tools/applypatch/imgdiff_test.sh
new file mode 100755
index 0000000..dcdb922
--- /dev/null
+++ b/tools/applypatch/imgdiff_test.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+#
+# A script for testing imgdiff/applypatch.  It takes two full OTA
+# packages as arguments.  It generates (on the host) patches for all
+# the zip/jar/apk files they have in common, as well as boot and
+# recovery images.  It then applies the patches on the device (or
+# emulator) and checks that the resulting file is correct.
+
+EMULATOR_PORT=5580
+
+# set to 0 to use a device instead
+USE_EMULATOR=0
+
+# where on the device to do all the patching.
+WORK_DIR=/data/local/tmp
+
+START_OTA_PACKAGE=$1
+END_OTA_PACKAGE=$2
+
+# ------------------------
+
+tmpdir=$(mktemp -d)
+
+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
+
+echo "waiting to connect to device"
+$ADB wait-for-device
+
+# run a command on the device; exit with the exit status of the device
+# command.
+run_command() {
+  $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
+}
+
+testname() {
+  echo
+  echo "$1"...
+  testname="$1"
+}
+
+fail() {
+  echo
+  echo FAIL: $testname
+  echo
+  [ "$open_pid" == "" ] || kill $open_pid
+  [ "$pid_emulator" == "" ] || kill $pid_emulator
+  exit 1
+}
+
+sha1() {
+  sha1sum $1 | awk '{print $1}'
+}
+
+size() {
+  stat -c %s $1 | tr -d '\n'
+}
+
+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/applypatch
+  run_command rm $WORK_DIR/source
+  run_command rm $WORK_DIR/target
+  run_command rm $WORK_DIR/patch
+
+  [ "$pid_emulator" == "" ] || kill $pid_emulator
+
+  rm -rf $tmpdir
+}
+
+$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
+
+patch_and_apply() {
+  local fn=$1
+  shift
+
+  unzip -p $START_OTA_PACKAGE $fn > $tmpdir/source
+  unzip -p $END_OTA_PACKAGE $fn > $tmpdir/target
+  imgdiff "$@" $tmpdir/source $tmpdir/target $tmpdir/patch
+  bsdiff $tmpdir/source $tmpdir/target $tmpdir/patch.bs
+  echo "patch for $fn is $(size $tmpdir/patch) [of $(size $tmpdir/target)] ($(size $tmpdir/patch.bs) with bsdiff)"
+  echo "$fn $(size $tmpdir/patch) of $(size $tmpdir/target) bsdiff $(size $tmpdir/patch.bs)" >> /tmp/stats.txt
+  $ADB push $tmpdir/source $WORK_DIR/source || fail "source push failed"
+  run_command rm /data/local/tmp/target
+  $ADB push $tmpdir/patch $WORK_DIR/patch || fail "patch push failed"
+  run_command /data/local/tmp/applypatch /data/local/tmp/source \
+    /data/local/tmp/target $(sha1 $tmpdir/target) $(size $tmpdir/target) \
+    $(sha1 $tmpdir/source):/data/local/tmp/patch \
+    || fail "applypatch of $fn failed"
+  $ADB pull /data/local/tmp/target $tmpdir/result
+  diff -q $tmpdir/target $tmpdir/result || fail "patch output not correct!"
+}
+
+# --------------- basic execution ----------------------
+
+for i in $((zipinfo -1 $START_OTA_PACKAGE; zipinfo -1 $END_OTA_PACKAGE) | \
+           sort | uniq -d | egrep -e '[.](apk|jar|zip)$'); do
+  patch_and_apply $i -z
+done
+patch_and_apply boot.img
+patch_and_apply system/recovery.img
+
+
+# --------------- cleanup ----------------------
+
+cleanup
+
+echo
+echo PASS
+echo
+
diff --git a/tools/applypatch/imgpatch.c b/tools/applypatch/imgpatch.c
new file mode 100644
index 0000000..74b041f
--- /dev/null
+++ b/tools/applypatch/imgpatch.c
@@ -0,0 +1,364 @@
+/*
+ * 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"
+#include "utils.h"
+
+/*
+ * 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,
+                    SinkFn sink, void* token, 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;
+  }
+
+  // IMGDIFF1 uses CHUNK_NORMAL and CHUNK_GZIP.
+  // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
+  if (memcmp(header, "IMGDIFF", 7) != 0 ||
+      (header[7] != '1' && header[7] != '2')) {
+    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 4 bytes.
+    unsigned char chunk[4];
+    if (fread(chunk, 1, 4, f) != 4) {
+      fprintf(stderr, "failed to read chunk %d record\n", i);
+      return -1;
+    }
+
+    int type = Read4(chunk);
+
+    if (type == CHUNK_NORMAL) {
+      unsigned char normal_header[24];
+      if (fread(normal_header, 1, 24, f) != 24) {
+        fprintf(stderr, "failed to read chunk %d normal header data\n", i);
+        return -1;
+      }
+
+      size_t src_start = Read8(normal_header);
+      size_t src_len = Read8(normal_header+8);
+      size_t patch_offset = Read8(normal_header+16);
+
+      fprintf(stderr, "CHUNK %d:  normal   patch offset %d\n", i, patch_offset);
+
+      ApplyBSDiffPatch(old_data + src_start, src_len,
+                       patch_filename, patch_offset,
+                       sink, token, ctx);
+    } else if (type == CHUNK_GZIP) {
+      // This branch is basically a duplicate of the CHUNK_DEFLATE
+      // branch, with a bit of extra processing for the gzip header
+      // and footer.  I've avoided factoring the common code out since
+      // this branch will just be deleted when we drop support for
+      // IMGDIFF1.
+
+      // gzip chunks have an additional 64 + gzip_header_len + 8 bytes
+      // in their chunk header.
+      unsigned char* gzip = malloc(64);
+      if (fread(gzip, 1, 64, f) != 64) {
+        fprintf(stderr, "failed to read chunk %d initial gzip header data\n",
+                i);
+        return -1;
+      }
+      size_t gzip_header_len = Read4(gzip+60);
+      gzip = realloc(gzip, 64 + gzip_header_len + 8);
+      if (fread(gzip+64, 1, gzip_header_len+8, f) != gzip_header_len+8) {
+        fprintf(stderr, "failed to read chunk %d remaining gzip header data\n",
+                i);
+        return -1;
+      }
+
+      size_t src_start = Read8(gzip);
+      size_t src_len = Read8(gzip+8);
+      size_t patch_offset = Read8(gzip+16);
+
+      size_t expanded_len = Read8(gzip+24);
+      size_t target_len = Read8(gzip+32);
+      int gz_level = Read4(gzip+40);
+      int gz_method = Read4(gzip+44);
+      int gz_windowBits = Read4(gzip+48);
+      int gz_memLevel = Read4(gzip+52);
+      int gz_strategy = Read4(gzip+56);
+
+      fprintf(stderr, "CHUNK %d:  gzip     patch offset %d\n", i, patch_offset);
+
+      // 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.
+      sink(gzip+64, gzip_header_len, token);
+      SHA_update(ctx, gzip+64, 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 (sink(temp_data, have, token) != 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.
+      sink(gzip+64+gzip_header_len, 8, token);
+      SHA_update(ctx, gzip+64+gzip_header_len, 8);
+
+      free(temp_data);
+      free(uncompressed_target_data);
+      free(gzip);
+    } else if (type == CHUNK_RAW) {
+      unsigned char raw_header[4];
+      if (fread(raw_header, 1, 4, f) != 4) {
+        fprintf(stderr, "failed to read chunk %d raw header data\n", i);
+        return -1;
+      }
+
+      size_t data_len = Read4(raw_header);
+
+      fprintf(stderr, "CHUNK %d:  raw      data %d\n", i, data_len);
+
+      unsigned char* temp = malloc(data_len);
+      if (fread(temp, 1, data_len, f) != data_len) {
+          fprintf(stderr, "failed to read chunk %d raw data\n", i);
+          return -1;
+      }
+      SHA_update(ctx, temp, data_len);
+      if (sink(temp, data_len, token) != data_len) {
+          fprintf(stderr, "failed to write chunk %d raw data\n", i);
+          return -1;
+      }
+    } else if (type == CHUNK_DEFLATE) {
+      // deflate chunks have an additional 60 bytes in their chunk header.
+      unsigned char deflate_header[60];
+      if (fread(deflate_header, 1, 60, f) != 60) {
+        fprintf(stderr, "failed to read chunk %d deflate header data\n", i);
+        return -1;
+      }
+
+      size_t src_start = Read8(deflate_header);
+      size_t src_len = Read8(deflate_header+8);
+      size_t patch_offset = Read8(deflate_header+16);
+      size_t expanded_len = Read8(deflate_header+24);
+      size_t target_len = Read8(deflate_header+32);
+      int level = Read4(deflate_header+40);
+      int method = Read4(deflate_header+44);
+      int windowBits = Read4(deflate_header+48);
+      int memLevel = Read4(deflate_header+52);
+      int strategy = Read4(deflate_header+56);
+
+      fprintf(stderr, "CHUNK %d:  deflate  patch offset %d\n", i, patch_offset);
+
+      // 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;
+      strm.next_in = (unsigned char*)(old_data + src_start);
+      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.
+
+      // 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, level, method, windowBits, memLevel, 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 (sink(temp_data, have, token) != 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);
+
+      free(temp_data);
+      free(uncompressed_target_data);
+    } 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/applypatch/utils.c b/tools/applypatch/utils.c
new file mode 100644
index 0000000..912229b
--- /dev/null
+++ b/tools/applypatch/utils.c
@@ -0,0 +1,62 @@
+/*
+ * 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>
+
+#include "utils.h"
+
+/** 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);
+}
+
+int Read2(unsigned char* p) {
+  return (int)(((unsigned int)p[1] << 8) |
+               (unsigned int)p[0]);
+}
+
+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]);
+}
diff --git a/tools/applypatch/utils.h b/tools/applypatch/utils.h
new file mode 100644
index 0000000..d6d6f1d
--- /dev/null
+++ b/tools/applypatch/utils.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef _BUILD_TOOLS_APPLYPATCH_UTILS_H
+#define _BUILD_TOOLS_APPLYPATCH_UTILS_H
+
+#include <stdio.h>
+
+// Read and write little-endian values of various sizes.
+
+void Write4(int value, FILE* f);
+void Write8(long long value, FILE* f);
+int Read2(unsigned char* p);
+int Read4(unsigned char* p);
+long long Read8(unsigned char* p);
+
+#endif //  _BUILD_TOOLS_APPLYPATCH_UTILS_H
diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh
index 4e99bf5..5c738a2 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,6 +20,7 @@
 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"
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/Android.mk b/tools/droiddoc/src/Android.mk
index bf404b7..30270b5 100644
--- a/tools/droiddoc/src/Android.mk
+++ b/tools/droiddoc/src/Android.mk
@@ -47,6 +47,7 @@
 	SampleTagInfo.java \
     Scoped.java \
 	SeeTagInfo.java \
+	SinceTagger.java \
 	Sorter.java \
 	SourcePositionInfo.java \
     Stubs.java \
@@ -57,6 +58,7 @@
 	TypeInfo.java
 
 LOCAL_JAVA_LIBRARIES := \
+	apicheck \
 	clearsilver
 
 LOCAL_CLASSPATH := \
diff --git a/tools/droiddoc/src/ClassInfo.java b/tools/droiddoc/src/ClassInfo.java
index f397a83..5c61941 100644
--- a/tools/droiddoc/src/ClassInfo.java
+++ b/tools/droiddoc/src/ClassInfo.java
@@ -863,6 +863,7 @@
         data.setValue(base + ".kind", this.kind());
         TagInfo.makeHDF(data, base + ".shortDescr", this.firstSentenceTags());
         TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
+        data.setValue(base + ".since", getSince());
     }
 
     /**
@@ -907,6 +908,7 @@
         if (kind != null) {
             data.setValue("class.kind", kind);
         }
+        data.setValue("class.since", getSince());
 
         // the containing package -- note that this can be passed to type_link,
         // but it also contains the list of all of the packages
diff --git a/tools/droiddoc/src/ClearPage.java b/tools/droiddoc/src/ClearPage.java
index 2a8fced..184c8b8 100644
--- a/tools/droiddoc/src/ClearPage.java
+++ b/tools/droiddoc/src/ClearPage.java
@@ -126,7 +126,7 @@
         OutputStreamWriter stream = null;
         try {
             stream = new OutputStreamWriter(
-                            new FileOutputStream(file));
+                            new FileOutputStream(file), "UTF-8");
             String rendered = cs.render();
             stream.write(rendered, 0, rendered.length());
         }
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..9901330 100644
--- a/tools/droiddoc/src/DocFile.java
+++ b/tools/droiddoc/src/DocFile.java
@@ -33,7 +33,8 @@
         try {
             File f = new File(filename);
             int length = (int)f.length();
-            FileReader reader = new FileReader(f);
+            FileInputStream is = new FileInputStream(f);
+            InputStreamReader reader = new InputStreamReader(is, "UTF-8");
             char[] buf = new char[length];
             int index = 0;
             int amt;
@@ -115,23 +116,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) || (outfile.indexOf("features.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/src/DocInfo.java b/tools/droiddoc/src/DocInfo.java
index 2530dc2..3abb367 100644
--- a/tools/droiddoc/src/DocInfo.java
+++ b/tools/droiddoc/src/DocInfo.java
@@ -51,8 +51,17 @@
 
     public abstract ContainerInfo parent();
 
+    public void setSince(String since) {
+        mSince = since;
+    }
+
+    public String getSince() {
+        return mSince;
+    }
+
     private String mRawCommentText;
     Comment mComment;
     SourcePositionInfo mPosition;
+    private String mSince;
 }
 
diff --git a/tools/droiddoc/src/DroidDoc.java b/tools/droiddoc/src/DroidDoc.java
index f664c41..4ff26bc 100644
--- a/tools/droiddoc/src/DroidDoc.java
+++ b/tools/droiddoc/src/DroidDoc.java
@@ -56,6 +56,7 @@
     public static ArrayList<String[]> mHDFData = new ArrayList<String[]>();
     public static Map<Character,String> escapeChars = new HashMap<Character,String>();
     public static String title = "";
+    public static SinceTagger sinceTagger = new SinceTagger();
 
     public static boolean checkLevel(int level)
     {
@@ -186,6 +187,9 @@
                 apiXML = true;
                 apiFile = a[1];
             }
+            else if (a[0].equals("-since")) {
+                sinceTagger.addVersion(a[1], a[2]);
+            }
         }
 
         // read some prefs from the template
@@ -204,6 +208,9 @@
             TodoFile.writeTodoFile(todoFile);
         }
 
+        // Apply @since tags from the XML file
+        sinceTagger.tagAll(Converter.rootClasses());
+
         // HTML Pages
         if (ClearPage.htmlDir != null) {
             writeHTMLPages();
@@ -244,7 +251,7 @@
         if (stubsDir != null) {
             Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
         }
-        
+
         if (sdkValuePath != null) {
             writeSdkValues(sdkValuePath);
         }
@@ -394,9 +401,12 @@
         if (option.equals("-apixml")) {
             return 2;
         }
+        if (option.equals("-since")) {
+            return 3;
+        }
         return 0;
     }
-    
+
     public static boolean validOptions(String[][] options, DocErrorReporter r)
     {
         for (String[] a: options) {
@@ -502,11 +512,13 @@
             data.setValue("reference", "true");
             data.setValue("docs.packages." + i + ".name", s);
             data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
+            data.setValue("docs.packages." + i + ".since", pkg.getSince());
             TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
                                                pkg.firstSentenceTags());
             i++;
         }
 
+        sinceTagger.writeVersionNames(data);
         return data;
     }
 
@@ -762,6 +774,7 @@
         String name = pkg.name();
 
         data.setValue("package.name", name);
+        data.setValue("package.since", pkg.getSince());
         data.setValue("package.descr", "...description...");
 
         makeClassListHDF(data, "package.interfaces", 
diff --git a/tools/droiddoc/src/Errors.java b/tools/droiddoc/src/Errors.java
index dfeac88..95439f1 100644
--- a/tools/droiddoc/src/Errors.java
+++ b/tools/droiddoc/src/Errors.java
@@ -114,6 +114,7 @@
     public static Error DEPRECATION_MISMATCH = new Error(13, WARNING);
     public static Error MISSING_COMMENT = new Error(14, WARNING);
     public static Error IO_ERROR = new Error(15, HIDDEN);
+    public static Error NO_SINCE_DATA = new Error(16, WARNING);
 
     public static Error[] ERRORS = {
             UNRESOLVED_LINK,
@@ -129,6 +130,7 @@
             HIDDEN_SUPERCLASS,
             DEPRECATED,
             IO_ERROR,
+            NO_SINCE_DATA,
         };
 
     public static boolean setErrorLevel(int code, int level) {
diff --git a/tools/droiddoc/src/FieldInfo.java b/tools/droiddoc/src/FieldInfo.java
index 536d798..1c975e4 100644
--- a/tools/droiddoc/src/FieldInfo.java
+++ b/tools/droiddoc/src/FieldInfo.java
@@ -223,6 +223,7 @@
         TagInfo.makeHDF(data, base + ".descr", inlineTags());
         TagInfo.makeHDF(data, base + ".deprecated", comment().deprecatedTags());
         TagInfo.makeHDF(data, base + ".seeAlso", comment().seeTags());
+        data.setValue(base + ".since", getSince());
         data.setValue(base + ".final", isFinal() ? "final" : "");
         data.setValue(base + ".static", isStatic() ? "static" : "");
         if (isPublic()) {
diff --git a/tools/droiddoc/src/MethodInfo.java b/tools/droiddoc/src/MethodInfo.java
index ca30665..bded88b 100644
--- a/tools/droiddoc/src/MethodInfo.java
+++ b/tools/droiddoc/src/MethodInfo.java
@@ -15,9 +15,8 @@
  */
 
 import org.clearsilver.HDF;
-import org.clearsilver.CS;
+
 import java.util.*;
-import java.io.*;
 
 public class MethodInfo extends MemberInfo
 {
@@ -357,6 +356,19 @@
         return s;
     }
 
+    /**
+     * Returns a name consistent with the {@link
+     * com.android.apicheck.MethodInfo#getHashableName()}.
+     */
+    public String getHashableName() {
+        StringBuilder result = new StringBuilder();
+        result.append(name());
+        for (ParameterInfo pInfo : mParameters) {
+            result.append(":").append(pInfo.type().fullName());
+        }
+        return result.toString();
+    }
+
     private boolean inList(ClassInfo item, ThrowsTagInfo[] list)
     {
         int len = list.length;
@@ -545,6 +557,7 @@
         TagInfo.makeHDF(data, base + ".descr", inlineTags());
         TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
         TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
+        data.setValue(base + ".since", getSince());
         ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
         AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
         ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
diff --git a/tools/droiddoc/src/NavTree.java b/tools/droiddoc/src/NavTree.java
index 9eef0ce..0469fdc 100644
--- a/tools/droiddoc/src/NavTree.java
+++ b/tools/droiddoc/src/NavTree.java
@@ -25,7 +25,7 @@
         for (PackageInfo pkg: DroidDoc.choosePackages()) {
             children.add(makePackageNode(pkg));
         }
-        Node node = new Node("Reference", dir + "packages.html", children);
+        Node node = new Node("Reference", dir + "packages.html", children, null);
 
         StringBuilder buf = new StringBuilder();
         if (false) {
@@ -46,7 +46,7 @@
     private static Node makePackageNode(PackageInfo pkg) {
         ArrayList<Node> children = new ArrayList();
 
-        children.add(new Node("Description", pkg.fullDescriptionHtmlPage(), null));
+        children.add(new Node("Description", pkg.fullDescriptionHtmlPage(), null, null));
 
         addClassNodes(children, "Interfaces", pkg.interfaces());
         addClassNodes(children, "Classes", pkg.ordinaryClasses());
@@ -54,7 +54,7 @@
         addClassNodes(children, "Exceptions", pkg.exceptions());
         addClassNodes(children, "Errors", pkg.errors());
 
-        return new Node(pkg.name(), pkg.htmlPage(), children);
+        return new Node(pkg.name(), pkg.htmlPage(), children, pkg.getSince());
     }
 
     private static void addClassNodes(ArrayList<Node> parent, String label, ClassInfo[] classes) {
@@ -62,12 +62,12 @@
 
         for (ClassInfo cl: classes) {
             if (cl.checkLevel()) {
-                children.add(new Node(cl.name(), cl.htmlPage(), null));
+                children.add(new Node(cl.name(), cl.htmlPage(), null, cl.getSince()));
             }
         }
 
         if (children.size() > 0) {
-            parent.add(new Node(label, null, children));
+            parent.add(new Node(label, null, children, null));
         }
     }
 
@@ -75,11 +75,13 @@
         private String mLabel;
         private String mLink;
         ArrayList<Node> mChildren;
+        private String mSince;
 
-        Node(String label, String link, ArrayList<Node> children) {
+        Node(String label, String link, ArrayList<Node> children, String since) {
             mLabel = label;
             mLink = link;
             mChildren = children;
+            mSince = since;
         }
 
         static void renderString(StringBuilder buf, String s) {
@@ -136,6 +138,8 @@
             renderString(buf, mLink);
             buf.append(", ");
             renderChildren(buf);
+            buf.append(", ");
+            renderString(buf, mSince);
             buf.append(" ]");
         }
     }
diff --git a/tools/droiddoc/src/PackageInfo.java b/tools/droiddoc/src/PackageInfo.java
index aac0def..18c636e 100644
--- a/tools/droiddoc/src/PackageInfo.java
+++ b/tools/droiddoc/src/PackageInfo.java
@@ -112,6 +112,7 @@
             data.setValue(base + ".link", htmlPage());
         }
         data.setValue(base + ".name", name());
+        data.setValue(base + ".since", getSince());
     }
 
     public void makeClassLinkListHDF(HDF data, String base)
@@ -122,6 +123,7 @@
         ClassInfo.makeLinkListHDF(data, base + ".enums", enums());
         ClassInfo.makeLinkListHDF(data, base + ".exceptions", exceptions());
         ClassInfo.makeLinkListHDF(data, base + ".errors", errors());
+        data.setValue(base + ".since", getSince());
     }
 
     public ClassInfo[] interfaces()
diff --git a/tools/droiddoc/src/SinceTagger.java b/tools/droiddoc/src/SinceTagger.java
new file mode 100644
index 0000000..fb69c04
--- /dev/null
+++ b/tools/droiddoc/src/SinceTagger.java
@@ -0,0 +1,195 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+import com.android.apicheck.*;
+
+import java.util.*;
+
+import org.clearsilver.HDF;
+
+/**
+ * Applies version information to the DroidDoc class model from apicheck XML
+ * files. Sample usage:
+ * <pre>
+ *   ClassInfo[] classInfos = ...
+ *
+ *   SinceTagger sinceTagger = new SinceTagger()
+ *   sinceTagger.addVersion("frameworks/base/api/1.xml", "Android 1.0")
+ *   sinceTagger.addVersion("frameworks/base/api/2.xml", "Android 1.5")
+ *   sinceTagger.tagAll(...);
+ * </pre>
+ */
+public class SinceTagger {
+
+    private final Map<String, String> xmlToName
+            = new LinkedHashMap<String, String>();
+
+    /**
+     * Specifies the apicheck XML file and the API version it holds. Calls to
+     * this method should be called in order from oldest version to newest.
+     */
+    public void addVersion(String file, String name) {
+        xmlToName.put(file, name);
+    }
+
+    public void tagAll(ClassInfo[] classDocs) {
+        // read through the XML files in order, applying their since information
+        // to the Javadoc models
+        for (Map.Entry<String, String> versionSpec : xmlToName.entrySet()) {
+            String xmlFile = versionSpec.getKey();
+            String versionName = versionSpec.getValue();
+            ApiInfo specApi = new ApiCheck().parseApi(xmlFile);
+
+            applyVersionsFromSpec(versionName, specApi, classDocs);
+        }
+
+        if (!xmlToName.isEmpty()) {
+            warnForMissingVersions(classDocs);
+        }
+    }
+
+    /**
+     * Writes an index of the version names to {@code data}. 
+     */
+    public void writeVersionNames(HDF data) {
+        int index = 1;
+        for (String version : xmlToName.values()) {
+            data.setValue("since." + index + ".name", version);
+            index++;
+        }
+    }
+
+    /**
+     * Applies the version information to {@code classDocs} where not already
+     * present.
+     *
+     * @param versionName the version name
+     * @param specApi the spec for this version. If a symbol is in this spec, it
+     *      was present in the named version
+     * @param classDocs the doc model to update
+     */
+    private void applyVersionsFromSpec(String versionName,
+            ApiInfo specApi, ClassInfo[] classDocs) {
+        for (ClassInfo classDoc : classDocs) {
+            com.android.apicheck.PackageInfo packageSpec
+                    = specApi.getPackages().get(classDoc.containingPackage().name());
+
+            if (packageSpec == null) {
+                continue;
+            }
+
+            com.android.apicheck.ClassInfo classSpec
+                    = packageSpec.allClasses().get(classDoc.name());
+
+            if (classSpec == null) {
+                continue;
+            }
+
+            versionPackage(versionName, classDoc.containingPackage());
+            versionClass(versionName, classDoc);
+            versionConstructors(versionName, classSpec, classDoc);
+            versionFields(versionName, classSpec, classDoc);
+            versionMethods(versionName, classSpec, classDoc);
+        }
+    }
+
+    /**
+     * Applies version information to {@code doc} where not already present.
+     */
+    private void versionPackage(String versionName, PackageInfo doc) {
+        if (doc.getSince() == null) {
+            doc.setSince(versionName);
+        }
+    }
+
+    /**
+     * Applies version information to {@code doc} where not already present.
+     */
+    private void versionClass(String versionName, ClassInfo doc) {
+        if (doc.getSince() == null) {
+            doc.setSince(versionName);
+        }
+    }
+
+    /**
+     * Applies version information from {@code spec} to {@code doc} where not
+     * already present.
+     */
+    private void versionConstructors(String versionName,
+            com.android.apicheck.ClassInfo spec, ClassInfo doc) {
+        for (MethodInfo constructor : doc.constructors()) {
+            if (constructor.getSince() == null
+                    && spec.allConstructors().containsKey(constructor.getHashableName())) {
+                constructor.setSince(versionName);
+            }
+        }
+    }
+
+    /**
+     * Applies version information from {@code spec} to {@code doc} where not
+     * already present.
+     */
+    private void versionFields(String versionName,
+            com.android.apicheck.ClassInfo spec, ClassInfo doc) {
+        for (FieldInfo field : doc.fields()) {
+            if (field.getSince() == null
+                    && spec.allFields().containsKey(field.name())) {
+                field.setSince(versionName);
+            }
+        }
+    }
+
+    /**
+     * Applies version information from {@code spec} to {@code doc} where not
+     * already present.
+     */
+    private void versionMethods(String versionName,
+            com.android.apicheck.ClassInfo spec, ClassInfo doc) {
+        for (MethodInfo method : doc.methods()) {
+            if (method.getSince() != null) {
+                continue;
+            }
+
+            for (com.android.apicheck.ClassInfo superclass : spec.hierarchy()) {
+                if (superclass.allMethods().containsKey(method.getHashableName())) {
+                    method.setSince(versionName);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Warns if any symbols are missing version information. When configured
+     * properly, this will yield zero warnings because {@code apicheck}
+     * guarantees that all symbols are present in the most recent API.
+     */
+    private void warnForMissingVersions(ClassInfo[] classDocs) {
+        for (ClassInfo claz : classDocs) {
+            if (claz.getSince() == null) {
+                Errors.error(Errors.NO_SINCE_DATA, claz.position(),
+                        "XML missing class " + claz.qualifiedName());
+            }
+            for (FieldInfo field : claz.fields()) {
+                if (field.getSince() == null) {
+                    Errors.error(Errors.NO_SINCE_DATA, field.position(),
+                            "XML missing field "
+                                    + claz.qualifiedName() + "#" + field .name());
+                }
+            }
+            for (MethodInfo constructor : claz.constructors()) {
+                if (constructor.getSince() == null) {
+                    Errors.error(Errors.NO_SINCE_DATA, constructor.position(),
+                            "XML missing constructor "
+                                    + claz.qualifiedName() + "#" + constructor.getHashableName());
+                }
+            }
+            for (MethodInfo method : claz.methods()) {
+                if (method.getSince() == null) {
+                    Errors.error(Errors.NO_SINCE_DATA, method.position(),
+                            "XML missing method "
+                                    + claz.qualifiedName() + "#" + method .getHashableName());
+                }
+            }
+        }
+    }
+}
diff --git a/tools/droiddoc/src/TypeInfo.java b/tools/droiddoc/src/TypeInfo.java
index c1119de..5196c13 100644
--- a/tools/droiddoc/src/TypeInfo.java
+++ b/tools/droiddoc/src/TypeInfo.java
@@ -159,6 +159,7 @@
         }
         else if (!isPrimitive() && cl != null && cl.isIncluded()) {
             data.setValue(base + ".link", cl.htmlPage());
+            data.setValue(base + ".since", cl.getSince());
         }
 
         if (mIsTypeVariable) {
diff --git a/tools/droiddoc/templates-pdk/customization.cs b/tools/droiddoc/templates-pdk/customization.cs
index 8d3cde6..dfebb12 100644
--- a/tools/droiddoc/templates-pdk/customization.cs
+++ b/tools/droiddoc/templates-pdk/customization.cs
@@ -1,31 +1,27 @@
 <?cs # This file defines custom definitions for the masthead (logo, searchbox, tabs, etc) and 
-left nav (toc) that gets placed on all pages. ?>
+left nav (toc) that gets placed on all pages, for the open source site?>
 
 <?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 Porting Development Kit" /></a>
+          <a href="http://source.android.com" 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
-                  elif:sdk ?> <?cs
-                  elif:home ?> <?cs
-                  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>-->
+                  if:releases ?> releases<?cs
+                  elif:guide ?> guide<?cs
+                  elif:licenses ?>licenses <?cs
+                  elif:home ?>home <?cs
+                  elif:community ?>community <?cs /if ?>">
+              <li id="home-link"><a href="<?cs var:toroot ?>index.html"><span>Home</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>-->
+                                  onClick="return loadLast('guide)'"><span>Guide</span></a></li>
+              <li id="releases-ink"><a href="<?cs var:toroot ?>releases/index.html"
+                                  onClick="return loadLast('releases)'"><span>Releases</span></a></li>
+              <li id="licenses-link"><a href="<?cs var:toroot ?>licenses/index.html"
+                                  onClick="return loadLast('licenses)'"><span>Licenses</span></a></li>
+              <li id="community-link"><a href="<?cs var:toroot ?>community/index.html"
+                                  onClick="return loadLast('community)'"><span>Community</span></a></li>
           </ul> 
       </div>
       <div id="headerRight">
@@ -35,8 +31,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 +42,37 @@
   <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>
+      addLoadEvent(function() {
+        scrollIntoView("devdoc-nav");
+        });
+    </script>
+<?cs /def ?>
+
+<?cs
+def:licenses_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="devdoc-nav"><?cs
+        include:"../../../../development/pdk/docs/licenses/licenses_toc.cs" ?>
+      </div>
+    </div> <!-- end side-nav -->
+    <script>
+      addLoadEvent(function() {
+        scrollIntoView("devdoc-nav");
+        });
+    </script>
+<?cs /def ?>
+
+<?cs
+def:releases_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="devdoc-nav"><?cs
+        include:"../../../../development/pdk/docs/releases/releases_toc.cs" ?>
       </div>
     </div> <!-- end side-nav -->
     <script>
@@ -58,12 +83,14 @@
 <?cs /def ?>
 
 <?cs 
-def:custom_left_nav() ?><?cs 
-  if:guide ?><?cs 
-    call:guide_nav() ?><?cs 
-  else ?><?cs 
-    call:default_left_nav() ?><?cs 
-  /if ?><?cs 
+def:custom_left_nav() ?><?cs
+  if:doc.type == "guide" ?><?cs
+    call:guide_nav() ?><?cs
+  elif:doc.type == "licenses" ?><?cs
+    call:licenses_nav() ?><?cs
+  elif:doc.type == "releases" ?><?cs
+    call:releases_nav() ?><?cs
+  /if ?><?cs
 /def ?>
 
 <?cs # appears at the bottom of every page ?><?cs 
@@ -71,7 +98,7 @@
   Except as noted, this content is 
   licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
   Creative Commons Attribution 2.5</a>. For details and 
-  restrictions, see the <a href="<?cs var:toroot ?>license.html">Content 
+  restrictions, see the <a href="http://developer.android.com/license.html">Content 
   License</a>.<?cs 
 /def ?>
 
@@ -79,7 +106,7 @@
 def:custom_copyright() ?>
   Except as noted, this content is licensed under <a
   href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>. 
-  For details and restrictions, see the <a href="<?cs var:toroot ?>license.html">
+  For details and restrictions, see the <a href="http://developer.android.com/license.html">
   Content License</a>.<?cs 
 /def ?>
 
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..6bdb992 100644
--- a/tools/droiddoc/templates-sdk/customization.cs
+++ b/tools/droiddoc/templates-sdk/customization.cs
@@ -1,45 +1,101 @@
 <?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>
+          <?cs if:template.showLanguageMenu ?>
+            <img src="<?cs var:toroot ?>assets/images/icon_world.jpg" alt="Language:" /> 
+            <span id="language">
+             	<select name="language" onChange="changeLangPref(this.value, true)">
+          			<option value="en">English&nbsp;&nbsp;&nbsp;</option>
+          			<option value="ja">日本語</option>
+          			<?cs # 
+      			    <option value="de">Deutsch</option> 
+          			<option value="es">Español</option>
+          			<option value="fr">Français</option>
+          			<option value="it">Italiano</option>
+          			<option value="zh-CN">中文 (简体)</option>
+          			<option value="zh-TW">中文 (繁體)</option>
+      			    ?>
+             	</select>	
+             	<script type="text/javascript">
+             	  <!--  
+                  loadLangPref();  
+             	   //-->
+             	</script>
             </span>
+          <?cs /if ?>
+          <a href="http://www.android.com">Android.com</a>
           </div><?cs 
-          call:default_search_box() ?>
+          call:default_search_box() ?><?cs 
+    	 	  if:reference ?>
+    			  <div id="api-level-toggle">
+    			    <label for="apiLevelControl"><a href="<?cs var:toroot ?>guide/appendix/api-levels.html">Filter by API Level</a>: </label>
+    			    <select id="apiLevelControl">
+    			      <!-- option elements added by buildApiLevelToggle() -->
+    			    </select>
+    			  </div>
+    	 	    <script>
+              var SINCE_DATA = [ <?cs 
+                each:since = since ?>'<?cs 
+                  var:since.name ?>'<?cs 
+                  if:!last(since) ?>, <?cs /if ?><?cs
+                /each 
+              ?> ];
+              buildApiLevelToggle();
+            </script><?cs 
+    			/if ?>
       </div><!-- headerRight -->
+      <script type="text/javascript">
+        <!--  
+        changeTabLang(getLangPref());
+        //-->
+      </script>
   </div><!-- header --><?cs 
-/def ?><?cs # custom_masthead ?>
+/def ?>
 
 <?cs 
 def:sdk_nav() ?>
@@ -66,22 +122,79 @@
     </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 call:package_link_list(docs.packages) ?>
+              </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_default_navtree("<?cs var:toroot ?>");
+        } 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 +228,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..2a897cb
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/header_tabs.cs
@@ -0,0 +1,85 @@
+<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:videos ?>videos<?cs /if ?>">
+         
+	<li id="home-link"><a href="<?cs var:toroot ?><?cs if:android.whichdoc != "online" ?>offline.html<?cs else ?>index.html<?cs /if ?>">
+	<?cs if:!sdk.redirect ?>
+		<span class="en">Home</span>
+		<span class="de">Startseite</span>
+		<span class="es"></span>
+		<span class="fr"></span>
+		<span class="it"></span>
+                <span class="ja">ホーム</span>
+		<span class="zh-CN">主页</span>
+		<span class="zh-TW">首頁</span>
+	<?cs /if ?>
+	</a></li>
+	<li id="sdk-link"><a href="<?cs var:toroot ?>sdk/<?cs var:sdk.current ?>/index.html">
+		<span class="en">SDK</span>
+	</a></li>
+	<li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html" onClick="return loadLast('guide')">
+	<?cs if:!sdk.redirect ?>
+		<span class="en">Dev Guide</span>
+		<span class="de">Handbuch</span>
+		<span class="es">Guía</span>
+		<span class="fr">Guide</span>
+		<span class="it">Guida</span>
+                <span class="ja">開発ガイド</span>
+		<span class="zh-CN">开发人员指南</span>
+		<span class="zh-TW">開發指南</span>
+	<?cs /if ?>
+	</a></li>
+	<li id="reference-link"><a href="<?cs var:toroot ?>reference/packages.html" onClick="return loadLast('reference')">
+	<?cs if:!sdk.redirect ?>
+		<span class="en">Reference</span>
+		<span class="de">Referenz</span>
+		<span class="es">Referencia</span>
+		<span class="fr">Référence</span>
+		<span class="it">Riferimento</span>
+                <span class="ja">リファレンス</span>
+		<span class="zh-CN">参考</span>
+		<span class="zh-TW">參考資料</span>
+	<?cs /if ?>
+	</a></li>
+	<li><a href="http://android-developers.blogspot.com" onClick="return requestAppendHL(this.href)">
+	<?cs if:!sdk.redirect ?>
+		<span class="en">Blog</span>
+		<span class="de"></span>
+		<span class="es"></span>
+		<span class="fr"></span>
+		<span class="it"></span>
+                <span class="ja">ブログ</span>
+		<span class="zh-CN">博客</span>
+		<span class="zh-TW">網誌</span>
+	<?cs /if ?>
+	</a></li>
+	<li id="videos-link"><a href="<?cs var:toroot ?>videos/index.html" onClick="return loadLast('videos')">
+	<?cs if:!sdk.redirect ?>
+		<span class="en">Videos</span>
+		<span class="de"></span>
+		<span class="es"></span>
+		<span class="fr"></span>
+		<span class="it"></span>
+                <span class="ja">ビデオ</span>
+		<span class="zh-CN"></span>
+		<span class="zh-TW"></span>
+	<?cs /if ?>
+	</a></li>
+	<li id="community-link"><a href="<?cs var:toroot ?>community/index.html">
+	<?cs if:!sdk.redirect ?>
+		<span class="en">Community</span>
+		<span class="de"></span>
+		<span class="es">Comunidad</span>
+		<span class="fr">Communauté</span>
+		<span class="it"></span>
+                <span class="ja">コミュニティ</span>
+		<span class="zh-CN">社区</span>
+		<span class="zh-TW">社群</span>
+	<?cs /if ?>
+	</a></li>
+     
+</ul>
diff --git a/tools/droiddoc/templates-sdk/sdkpage.cs b/tools/droiddoc/templates-sdk/sdkpage.cs
index b425da0..fdc1df5 100644
--- a/tools/droiddoc/templates-sdk/sdkpage.cs
+++ b/tools/droiddoc/templates-sdk/sdkpage.cs
@@ -4,7 +4,9 @@
 <?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 
+      if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?><?cs 
+      else ?><?cs var:sdk.current ?>/index.html<?cs /if ?>">
     <link href="<?cs var:toroot ?>assets/android-developer-docs.css" rel="stylesheet" type="text/css" />
   </head>
 <?cs else ?>
@@ -16,43 +18,106 @@
 
 <?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="<?cs var:toroot ?>sdk/<?cs 
+      if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?><?cs 
+      else ?><?cs var:sdk.current ?>/index.html<?cs /if ?>">sdk/<?cs 
+      if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?><?cs 
+      else ?><?cs var:sdk.current ?>/index.html<?cs /if ?>
+    </a> ...</p>
 <?cs else ?>
-  
+<div class="g-unit" id="doc-content" >
   <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 ?>
-  
-  
-<?cs if:android.whichdoc != "online" ?>
+
+<?cs if:android.whichdoc != "online" && !android.preview ?>
 
 <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="#overview">What is the 
+Android NDK?</a> to get an understanding of what the NDK offers and whether it 
+will be useful to you.</p>
+
+<p>Select the download package that is appropriate for your development
+computer. </p>
+
+  <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/ndk/<?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/ndk/<?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/64-bit (x86)</td>
+    <td>
+  <a href="http://dl.google.com/android/ndk/<?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>
+  </table>
+
+  <?cs else ?><?cs if:android.whichdoc == "online" ?>
+
+  <?cs if:sdk.preview ?>
+  <p>Welcome developers! The next release of the Android platform will be
+  Android 1.6 and we are pleased to announce the availability of an early look
+  SDK to give you a head-start on developing applications for it. </p>
+
+  <p>The Android 1.6 platform includes a variety of improvements and new
+  features for users and developers. Additionally, the SDK itself introduces
+  several new capabilities that enable you to develop applications more
+  efficiently. See the <a href="features.html">Android 1.6 Highlights</a> 
+  document for a list of highlights.</p>
+  <?cs /if ?>
+
+<p>Before downloading, please read the <a href="requirements.html">
 System Requirements</a> document. As you start the download, you will also need to review and agree to 
 the Terms and Conditions that govern the use of the Android SDK. </p>
   
@@ -87,8 +152,32 @@
     <td><?cs var:sdk.linux_bytes ?> bytes</td>
     <td><?cs var:sdk.linux_checksum ?></td>
   </tr>
+  <?cs if:adt.zip_download ?>
+  <tr class="alt-color">
+    <td>ADT Plugin for Eclipse <?cs var:adt.zip_version ?></td>
+    <td>
+  <a href="<?cs var:toroot ?>sdk/download.html?v=<?cs var:adt.zip_download ?>"><?cs var:adt.zip_download ?></a>
+    </td>
+    <td><?cs var:adt.zip_bytes ?> bytes</td>
+    <td><?cs var:adt.zip_checksum ?></td>
+  </tr>
+  <?cs /if ?>
   </table>
 
+  <?cs /if ?>
+ <?cs /if ?>
+<?cs /if ?> 
+
+<?cs if:android.whichdoc != "online" && sdk.preview ?>
+  <p>Welcome developers! The next release of the Android platform will be
+  Android 1.6 and we are pleased to announce the availability of an early look SDK
+  to give you a head-start on developing applications for it. </p>
+
+  <p>The Android 1.6 platform includes a variety of improvements and new features
+  for users and developers. Additionally, the SDK itself introduces several new
+  capabilities that enable you to develop applications more efficiently.
+  See the <a href="http://developer.android.com/sdk/preview/features.html">
+  Android 1.6 Highlights</a> document for a list of highlights.</p>
 <?cs /if ?>
 
       <?cs call:tag_list(root.descr) ?>
@@ -96,7 +185,10 @@
 <?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..acb873d 100644
--- a/tools/droiddoc/templates/assets/android-developer-core.css
+++ b/tools/droiddoc/templates/assets/android-developer-core.css
@@ -51,6 +51,12 @@
 textarea, option {
   font-family:inherit;
   font-size:inherit;
+  padding:0;
+  margin:0;
+}
+
+option {
+  padding:0 4px;
 }
 
 p {
@@ -110,23 +116,34 @@
   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;
 }
 
 li ul,
-li ol {
-  margin:.5em 0 0 0;
+li ol,
+dd ul,
+dd ol {
+  margin:0;
   padding: 0 0 0 2em;
 }
 
-dl li {
-  padding:.5em 0 0 0;
+li li,
+dd li {
+  margin:0;
+  padding:.5em 0 0;
 }
 
 dl dl,
@@ -166,10 +183,11 @@
 
 /* LAYOUT */
 #body-content {
+  /* "Preliminary" watermark for preview releases and interim builds.
+  background:transparent url(images/preliminary.png) repeat scroll 0 0;  */
   margin:0;
   position:relative;
   width:100%;
-  background: url('images/preliminary.png');
 }
 
 #header {
@@ -185,6 +203,11 @@
   padding: 25px 0 0;
 }
 
+#headerLeft img{
+  height:50px;
+  width:180px;
+}
+
 #headerRight {
   position:absolute;
   right:0;
@@ -232,14 +255,17 @@
   top:7px;
 }
 
+#header li a span+span {
+  display:none;
+}
+
 /* TAB HIGHLIGHTING */
 .home #home-link a,
-.publish #publish-link a,
 .guide #guide-link a,
 .reference #reference-link a,
 .sdk #sdk-link a,
 .community #community-link a,
-.about #about-link a {
+.videos #videos-link a {
   background-image: url(images/bg_images_sprite.png);
   background-position: 0 0;
   background-repeat: no-repeat;
@@ -249,12 +275,11 @@
 }
 
 .home #home-link a:hover,
-.publish #publish-link a:hover,
 .guide #guide-link a:hover,
 .reference #reference-link a:hover,
 .sdk #sdk-link a:hover,
 .community #community-link a:hover,
-.about #about-link  a:hover {
+.videos #videos-link  a:hover {
   background-image: url(images/bg_images_sprite.png);
   background-position: 0 0;
 }
@@ -262,20 +287,20 @@
 #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;
+}
+
+#headerLinks img {
+  vertical-align:middle;
+}
+
+#language {
+  margin:0 10px 0 4px;
 }
 
 #search {
@@ -679,7 +704,6 @@
 #search-button {
   margin:0 0 0 2px;
   font-size:11px;
-  height:1.8em;
 }
 
 /* search result tabs */
@@ -717,14 +741,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 +758,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,49 +778,83 @@
   margin:0;
 }
 
+#carousel {
+  background:url(images/home/bg_home_carousel.png) no-repeat 0 0;
+  position:relative;
+  height:400px;
+}
+
+#carouselMain {
+	background: url(images/home/bg_home_carousel_board.png) 0 0 no-repeat;
+	height:auto;
+  padding: 25px 21px 0;
+  overflow:hidden;
+  position:relative;
+  zoom:1; /*IE6*/
+}
+
+#carouselMain img {
+  margin:0;
+}
+
+#carouselMain .bulletinDesc h3 {
+	margin:0;
+	padding:0;
+}
+
+#carouselMain .bulletinDesc p {
+	margin:0;
+	padding:0.7em 0 0;
+}
+
+#carouselWheel {
+	background: url(images/home/bg_home_carousel_wheel.png) 0 0 no-repeat;
+	padding-top:40px;
+	height:150px;
+}
+
 .clearer { clear:both; }
 
-#arrow-left, #arrow-right {
+a#arrow-left, a#arrow-right {
   float:left;
   width:42px;
   height:42px;
   background-image:url(images/home/carousel_buttons_sprite.png);
   background-repeat:no-repeat;
 }
-#arrow-left {
+a#arrow-left {
   margin:35px 3px 0 10px;
 }
-#arrow-right {
+a#arrow-right {
   margin:35px 10px 0 0;
 }
-.arrow-left-off,
-#arrow-left.arrow-left-off:hover { 
+a.arrow-left-off,
+a#arrow-left.arrow-left-off:hover { 
   background-position:0 0;
 }
-.arrow-right-off, 
-#arrow-right.arrow-right-off:hover { 
+a.arrow-right-off, 
+a#arrow-right.arrow-right-off:hover { 
   background-position:-42px 0;
 }
-#arrow-left:hover { 
+a#arrow-left:hover { 
   background-position:0 -42px;
 }
-#arrow-right:hover { 
+a#arrow-right:hover { 
   background-position:-42px -42px;
 }
-.arrow-left-on {
+a.arrow-left-on {
   background-position:0 0;
 }
-.arrow-right-on {
+a.arrow-right-on {
   background-position:-42px 0;
 }
-.arrow-right-off,
-.arrow-left-off {
+a.arrow-right-off,
+a.arrow-left-off {
   cursor:default;
 }
 
 .app-list-container {
-  margin:37px 20px 0;
-  _margin-top:33px;
+  margin:0 20px;
   position:relative;
   width:100%;
 }
@@ -824,6 +890,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,16 +929,11 @@
   padding-bottom:.25em;
 }
 
-#carouselMain {
-  margin: 25px 21px 0;
-  height:185px;
-  background-position:top;
-  background-repeat:no-repeat;
-  overflow:hidden;
-}
+/*IE6*/
+* html #app-list a { zoom: 1; margin:0 24px 0 15px;}
 
-#carouselMain img {
-  margin:0;
+* html #list-clip { 
+  width:430px !important;
 }
 
 /*carousel bulletin layouts*/
@@ -875,36 +942,296 @@
 .img-left {
   float:left;
   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 {
-  height:80px;
-  text-align:center;
+  padding:20px 20px 0;
 }
 .desc-bottom {
-  height:100px;
-  margin:10px;
+  padding:10px;
 }
 
 
+/* VIDEO PAGE */
 
+#mainBodyLeft.videoPlayer {
+  width:570px;
+}
+
+#mainBodyRight.videoPlayer {
+  width:330px;
+}
+
+/* player */
+
+#videoPlayerBox {
+  background-color: #DAF3FC;
+  border-radius:7px;
+  -moz-border-radius:7px;
+  -webkit-border-radius:7px;
+  width:530px;
+  padding:20px;
+  border:1px solid #d3ecf5;
+  box-shadow:2px 3px 1px #eee;
+  -moz-box-shadow:2px 3px 1px #eee;
+  -webkit-box-shadow:2px 3px 1px #eee;
+}
+
+#videoBorder {
+  background-color: #FFF;
+  min-height:399px;
+  height:auto !important;
+  border:1px solid #ccdada;
+  border-radius:7px 7px 0 0;
+  -moz-border-radius:7px 7px 0 0;
+  -webkit-border-top-left-radius:7px;
+  -webkit-border-top-right-radius:7px;
+}
+
+#videoPlayerTitle {
+  width:500px;
+  padding:15px 15px 0;
+}
+
+#videoPlayerTitle h2 {
+  font-weight:bold;
+  font-size:1.2em;
+  color:#336666;
+  margin:0;
+  padding:0;
+}
+
+#objectWrapper {
+  padding:15px 15px;
+  height:334px;
+  width:500px;
+}
+
+/* playlist tabs */
+
+ul#videoTabs {
+  list-style-type:none;
+  padding:0;
+  clear:both;
+  margin:0;
+  padding: 20px 0 0 15px;
+  zoom:1; /* IE7/8, otherwise top-padding is double */
+}
+
+ul#videoTabs li {
+  display:inline;
+  padding:0;
+  margin:0 3px 0 0;
+  line-height:2em;
+}
+
+ul#videoTabs li a {
+  border-radius:7px 7px 0 0;
+  -moz-border-radius:7px 7px 0 0;
+  -webkit-border-top-left-radius:7px;
+  -webkit-border-top-right-radius:7px;
+  background:#95c0d0;
+  color:#fff;
+  text-decoration:none;
+  padding:.45em 1.5em;
+  font-weight:bold;
+}
+
+ul#videoTabs li.selected a {
+  font-weight:bold;
+  text-decoration:none;
+  color:#555;
+  background:#daf3fc;
+  border-bottom:1px solid #daf3fc;
+}
+
+ul#videoTabs li:hover a {
+  background:#85acba;
+}
+
+ul#videoTabs li.selected:hover a {
+  background:#daf3fc;
+}
+
+/* playlists */
+
+#videos {
+  background:#daf3fc;
+  margin-bottom:1.5em;
+  padding:15px;
+  border-radius:5px;
+  -moz-border-radius:5px;
+  -webkit-border-radius:5px;
+  box-shadow:2px 3px 1px #eee;
+  -moz-box-shadow:2px 3px 1px #eee;
+  -webkit-box-shadow:2px 3px 1px #eee;
+}
+
+#videos div {
+  display:none;
+}
+
+#videos div.selected {
+  display:block;
+}
+
+ul.videoPreviews {
+  list-style:none;
+  padding:0;
+  margin:0;
+  zoom:1; /* IE, otherwise, layout doesn't update when showing 'more' */
+}
+
+ul.videoPreviews li {
+  margin:0 0 5px;
+  padding:0;
+  overflow:hidden;
+  position:relative;
+}
+
+#mainBodyFixed ul.videoPreviews h3 {
+  font-size: 12px;
+  margin:0 0 1em 130px;
+  padding:0;
+  font-weight:bold;
+  color:inherit;
+}
+
+ul.videoPreviews a {
+  margin:1px;
+  padding:10px;
+  text-decoration:none;
+  height:90px;
+  display:block;
+  border-radius:5px;
+  -moz-border-radius:5px;
+  -webkit-border-radius:5px;
+  background-color:transparent;
+}
+
+ul.videoPreviews a:hover {
+  background-color:#FFF;
+  border:none; /* IE8, otherwise, bg doesn't work */
+}
+
+ul.videoPreviews a.selected {
+  background-color: #FF9900;
+}
+
+ul.videoPreviews img {
+  float:left;
+  clear:left;
+  margin:0;
+}
+
+ul.videoPreviews h3 {
+  font-size:12px;
+  font-weight:bold;
+  text-decoration:none;
+  margin:0 0 1em 130px;
+  padding:0;
+}
+
+ul.videoPreviews p {
+  font-size: 12px;
+  text-decoration:none;
+  margin:0 0 1.2em 130px;
+}
+
+ul.videoPreviews p.full {
+  display:none;
+}
+
+ul.videoPreviews span.more {
+  padding:0 0 0 12px;
+  background:url(images/arrow_bluelink_down.png) 0 2px no-repeat;
+}
+
+ul.videoPreviews span.less {
+  padding:0 0 0 12px;
+  background:url(images/arrow_bluelink_up.png) 0 2px no-repeat;
+  display:none;
+}
+
+ul.videoPreviews p.toggle {
+  position:absolute;
+  margin:0;
+  margin-top:-23px; /* instead of bottom:23px, because IE won't do it correctly */
+  left:140px;
+}
+
+ul.videoPreviews p.toggle a {
+  height:auto;
+  margin:0;
+  padding:0;
+  zoom:1; /* IE6, otherwise the margin considers the img on redraws */
+}
+
+ul.videoPreviews p.toggle a:hover {
+  text-decoration:underline;
+  background:transparent; /* IE6, otherwise it inherits white */
+}
+
+/* featured videos */
+
+#mainBodyRight h2 {
+  padding:0 0 5px;
+}
+
+#mainBodyRight ul.videoPreviews {
+  margin:10px 0 0;
+}
+
+#mainBodyRight ul.videoPreviews li {
+  font-size:11px;
+  line-height:13px;
+  margin:0 0 5px;
+  padding:0;
+}
+
+#mainBodyRight ul.videoPreviews h3 {
+  padding:0;
+  margin:0;
+}
+
+#mainBodyRight ul.videoPreviews a {
+  text-decoration:none;
+  height:108px;
+  border:1px solid #FFF;
+}
+
+#mainBodyRight ul.videoPreviews a:hover {
+  border:1px solid #CCDADA;
+}
+
+#mainBodyRight ul.videoPreviews a.selected {
+  border:1px solid #FFF;
+}
+
+#mainBodyRight ul.videoPreviews p {
+	line-height:1.2em;
+  padding:0;
+  margin:4px 0 0 130px;
+}
+
+#mainBodyRight ul.videoPreviews img {
+	margin-top:5px;
+}
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.css b/tools/droiddoc/templates/assets/android-developer-docs.css
index 747da92..d72098f 100644
--- a/tools/droiddoc/templates/assets/android-developer-docs.css
+++ b/tools/droiddoc/templates/assets/android-developer-docs.css
@@ -89,6 +89,10 @@
   zoom:1;
 }
 
+#side-nav li a span+span {
+  display:none;
+}
+
 #side-nav li a:hover {
   text-decoration:underline;
 }
@@ -272,9 +276,9 @@
 
 /* summary tables for reference pages */
 .jd-sumtable {
-margin: .5em 1em 1em 1em;
-width:99%;
-font-size:.9em;
+  margin: .5em 1em 1em 1em;
+  width:95%; /* consistent table widths; within IE's quirks */
+  font-size:.9em;
 }
 
 .jd-sumtable a {
@@ -317,7 +321,6 @@
 
 /* a div that holds a short description */
 .jd-descrdiv {
-  width:100%; 
   padding:3px 1em 0 1em;
   margin:0;
   border:0;
@@ -327,8 +330,7 @@
 links to summary tables) */
 #api-info-block {
   font-size:.8em;
-  margin:0;
-  padding:6px;
+  padding:6px 10px;
   font-weight:normal;
   float:right;
   text-align:right;
@@ -336,9 +338,117 @@
   max-width:70%;
 }
 
+#api-level-toggle {
+  float:right;
+  padding:0 10px;
+  font-size:11px;
+  color:#999;
+}
+
+div.api-level { 
+  font-size:.8em;
+  font-weight:normal;
+  color:#999;
+  float:right;
+  padding:0 7px 0;
+  margin-top:-25px;
+}
+
+#api-info-block div.api-level { 
+  font-size:1.3em;
+  font-weight:bold;
+  float:none;
+  color:#444;
+  padding:0;
+  margin:0;
+}
+
+/* Force link colors for IE6 */
+div.api-level a {
+  color:#999;
+}
+#api-info-block div.api-level a:link {
+  color:#444;
+}
+#api-level-toggle a {
+  color:#999;
+}
+
+div#naMessage {
+  display:none;
+  width:555px;
+  height:0;
+  margin:0 auto;
+}
+
+div#naMessage div {
+  width:450px;
+  position:fixed;
+  margin:50px 0;
+  padding:4em 4em 3em;
+  background:#FFF;
+  background:rgba(255,255,255,0.7);
+  border:1px solid #dddd00;
+}
+/* IE6 can't position fixed */
+* html div#naMessage div { position:absolute; }
+
+div#naMessage strong {
+  font-size:1.1em;
+}
+
+.absent,
+.absent a:link,
+.absent a:visited,
+.absent a:hover,
+.absent * {
+  color:#bbb !important;
+  cursor:default !important;
+  text-decoration:none !important;
+}
+
+#api-level-toggle a,
+.api-level a {
+  color:inherit;
+  text-decoration:none;
+}
+
+#api-level-toggle a:hover,
+.api-level a:hover {
+  color:inherit;
+  text-decoration:underline !important;
+  cursor:pointer !important;
+}
+
+#side-nav li.absent.selected,
+#side-nav li.absent.selected *,
+#side-nav div.label.absent.selected,
+#side-nav div.label.absent.selected * {
+  background-color:#eaeaea !important;
+}
+/* IE6 quirk (won't chain classes, so just keep background blue) */
+* html #side-nav li.selected,
+* html #side-nav li.selected *,
+* html #side-nav div.label.selected,
+* html #side-nav div.label.selected * {
+  background-color: #435a6e !important;
+}
+
+
+.absent h4.jd-details-title,
+.absent h4.jd-details-title * {
+  background-color:#f6f6f6 !important;
+}
+
+.absent img {
+  opacity: .3;
+  filter: alpha(opacity=30);
+  -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";
+}
+
+
 /* applies to a div containing links to summary tables */
 .sum-details-links {
-  margin:0 .5em;
   padding:0;
   font-weight:normal;
 }
@@ -433,11 +543,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;
 }
 
@@ -467,6 +577,10 @@
   margin:.5em 1em;
 }
 
+.jd-tagdata p {
+  margin:0 0 1em 1em;
+}
+
 /* API reference: adjustments to
 the detailed description block */
 .jd-tagdescr {
@@ -503,7 +617,7 @@
   font-size:1.15em;
   background-color: #E2E2E2;
   margin:1.5em 0 .6em;
-  padding:3px;
+  padding:3px 95px 3px 3px; /* room for api-level */
 }
 
 h4.jd-tagtitle {
@@ -570,15 +684,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 +700,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 +786,7 @@
 
 #qv ol ol{
   list-style:none;
-  padding: 0 0 3px 12px;
+  padding: 0 0 0 12px;
   margin:0;
 }
 
@@ -690,11 +795,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 +918,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 {
@@ -1019,21 +1184,16 @@
     padding:0;
   }
 
-  #headerLeft .guide {
-    display:none;
-  }
-  
-  #headerRight {
+  #header-tabs,
+  #headerRight,
+  #side-nav,
+  #api-info-block {
     display:none;
   }
 
   #body-content {
     position:inherit;
   }
-
-  #side-nav {
-    display:none;
-  }
   
   #doc-content {
     margin-left:0 !important;
@@ -1079,9 +1239,9 @@
     page-break-inside: avoid;
   }
 
-  #qv,
+/*  #qv,
   #qv-wrapper {
     display:none;
   }
-
+*/
 }
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.js b/tools/droiddoc/templates/assets/android-developer-docs.js
index 2a8c3bf..b16ed0d 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;
@@ -31,7 +31,9 @@
   addLoadEvent(mobileSetup);
 }
 
+addLoadEvent(function() {
 window.onresize = resizeAll;
+});
 
 function mobileSetup() {
   $("body").css({'overflow':'auto'});
@@ -42,6 +44,15 @@
   $("#nav-tree").css({'overflow-y': 'auto'});
 }
 
+/* loads the lists.js file to the page.
+Loading this in the head was slowing page load time */
+addLoadEvent( function() {
+  var lists = document.createElement("script");
+  lists.setAttribute("type","text/javascript");
+  lists.setAttribute("src", toRoot+"reference/lists.js");
+  $("head").append($(lists));
+} );
+
 function setToRoot(root) {
   toRoot = root;
   // note: toRoot also used by carousel.js
@@ -70,8 +81,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 +98,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 +126,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 +185,10 @@
   $("#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 basePath = getBaseUri(location.pathname);
+  var section = basePath.substring(1,basePath.indexOf("/",1));
+  writeCookie("height", resizePackagesNav.css("height"), section, null);
 }
 
 function resizeWidth() {
@@ -190,7 +203,10 @@
   resizePackagesNav.css({width:sidenavWidth});
   classesNav.css({width:sidenavWidth});
   $("#packages-nav").css({width:sidenavWidth});
-  writeCookie("width", sidenavWidth, "", null);
+  
+  var basePath = getBaseUri(location.pathname);
+  var section = basePath.substring(1,basePath.indexOf("/",1));
+  writeCookie("width", sidenavWidth, section, null);
 }
 
 function resizeAll() {
@@ -202,12 +218,38 @@
   }
 }
 
+function getBaseUri(uri) {
+  var intlUrl = (uri.substring(0,6) == "/intl/");
+  if (intlUrl) {
+    base = uri.substring(uri.indexOf('intl/')+5,uri.length);
+    base = base.substring(base.indexOf('/')+1, base.length);
+      //alert("intl, returning base url: /" + base);
+    return ("/" + base);
+  } else {
+      //alert("not intl, returning uri as found.");
+    return uri;
+  }
+}
+
+function requestAppendHL(uri) {
+//append "?hl=<lang> to an outgoing request (such as to blog)
+  var lang = getLangPref();
+  if (lang) {
+    var q = 'hl=' + lang;
+    uri += '?' + q;
+    window.location = uri;
+    return false;
+  } else {
+    return true;
+  }
+}
+
 function loadLast(cookiePath) {
   var location = window.location.href;
   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,16 +258,14 @@
 }
 
 $(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 = getBaseUri(location.pathname);
+  if (path.indexOf("/reference/") != -1) {
+    writeCookie("lastpage", path, "reference", null);
+  } else if (path.indexOf("/guide/") != -1) {
+    writeCookie("lastpage", path, "guide", null);
   }
 });
 
-
-
 function toggle(obj, slide) {
   var ul = $("ul", obj);
   var li = ul.parent();
@@ -246,8 +286,6 @@
   }
 }
 
-
-
 function buildToggleLists() {
   $(".toggle-list").each(
     function(i) {
@@ -257,7 +295,7 @@
 }
 
 function getNavPref() {
-  var v = getCookie('reference_nav');
+  var v = readCookie('reference_nav');
   if (v != NAV_PREF_TREE) {
     v = NAV_PREF_PANELS;
   }
@@ -279,11 +317,11 @@
     nav_pref = NAV_PREF_PANELS;
   } else {
     nav_pref = NAV_PREF_TREE;
-    init_navtree("nav-tree", toRoot, NAVTREE_DATA);
+    init_default_navtree(toRoot);
   }
   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, "reference", date.toGMTString());
 
   $("#nav-panels").toggle();
   $("#panel-link").toggle();
@@ -349,3 +387,64 @@
   }
   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, refresh) {
+  var date = new Date();
+  expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000))); // keep this for 50 years
+  //alert("expires: " + expires)
+  writeCookie("pref_lang", lang, null, expires);
+  //changeDocLang(lang);
+  if (refresh) {
+    l = getBaseUri(location.pathname);
+    window.location = l;
+  }
+}
+
+function loadLangPref() {
+  var lang = readCookie("pref_lang");
+  if (lang != 0) {
+    $("#language").find("option[value='"+lang+"']").attr("selected",true);
+  }
+}
+
+function getLangPref() {
+  var lang = $("#language").find(":selected").attr("value");
+  if (!lang) {
+    lang = readCookie("pref_lang");
+  }
+  return (lang != 0) ? lang : 'en';
+}
diff --git a/tools/droiddoc/templates/assets/android-developer-reference.js b/tools/droiddoc/templates/assets/android-developer-reference.js
new file mode 100644
index 0000000..3080760
--- /dev/null
+++ b/tools/droiddoc/templates/assets/android-developer-reference.js
@@ -0,0 +1,266 @@
+
+/* API LEVEL TOGGLE */
+addLoadEvent(changeApiLevel);
+var API_LEVEL_COOKIE = "api_level";
+var minLevel = 1;
+
+function buildApiLevelToggle() {
+	var maxLevel = SINCE_DATA.length;
+	var userApiLevel = readCookie(API_LEVEL_COOKIE);
+	
+	if (userApiLevel != 0) {
+		selectedLevel = userApiLevel;
+	} else {
+	  selectedLevel = maxLevel;
+	}
+	
+  minLevel = $("body").attr("class");
+	var select = $("#apiLevelControl").html("").change(changeApiLevel);
+	for (var i = maxLevel-1; i >= 0; i--) {
+		var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
+//		if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
+		select.append(option);
+	}
+	
+  // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
+	var selectedLevelItem = $("#apiLevelControl option[value='"+selectedLevel+"']").get(0); 
+  selectedLevelItem.setAttribute('selected',true); 
+}
+
+function changeApiLevel() {
+	var selectedLevel = $("#apiLevelControl option:selected").val();
+  toggleVisisbleApis(selectedLevel, "body");
+  
+  var date = new Date();
+  date.setTime(date.getTime()+(50*365*24*60*60*1000)); // keep this for 50 years
+  writeCookie(API_LEVEL_COOKIE, selectedLevel, null, date);
+  
+	if (selectedLevel < minLevel) {
+	  var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
+	  $("#naMessage").show().html("<div><p><strong>This " + thing + " is not available with API Level " + selectedLevel + ".</strong></p>"
+                              + "<p>To use this " + thing + ", your application must specify API Level " + minLevel + " or higher in its manifest "
+                              + "and be compiled against a version of the Android library that supports an equal or higher API Level. To reveal this "
+                              + "document, change the value of the API Level filter above.</p>"
+                              + "<p><a href='" +toRoot+ "guide/appendix/api-levels.html'>What is the API Level?</a></p></div>");
+	} else {
+    $("#naMessage").hide();
+  }
+}
+
+function toggleVisisbleApis(selectedLevel, context) {
+	var apis = $(".api",context);
+	apis.each(function(i) {
+		var obj = $(this);
+		var className = obj.attr("class");
+		var apiLevelIndex = className.lastIndexOf("-")+1;
+		var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
+		apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
+		var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
+		if (apiLevel > selectedLevel) obj.addClass("absent").attr("title","Requires API Level "+apiLevel+" or higher");
+		else obj.removeClass("absent").removeAttr("title");
+	});
+}
+
+/* NAVTREE */
+
+function new_node(me, mom, text, link, children_data, api_level)
+{
+  var node = new Object();
+  node.children = Array();
+  node.children_data = children_data;
+  node.depth = mom.depth + 1;
+
+  node.li = document.createElement("li");
+  mom.get_children_ul().appendChild(node.li);
+
+  node.label_div = document.createElement("div");
+  node.label_div.className = "label";
+  if (api_level != null) {
+    $(node.label_div).addClass("api");
+    $(node.label_div).addClass("api-level-"+api_level);
+  }
+  node.li.appendChild(node.label_div);
+  node.label_div.style.paddingLeft = 10*node.depth + "px";
+
+  if (children_data == null) {
+    // 12 is the width of the triangle and padding extra space
+    node.label_div.style.paddingLeft = ((10*node.depth)+12) + "px";
+  } else {
+    node.label_div.style.paddingLeft = 10*node.depth + "px";
+    node.expand_toggle = document.createElement("a");
+    node.expand_toggle.href = "javascript:void(0)";
+    node.expand_toggle.onclick = function() {
+          if (node.expanded) {
+            $(node.get_children_ul()).slideUp("fast");
+            node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
+            node.expanded = false;
+          } else {
+            expand_node(me, node);
+          }
+       };
+    node.label_div.appendChild(node.expand_toggle);
+
+    node.plus_img = document.createElement("img");
+    node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
+    node.plus_img.className = "plus";
+    node.plus_img.border = "0";
+    node.expand_toggle.appendChild(node.plus_img);
+
+    node.expanded = false;
+  }
+
+  var a = document.createElement("a");
+  node.label_div.appendChild(a);
+  node.label = document.createTextNode(text);
+  a.appendChild(node.label);
+  if (link) {
+    a.href = me.toroot + link;
+  } else {
+    if (children_data != null) {
+      a.className = "nolink";
+      a.href = "javascript:void(0)";
+      a.onclick = node.expand_toggle.onclick;
+      // This next line shouldn't be necessary.  I'll buy a beer for the first
+      // person who figures out how to remove this line and have the link
+      // toggle shut on the first try. --joeo@android.com
+      node.expanded = false;
+    }
+  }
+  
+
+  node.children_ul = null;
+  node.get_children_ul = function() {
+      if (!node.children_ul) {
+        node.children_ul = document.createElement("ul");
+        node.children_ul.className = "children_ul";
+        node.children_ul.style.display = "none";
+        node.li.appendChild(node.children_ul);
+      }
+      return node.children_ul;
+    };
+
+  return node;
+}
+
+function expand_node(me, node)
+{
+  if (node.children_data && !node.expanded) {
+    if (node.children_visited) {
+      $(node.get_children_ul()).slideDown("fast");
+    } else {
+      get_node(me, node);
+      if ($(node.label_div).hasClass("absent")) $(node.get_children_ul()).addClass("absent");
+      $(node.get_children_ul()).slideDown("fast");
+    }
+    node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
+    node.expanded = true;
+    
+    // perform api level toggling because new nodes are new to the DOM 
+	  var selectedLevel = $("#apiLevelControl option:selected").val();
+    toggleVisisbleApis(selectedLevel, "#side-nav");
+  }
+}
+
+function get_node(me, mom)
+{
+  mom.children_visited = true;
+  for (var i in mom.children_data) {
+    var node_data = mom.children_data[i];
+    mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
+        node_data[2], node_data[3]);
+  }
+}
+
+function this_page_relative(toroot)
+{
+  var full = document.location.pathname;
+  var file = "";
+  if (toroot.substr(0, 1) == "/") {
+    if (full.substr(0, toroot.length) == toroot) {
+      return full.substr(toroot.length);
+    } else {
+      // the file isn't under toroot.  Fail.
+      return null;
+    }
+  } else {
+    if (toroot != "./") {
+      toroot = "./" + toroot;
+    }
+    do {
+      if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
+        var pos = full.lastIndexOf("/");
+        file = full.substr(pos) + file;
+        full = full.substr(0, pos);
+        toroot = toroot.substr(0, toroot.length-3);
+      }
+    } while (toroot != "" && toroot != "/");
+    return file.substr(1);
+  }
+}
+
+function find_page(url, data)
+{
+  var nodes = data;
+  var result = null;
+  for (var i in nodes) {
+    var d = nodes[i];
+    if (d[1] == url) {
+      return new Array(i);
+    }
+    else if (d[2] != null) {
+      result = find_page(url, d[2]);
+      if (result != null) {
+        return (new Array(i).concat(result));
+      }
+    }
+  }
+  return null;
+}
+
+function load_navtree_data(toroot) {
+  var navtreeData = document.createElement("script");
+  navtreeData.setAttribute("type","text/javascript");
+  navtreeData.setAttribute("src", toroot+"navtree_data.js");
+  $("head").append($(navtreeData));
+}
+
+function init_default_navtree(toroot) {
+  init_navtree("nav-tree", toroot, NAVTREE_DATA);
+  
+  // perform api level toggling because because the whole tree is new to the DOM 
+	var selectedLevel = $("#apiLevelControl option:selected").val();
+  toggleVisisbleApis(selectedLevel, "#side-nav");
+}
+
+function init_navtree(navtree_id, toroot, root_nodes)
+{
+  var me = new Object();
+  me.toroot = toroot;
+  me.node = new Object();
+
+  me.node.li = document.getElementById(navtree_id);
+  me.node.children_data = root_nodes;
+  me.node.children = new Array();
+  me.node.children_ul = document.createElement("ul");
+  me.node.get_children_ul = function() { return me.node.children_ul; };
+  //me.node.children_ul.className = "children_ul";
+  me.node.li.appendChild(me.node.children_ul);
+  me.node.depth = 0;
+
+  get_node(me, me.node);
+
+  me.this_page = this_page_relative(toroot);
+  me.breadcrumbs = find_page(me.this_page, root_nodes);
+  if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
+    var mom = me.node;
+    for (var i in me.breadcrumbs) {
+      var j = me.breadcrumbs[i];
+      mom = mom.children[j];
+      expand_node(me, mom);
+    }
+    mom.label_div.className = mom.label_div.className + " selected";
+    addLoadEvent(function() {
+      scrollIntoView("nav-tree");
+      });
+  }
+}
diff --git a/tools/droiddoc/templates/assets/carousel.js b/tools/droiddoc/templates/assets/carousel.js
index 4eebd89..57eaff7 100644
--- a/tools/droiddoc/templates/assets/carousel.js
+++ b/tools/droiddoc/templates/assets/carousel.js
@@ -45,23 +45,8 @@
   currentDroid = appName;
 
   var droid = droidList[appName];
-  var layout = droid.layout;
-  var imgDiv = document.getElementById("bulletinImg");
-  var descDiv = document.getElementById("bulletinDesc");
-
-  if (layout == "imgLeft") {
-    imgDiv.className = "img-left";
-    descDiv.className = "desc-right";
-  } else if (layout == "imgTop") {
-    imgDiv.className = "img-top";
-    descDiv.className = "desc-bottom";
-  } else if (layout == "imgRight") {
-    imgDiv.className = "img-right";
-    descDiv.className = "desc-left";
-  }
-
-  imgDiv.innerHTML = "<img src='" + toRoot + assetsRoot + "images/home/" + droid.img + "'>";
-  descDiv.innerHTML = (droid.title != "") ? "<h3>" + droid.title + "</h3>" + droid.desc : droid.desc;
+  
+  $("#"+appName).show().siblings().hide();
 
   if(oldDroid)
     oldDroid.removeClass("selected");
@@ -95,7 +80,41 @@
     a.appendChild(br);
     a.appendChild(span);
     appList.appendChild(a);
+    
+    
+    /* add the bulletins */
+	  var layout = droid.layout;
+    var div = document.createElement("div");
+    var imgDiv = document.createElement("div");
+    var descDiv = document.createElement("div");
+    
+    div.setAttribute("id", x);
+    div.setAttribute("style", "display:none");
+    imgDiv.setAttribute("class", "bulletinImg");
+    descDiv.setAttribute("class", "bulletinDesc");
+	
+	  if (layout == "imgLeft") {
+	    $(imgDiv).addClass("img-left");
+	    $(descDiv).addClass("desc-right");
+	  } else if (layout == "imgTop") {
+	    $(imgDiv).addClass("img-top");
+	    $(descDiv).addClass("desc-bottom");
+	  } else if (layout == "imgRight") {
+	    $(imgDiv).addClass("img-right");
+	    $(descDiv).addClass("desc-left");
+	  }
+	
+	  imgDiv.innerHTML = "<img src='" + toRoot + assetsRoot + "images/home/" + droid.img + "'>";
+	  descDiv.innerHTML = (droid.title != "") ? "<h3>" + droid.title + "</h3>" + droid.desc : droid.desc;
+		$(div).append(imgDiv);
+		$(div).append(descDiv);
+    
+    $("#carouselMain").append(div);
+    
   }
+
+		
+  
 }
 
 // -- * slider * -- //
diff --git a/tools/droiddoc/templates/assets/images/arrow_bluelink_down.png b/tools/droiddoc/templates/assets/images/arrow_bluelink_down.png
new file mode 100755
index 0000000..58c248a
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_bluelink_down.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/arrow_bluelink_up.png b/tools/droiddoc/templates/assets/images/arrow_bluelink_up.png
new file mode 100755
index 0000000..7d0f38e
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_bluelink_up.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/android_adc.png b/tools/droiddoc/templates/assets/images/home/android_adc.png
new file mode 100644
index 0000000..9fe7f8f
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/android_adc.png
Binary files differ
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_carousel_board.png b/tools/droiddoc/templates/assets/images/home/bg_home_carousel_board.png
new file mode 100755
index 0000000..c577e02
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/bg_home_carousel_board.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/bg_home_carousel_wheel.png b/tools/droiddoc/templates/assets/images/home/bg_home_carousel_wheel.png
new file mode 100755
index 0000000..aa224ad
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/bg_home_carousel_wheel.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/home/donut-android.png b/tools/droiddoc/templates/assets/images/home/donut-android.png
new file mode 100644
index 0000000..85bc952
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/donut-android.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/io-large.png b/tools/droiddoc/templates/assets/images/home/io-large.png
new file mode 100755
index 0000000..986053c
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/io-large.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/io-small.png b/tools/droiddoc/templates/assets/images/home/io-small.png
new file mode 100755
index 0000000..3a22549
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/io-small.png
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/preliminary.png b/tools/droiddoc/templates/assets/images/preliminary.png
old mode 100755
new mode 100644
index aad1644..fe0da3d
--- a/tools/droiddoc/templates/assets/images/preliminary.png
+++ b/tools/droiddoc/templates/assets/images/preliminary.png
Binary files differ
diff --git a/tools/droiddoc/templates/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/jquery-resizable.min.js b/tools/droiddoc/templates/assets/jquery-resizable.min.js
index b3b4aed..67186fe 100755
--- a/tools/droiddoc/templates/assets/jquery-resizable.min.js
+++ b/tools/droiddoc/templates/assets/jquery-resizable.min.js
@@ -1,94 +1,40 @@
 /*
- * jQuery 1.2.6 - New Wave Javascript
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
  *
- * Copyright (c) 2008 John Resig (jquery.com)
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
+ */
+(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|checkbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),this.length>1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){if(G&&/\S/.test(G)){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+"></"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/<tbody/i.test(S),N=!O.indexOf("<table")&&!R?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(G){var J=[],L=o(G);for(var K=0,H=L.length;K<H;K++){var I=(K>0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}});
+/*
+ * Sizzle CSS Selector Engine - v0.9.3
+ *  Copyright 2009, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibling",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W){var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasDuplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=this.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML='   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();
+/*
+ * jQuery UI 1.7.2
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
  * Dual licensed under the MIT (MIT-LICENSE.txt)
  * and GPL (GPL-LICENSE.txt) licenses.
  *
- * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $
- * $Rev: 5685 $
+ * http://docs.jquery.com/UI
  */
-/* Includes jQuery UI core and resizable */ 
-(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else
-return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else
-return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else
-selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.attributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else
-return(this[0].value||"").replace(/\r/g,"");}return undefined;}if(value.constructor==Number)value+='';return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else
-this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else
-return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else
-jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){var src=target[name],copy=options[name];if(target===copy)continue;if(deep&&copy&&typeof copy=="object"&&!copy.nodeType)target[name]=jQuery.extend(deep,src||(copy.length!=null?[]:{}),copy);else if(copy!==undefined)target[name]=copy;}return target;};var expando="jQuery"+now(),uuid=0,windowData={},exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,defaultView=document.defaultView||{};jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/^[\s[]?function/.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else
-script.appendChild(document.createTextNode(data));head.insertBefore(script,head.firstChild);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!==undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){var name,i=0,length=object.length;if(args){if(length==undefined){for(name in object)if(callback.apply(object[name],args)===false)break;}else
-for(;i<length;)if(callback.apply(object[i++],args)===false)break;}else{if(length==undefined){for(name in object)if(callback.call(object[name],name,object[name])===false)break;}else
-for(var value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else
-jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(computedStyle&&computedStyle.getPropertyValue(name))||"";for(i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem+='';if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else
-ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&&notxml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&&notxml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&&notxml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else
-while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]===elem)return i;return-1;},merge:function(first,second){var i=0,elem,pos=first.length;if(jQuery.browser.msie){while(elem=second[i++])if(elem.nodeType!=8)first[pos++]=elem;}else
-while(elem=second[i++])first[pos++]=elem;return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv!=!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!=null)ret[ret.length]=value;}return ret.concat.apply([],ret);}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},siblings:function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.exec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else
-for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i<args.length)jQuery.event.proxy(fn,args[i++]);return this.click(jQuery.event.proxy(fn,function(event){this.lastToggle=(this.lastToggle||0)%i;event.preventDefault();return args[this.lastToggle++].apply(this,arguments)||false;}));},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else
-jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.call(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser.safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({_load:jQuery.fn.load,load:function(url,params,callback){if(typeof url!='string')return this._load(url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else
-xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else
-jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else
-for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else
-s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else
-e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this.elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})();
-
-;(function($){var _remove=$.fn.remove;$.fn.remove=function(){$("*",this).add(this).triggerHandler("remove");return _remove.apply(this,arguments);};function isVisible(element){function checkStyles(element){var style=element.style;return(style.display!='none'&&style.visibility!='hidden');}
-var visible=checkStyles(element);(visible&&$.each($.dir(element,'parentNode'),function(){return(visible=checkStyles(this));}));return visible;}
-$.extend($.expr[':'],{data:function(a,i,m){return $.data(a,m[3]);},tabbable:function(a,i,m){var nodeName=a.nodeName.toLowerCase();return(a.tabIndex>=0&&(('a'==nodeName&&a.href)||(/input|select|textarea|button/.test(nodeName)&&'hidden'!=a.type&&!a.disabled))&&isVisible(a));}});$.keyCode={BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38};function getter(namespace,plugin,method,args){function getMethods(type){var methods=$[namespace][plugin][type]||[];return(typeof methods=='string'?methods.split(/,?\s+/):methods);}
-var methods=getMethods('getter');if(args.length==1&&typeof args[0]=='string'){methods=methods.concat(getMethods('getterSetter'));}
-return($.inArray(method,methods)!=-1);}
-$.widget=function(name,prototype){var namespace=name.split(".")[0];name=name.split(".")[1];$.fn[name]=function(options){var isMethodCall=(typeof options=='string'),args=Array.prototype.slice.call(arguments,1);if(isMethodCall&&options.substring(0,1)=='_'){return this;}
-if(isMethodCall&&getter(namespace,name,options,args)){var instance=$.data(this[0],name);return(instance?instance[options].apply(instance,args):undefined);}
-return this.each(function(){var instance=$.data(this,name);(!instance&&!isMethodCall&&$.data(this,name,new $[namespace][name](this,options)));(instance&&isMethodCall&&$.isFunction(instance[options])&&instance[options].apply(instance,args));});};$[namespace][name]=function(element,options){var self=this;this.widgetName=name;this.widgetEventPrefix=$[namespace][name].eventPrefix||name;this.widgetBaseClass=namespace+'-'+name;this.options=$.extend({},$.widget.defaults,$[namespace][name].defaults,$.metadata&&$.metadata.get(element)[name],options);this.element=$(element).bind('setData.'+name,function(e,key,value){return self._setData(key,value);}).bind('getData.'+name,function(e,key){return self._getData(key);}).bind('remove',function(){return self.destroy();});this._init();};$[namespace][name].prototype=$.extend({},$.widget.prototype,prototype);$[namespace][name].getterSetter='option';};$.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName);},option:function(key,value){var options=key,self=this;if(typeof key=="string"){if(value===undefined){return this._getData(key);}
-options={};options[key]=value;}
-$.each(options,function(key,value){self._setData(key,value);});},_getData:function(key){return this.options[key];},_setData:function(key,value){this.options[key]=value;if(key=='disabled'){this.element[value?'addClass':'removeClass'](this.widgetBaseClass+'-disabled');}},enable:function(){this._setData('disabled',false);},disable:function(){this._setData('disabled',true);},_trigger:function(type,e,data){var eventName=(type==this.widgetEventPrefix?type:this.widgetEventPrefix+type);e=e||$.event.fix({type:eventName,target:this.element[0]});return this.element.triggerHandler(eventName,[e,data],this.options[type]);}};$.widget.defaults={disabled:false};$.ui={plugin:{add:function(module,option,set){var proto=$.ui[module].prototype;for(var i in set){proto.plugins[i]=proto.plugins[i]||[];proto.plugins[i].push([option,set[i]]);}},call:function(instance,name,args){var set=instance.plugins[name];if(!set){return;}
-for(var i=0;i<set.length;i++){if(instance.options[set[i][0]]){set[i][1].apply(instance.element,args);}}}},cssCache:{},css:function(name){if($.ui.cssCache[name]){return $.ui.cssCache[name];}
-var tmp=$('<div class="ui-gen">').addClass(name).css({position:'absolute',top:'-5000px',left:'-5000px',display:'block'}).appendTo('body');$.ui.cssCache[name]=!!((!(/auto|default/).test(tmp.css('cursor'))||(/^[1-9]/).test(tmp.css('height'))||(/^[1-9]/).test(tmp.css('width'))||!(/none/).test(tmp.css('backgroundImage'))||!(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor'))));try{$('body').get(0).removeChild(tmp.get(0));}catch(e){}
-return $.ui.cssCache[name];},disableSelection:function(el){return $(el).attr('unselectable','on').css('MozUserSelect','none').bind('selectstart.ui',function(){return false;});},enableSelection:function(el){return $(el).attr('unselectable','off').css('MozUserSelect','').unbind('selectstart.ui');},hasScroll:function(e,a){if($(e).css('overflow')=='hidden'){return false;}
-var scroll=(a&&a=='left')?'scrollLeft':'scrollTop',has=false;if(e[scroll]>0){return true;}
-e[scroll]=1;has=(e[scroll]>0);e[scroll]=0;return has;}};$.ui.mouse={_mouseInit:function(){var self=this;this.element.bind('mousedown.'+this.widgetName,function(e){return self._mouseDown(e);});if($.browser.msie){this._mouseUnselectable=this.element.attr('unselectable');this.element.attr('unselectable','on');}
-this.started=false;},_mouseDestroy:function(){this.element.unbind('.'+this.widgetName);($.browser.msie&&this.element.attr('unselectable',this._mouseUnselectable));},_mouseDown:function(e){(this._mouseStarted&&this._mouseUp(e));this._mouseDownEvent=e;var self=this,btnIsLeft=(e.which==1),elIsCancel=(typeof this.options.cancel=="string"?$(e.target).parents().add(e.target).filter(this.options.cancel).length:false);if(!btnIsLeft||elIsCancel||!this._mouseCapture(e)){return true;}
-this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){self.mouseDelayMet=true;},this.options.delay);}
-if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(e)!==false);if(!this._mouseStarted){e.preventDefault();return true;}}
-this._mouseMoveDelegate=function(e){return self._mouseMove(e);};this._mouseUpDelegate=function(e){return self._mouseUp(e);};$(document).bind('mousemove.'+this.widgetName,this._mouseMoveDelegate).bind('mouseup.'+this.widgetName,this._mouseUpDelegate);return false;},_mouseMove:function(e){if($.browser.msie&&!e.button){return this._mouseUp(e);}
-if(this._mouseStarted){this._mouseDrag(e);return false;}
-if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,e)!==false);(this._mouseStarted?this._mouseDrag(e):this._mouseUp(e));}
-return!this._mouseStarted;},_mouseUp:function(e){$(document).unbind('mousemove.'+this.widgetName,this._mouseMoveDelegate).unbind('mouseup.'+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._mouseStop(e);}
-return false;},_mouseDistanceMet:function(e){return(Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance);},_mouseDelayMet:function(e){return this.mouseDelayMet;},_mouseStart:function(e){},_mouseDrag:function(e){},_mouseStop:function(e){},_mouseCapture:function(e){return true;}};$.ui.mouse.defaults={cancel:null,distance:1,delay:0};})(jQuery);(function($){$.widget("ui.resizable",$.extend({},$.ui.mouse,{_init:function(){var self=this,o=this.options;var elpos=this.element.css('position');this.originalElement=this.element;this.element.addClass("ui-resizable").css({position:/static/.test(elpos)?'relative':elpos});$.extend(o,{_aspectRatio:!!(o.aspectRatio),helper:o.helper||o.ghost||o.animate?o.helper||'proxy':null,knobHandles:o.knobHandles===true?'ui-resizable-knob-handle':o.knobHandles});var aBorder='1px solid #DEDEDE';o.defaultTheme={'ui-resizable':{display:'block'},'ui-resizable-handle':{position:'absolute',background:'#F2F2F2',fontSize:'0.1px'},'ui-resizable-n':{cursor:'n-resize',height:'4px',left:'0px',right:'0px',borderTop:aBorder},'ui-resizable-s':{cursor:'s-resize',height:'4px',left:'0px',right:'0px',borderBottom:aBorder},'ui-resizable-e':{cursor:'e-resize',width:'4px',top:'0px',bottom:'0px',borderRight:aBorder},'ui-resizable-w':{cursor:'w-resize',width:'4px',top:'0px',bottom:'0px',borderLeft:aBorder},'ui-resizable-se':{cursor:'se-resize',width:'4px',height:'4px',borderRight:aBorder,borderBottom:aBorder},'ui-resizable-sw':{cursor:'sw-resize',width:'4px',height:'4px',borderBottom:aBorder,borderLeft:aBorder},'ui-resizable-ne':{cursor:'ne-resize',width:'4px',height:'4px',borderRight:aBorder,borderTop:aBorder},'ui-resizable-nw':{cursor:'nw-resize',width:'4px',height:'4px',borderLeft:aBorder,borderTop:aBorder}};o.knobTheme={'ui-resizable-handle':{background:'#F2F2F2',border:'1px solid #808080',height:'8px',width:'8px'},'ui-resizable-n':{cursor:'n-resize',top:'0px',left:'45%'},'ui-resizable-s':{cursor:'s-resize',bottom:'0px',left:'45%'},'ui-resizable-e':{cursor:'e-resize',right:'0px',top:'45%'},'ui-resizable-w':{cursor:'w-resize',left:'0px',top:'45%'},'ui-resizable-se':{cursor:'se-resize',right:'0px',bottom:'0px'},'ui-resizable-sw':{cursor:'sw-resize',left:'0px',bottom:'0px'},'ui-resizable-nw':{cursor:'nw-resize',left:'0px',top:'0px'},'ui-resizable-ne':{cursor:'ne-resize',right:'0px',top:'0px'}};o._nodeName=this.element[0].nodeName;if(o._nodeName.match(/canvas|textarea|input|select|button|img/i)){var el=this.element;if(/relative/.test(el.css('position'))&&$.browser.opera)
-el.css({position:'relative',top:'auto',left:'auto'});el.wrap($('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:el.css('position'),width:el.outerWidth(),height:el.outerHeight(),top:el.css('top'),left:el.css('left')}));var oel=this.element;this.element=this.element.parent();this.element.data('resizable',this);this.element.css({marginLeft:oel.css("marginLeft"),marginTop:oel.css("marginTop"),marginRight:oel.css("marginRight"),marginBottom:oel.css("marginBottom")});oel.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});if($.browser.safari&&o.preventDefault)oel.css('resize','none');o.proportionallyResize=oel.css({position:'static',zoom:1,display:'block'});this.element.css({margin:oel.css('margin')});this._proportionallyResize();}
-if(!o.handles)o.handles=!$('.ui-resizable-handle',this.element).length?"e,s,se":{n:'.ui-resizable-n',e:'.ui-resizable-e',s:'.ui-resizable-s',w:'.ui-resizable-w',se:'.ui-resizable-se',sw:'.ui-resizable-sw',ne:'.ui-resizable-ne',nw:'.ui-resizable-nw'};if(o.handles.constructor==String){o.zIndex=o.zIndex||1000;if(o.handles=='all')o.handles='n,e,s,w,se,sw,ne,nw';var n=o.handles.split(",");o.handles={};var insertionsDefault={handle:'position: absolute; display: none; overflow:hidden;',n:'top: 0pt; width:100%;',e:'right: 0pt; height:100%;',s:'bottom: 0pt; width:100%;',w:'left: 0pt; height:100%;',se:'bottom: 0pt; right: 0px;',sw:'bottom: 0pt; left: 0px;',ne:'top: 0pt; right: 0px;',nw:'top: 0pt; left: 0px;'};for(var i=0;i<n.length;i++){var handle=$.trim(n[i]),dt=o.defaultTheme,hname='ui-resizable-'+handle,loadDefault=!$.ui.css(hname)&&!o.knobHandles,userKnobClass=$.ui.css('ui-resizable-knob-handle'),allDefTheme=$.extend(dt[hname],dt['ui-resizable-handle']),allKnobTheme=$.extend(o.knobTheme[hname],!userKnobClass?o.knobTheme['ui-resizable-handle']:{});var applyZIndex=/sw|se|ne|nw/.test(handle)?{zIndex:++o.zIndex}:{};var defCss=(loadDefault?insertionsDefault[handle]:''),axis=$(['<div class="ui-resizable-handle ',hname,'" style="',defCss,insertionsDefault.handle,'"></div>'].join('')).css(applyZIndex);o.handles[handle]='.ui-resizable-'+handle;this.element.append(axis.css(loadDefault?allDefTheme:{}).css(o.knobHandles?allKnobTheme:{}).addClass(o.knobHandles?'ui-resizable-knob-handle':'').addClass(o.knobHandles));}
-if(o.knobHandles)this.element.addClass('ui-resizable-knob').css(!$.ui.css('ui-resizable-knob')?{}:{});}
-this._renderAxis=function(target){target=target||this.element;for(var i in o.handles){if(o.handles[i].constructor==String)
-o.handles[i]=$(o.handles[i],this.element).show();if(o.transparent)
-o.handles[i].css({opacity:0});if(this.element.is('.ui-wrapper')&&o._nodeName.match(/textarea|input|select|button/i)){var axis=$(o.handles[i],this.element),padWrapper=0;padWrapper=/sw|ne|nw|se|n|s/.test(i)?axis.outerHeight():axis.outerWidth();var padPos=['padding',/ne|nw|n/.test(i)?'Top':/se|sw|s/.test(i)?'Bottom':/^e$/.test(i)?'Right':'Left'].join("");if(!o.transparent)
-target.css(padPos,padWrapper);this._proportionallyResize();}
-if(!$(o.handles[i]).length)continue;}};this._renderAxis(this.element);o._handles=$('.ui-resizable-handle',self.element);if(o.disableSelection)
-o._handles.each(function(i,e){$.ui.disableSelection(e);});o._handles.mouseover(function(){if(!o.resizing){if(this.className)
-var axis=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);self.axis=o.axis=axis&&axis[1]?axis[1]:'se';}});if(o.autoHide){o._handles.hide();$(self.element).addClass("ui-resizable-autohide").hover(function(){$(this).removeClass("ui-resizable-autohide");o._handles.show();},function(){if(!o.resizing){$(this).addClass("ui-resizable-autohide");o._handles.hide();}});}
-this._mouseInit();},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,options:this.options,originalSize:this.originalSize,originalPosition:this.originalPosition};},_propagate:function(n,e){$.ui.plugin.call(this,n,[e,this.ui()]);if(n!="resize")this.element.triggerHandler(["resize",n].join(""),[e,this.ui()],this.options[n]);},destroy:function(){var el=this.element,wrapped=el.children(".ui-resizable").get(0);this._mouseDestroy();var _destroy=function(exp){$(exp).removeClass("ui-resizable ui-resizable-disabled").removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();};_destroy(el);if(el.is('.ui-wrapper')&&wrapped){el.parent().append($(wrapped).css({position:el.css('position'),width:el.outerWidth(),height:el.outerHeight(),top:el.css('top'),left:el.css('left')})).end().remove();_destroy(wrapped);}},_mouseCapture:function(e){if(this.options.disabled)return false;var handle=false;for(var i in this.options.handles){if($(this.options.handles[i])[0]==e.target)handle=true;}
-if(!handle)return false;return true;},_mouseStart:function(e){var o=this.options,iniPos=this.element.position(),el=this.element,num=function(v){return parseInt(v,10)||0;},ie6=$.browser.msie&&$.browser.version<7;o.resizing=true;o.documentScroll={top:$(document).scrollTop(),left:$(document).scrollLeft()};if(el.is('.ui-draggable')||(/absolute/).test(el.css('position'))){var sOffset=$.browser.msie&&!o.containment&&(/absolute/).test(el.css('position'))&&!(/relative/).test(el.parent().css('position'));var dscrollt=sOffset?o.documentScroll.top:0,dscrolll=sOffset?o.documentScroll.left:0;el.css({position:'absolute',top:(iniPos.top+dscrollt),left:(iniPos.left+dscrolll)});}
-if($.browser.opera&&/relative/.test(el.css('position')))
-el.css({position:'relative',top:'auto',left:'auto'});this._renderProxy();var curleft=num(this.helper.css('left')),curtop=num(this.helper.css('top'));if(o.containment){curleft+=$(o.containment).scrollLeft()||0;curtop+=$(o.containment).scrollTop()||0;}
-this.offset=this.helper.offset();this.position={left:curleft,top:curtop};this.size=o.helper||ie6?{width:el.outerWidth(),height:el.outerHeight()}:{width:el.width(),height:el.height()};this.originalSize=o.helper||ie6?{width:el.outerWidth(),height:el.outerHeight()}:{width:el.width(),height:el.height()};this.originalPosition={left:curleft,top:curtop};this.sizeDiff={width:el.outerWidth()-el.width(),height:el.outerHeight()-el.height()};this.originalMousePosition={left:e.pageX,top:e.pageY};o.aspectRatio=(typeof o.aspectRatio=='number')?o.aspectRatio:((this.originalSize.width/this.originalSize.height)||1);if(o.preserveCursor)
-$('body').css('cursor',this.axis+'-resize');this._propagate("start",e);return true;},_mouseDrag:function(e){var el=this.helper,o=this.options,props={},self=this,smp=this.originalMousePosition,a=this.axis;var dx=(e.pageX-smp.left)||0,dy=(e.pageY-smp.top)||0;var trigger=this._change[a];if(!trigger)return false;var data=trigger.apply(this,[e,dx,dy]),ie6=$.browser.msie&&$.browser.version<7,csdif=this.sizeDiff;if(o._aspectRatio||e.shiftKey)
-data=this._updateRatio(data,e);data=this._respectSize(data,e);this._propagate("resize",e);el.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});if(!o.helper&&o.proportionallyResize)
-this._proportionallyResize();this._updateCache(data);this.element.triggerHandler("resize",[e,this.ui()],this.options["resize"]);return false;},_mouseStop:function(e){this.options.resizing=false;var o=this.options,num=function(v){return parseInt(v,10)||0;},self=this;if(o.helper){var pr=o.proportionallyResize,ista=pr&&(/textarea/i).test(pr.get(0).nodeName),soffseth=ista&&$.ui.hasScroll(pr.get(0),'left')?0:self.sizeDiff.height,soffsetw=ista?0:self.sizeDiff.width;var s={width:(self.size.width-soffsetw),height:(self.size.height-soffseth)},left=(parseInt(self.element.css('left'),10)+(self.position.left-self.originalPosition.left))||null,top=(parseInt(self.element.css('top'),10)+(self.position.top-self.originalPosition.top))||null;if(!o.animate)
-this.element.css($.extend(s,{top:top,left:left}));if(o.helper&&!o.animate)this._proportionallyResize();}
-if(o.preserveCursor)
-$('body').css('cursor','auto');this._propagate("stop",e);if(o.helper)this.helper.remove();return false;},_updateCache:function(data){var o=this.options;this.offset=this.helper.offset();if(data.left)this.position.left=data.left;if(data.top)this.position.top=data.top;if(data.height)this.size.height=data.height;if(data.width)this.size.width=data.width;},_updateRatio:function(data,e){var o=this.options,cpos=this.position,csize=this.size,a=this.axis;if(data.height)data.width=(csize.height*o.aspectRatio);else if(data.width)data.height=(csize.width/o.aspectRatio);if(a=='sw'){data.left=cpos.left+(csize.width-data.width);data.top=null;}
-if(a=='nw'){data.top=cpos.top+(csize.height-data.height);data.left=cpos.left+(csize.width-data.width);}
-return data;},_respectSize:function(data,e){var el=this.helper,o=this.options,pRatio=o._aspectRatio||e.shiftKey,a=this.axis,ismaxw=data.width&&o.maxWidth&&o.maxWidth<data.width,ismaxh=data.height&&o.maxHeight&&o.maxHeight<data.height,isminw=data.width&&o.minWidth&&o.minWidth>data.width,isminh=data.height&&o.minHeight&&o.minHeight>data.height;if(isminw)data.width=o.minWidth;if(isminh)data.height=o.minHeight;if(ismaxw)data.width=o.maxWidth;if(ismaxh)data.height=o.maxHeight;var dw=this.originalPosition.left+this.originalSize.width,dh=this.position.top+this.size.height;var cw=/sw|nw|w/.test(a),ch=/nw|ne|n/.test(a);if(isminw&&cw)data.left=dw-o.minWidth;if(ismaxw&&cw)data.left=dw-o.maxWidth;if(isminh&&ch)data.top=dh-o.minHeight;if(ismaxh&&ch)data.top=dh-o.maxHeight;var isNotwh=!data.width&&!data.height;if(isNotwh&&!data.left&&data.top)data.top=null;else if(isNotwh&&!data.top&&data.left)data.left=null;return data;},_proportionallyResize:function(){var o=this.options;if(!o.proportionallyResize)return;var prel=o.proportionallyResize,el=this.helper||this.element;if(!o.borderDif){var b=[prel.css('borderTopWidth'),prel.css('borderRightWidth'),prel.css('borderBottomWidth'),prel.css('borderLeftWidth')],p=[prel.css('paddingTop'),prel.css('paddingRight'),prel.css('paddingBottom'),prel.css('paddingLeft')];o.borderDif=$.map(b,function(v,i){var border=parseInt(v,10)||0,padding=parseInt(p[i],10)||0;return border+padding;});}
-prel.css({height:(el.height()-o.borderDif[0]-o.borderDif[2])+"px",width:(el.width()-o.borderDif[1]-o.borderDif[3])+"px"});},_renderProxy:function(){var el=this.element,o=this.options;this.elementOffset=el.offset();if(o.helper){this.helper=this.helper||$('<div style="overflow:hidden;"></div>');var ie6=$.browser.msie&&$.browser.version<7,ie6offset=(ie6?1:0),pxyoffset=(ie6?2:-1);this.helper.addClass(o.helper).css({width:el.outerWidth()+pxyoffset,height:el.outerHeight()+pxyoffset,position:'absolute',left:this.elementOffset.left-ie6offset+'px',top:this.elementOffset.top-ie6offset+'px',zIndex:++o.zIndex});this.helper.appendTo("body");if(o.disableSelection)
-$.ui.disableSelection(this.helper.get(0));}else{this.helper=el;}},_change:{e:function(e,dx,dy){return{width:this.originalSize.width+dx};},w:function(e,dx,dy){var o=this.options,cs=this.originalSize,sp=this.originalPosition;return{left:sp.left+dx,width:cs.width-dx};},n:function(e,dx,dy){var o=this.options,cs=this.originalSize,sp=this.originalPosition;return{top:sp.top+dy,height:cs.height-dy};},s:function(e,dx,dy){return{height:this.originalSize.height+dy};},se:function(e,dx,dy){return $.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,dx,dy]));},sw:function(e,dx,dy){return $.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,dx,dy]));},ne:function(e,dx,dy){return $.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,dx,dy]));},nw:function(e,dx,dy){return $.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,dx,dy]));}}}));$.extend($.ui.resizable,{defaults:{cancel:":input",distance:1,delay:0,preventDefault:true,transparent:false,minWidth:10,minHeight:10,aspectRatio:false,disableSelection:true,preserveCursor:true,autoHide:false,knobHandles:false}});$.ui.plugin.add("resizable","containment",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),el=self.element;var oc=o.containment,ce=(oc instanceof $)?oc.get(0):(/parent/.test(oc))?el.parent().get(0):oc;if(!ce)return;self.containerElement=$(ce);if(/document/.test(oc)||oc==document){self.containerOffset={left:0,top:0};self.containerPosition={left:0,top:0};self.parentData={element:$(document),left:0,top:0,width:$(document).width(),height:$(document).height()||document.body.parentNode.scrollHeight};}
-else{self.containerOffset=$(ce).offset();self.containerPosition=$(ce).position();self.containerSize={height:$(ce).innerHeight(),width:$(ce).innerWidth()};var co=self.containerOffset,ch=self.containerSize.height,cw=self.containerSize.width,width=($.ui.hasScroll(ce,"left")?ce.scrollWidth:cw),height=($.ui.hasScroll(ce)?ce.scrollHeight:ch);self.parentData={element:ce,left:co.left,top:co.top,width:width,height:height};}},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),ps=self.containerSize,co=self.containerOffset,cs=self.size,cp=self.position,pRatio=o._aspectRatio||e.shiftKey,cop={top:0,left:0},ce=self.containerElement;if(ce[0]!=document&&/static/.test(ce.css('position')))
-cop=self.containerPosition;if(cp.left<(o.helper?co.left:cop.left)){self.size.width=self.size.width+(o.helper?(self.position.left-co.left):(self.position.left-cop.left));if(pRatio)self.size.height=self.size.width/o.aspectRatio;self.position.left=o.helper?co.left:cop.left;}
-if(cp.top<(o.helper?co.top:0)){self.size.height=self.size.height+(o.helper?(self.position.top-co.top):self.position.top);if(pRatio)self.size.width=self.size.height*o.aspectRatio;self.position.top=o.helper?co.top:0;}
-var woset=(o.helper?self.offset.left-co.left:(self.position.left-cop.left))+self.sizeDiff.width,hoset=(o.helper?self.offset.top-co.top:self.position.top)+self.sizeDiff.height;if(woset+self.size.width>=self.parentData.width){self.size.width=self.parentData.width-woset;if(pRatio)self.size.height=self.size.width/o.aspectRatio;}
-if(hoset+self.size.height>=self.parentData.height){self.size.height=self.parentData.height-hoset;if(pRatio)self.size.width=self.size.height*o.aspectRatio;}},stop:function(e,ui){var o=ui.options,self=$(this).data("resizable"),cp=self.position,co=self.containerOffset,cop=self.containerPosition,ce=self.containerElement;var helper=$(self.helper),ho=helper.offset(),w=helper.innerWidth(),h=helper.innerHeight();if(o.helper&&!o.animate&&/relative/.test(ce.css('position')))
-$(this).css({left:(ho.left-co.left),top:(ho.top-co.top),width:w,height:h});if(o.helper&&!o.animate&&/static/.test(ce.css('position')))
-$(this).css({left:cop.left+(ho.left-co.left),top:cop.top+(ho.top-co.top),width:w,height:h});}});$.ui.plugin.add("resizable","grid",{resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),cs=self.size,os=self.originalSize,op=self.originalPosition,a=self.axis,ratio=o._aspectRatio||e.shiftKey;o.grid=typeof o.grid=="number"?[o.grid,o.grid]:o.grid;var ox=Math.round((cs.width-os.width)/(o.grid[0]||1))*(o.grid[0]||1),oy=Math.round((cs.height-os.height)/(o.grid[1]||1))*(o.grid[1]||1);if(/^(se|s|e)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;}
-else if(/^(ne)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.top=op.top-oy;}
-else if(/^(sw)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.left=op.left-ox;}
-else{self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.top=op.top-oy;self.position.left=op.left-ox;}}});$.ui.plugin.add("resizable","animate",{stop:function(e,ui){var o=ui.options,self=$(this).data("resizable");var pr=o.proportionallyResize,ista=pr&&(/textarea/i).test(pr.get(0).nodeName),soffseth=ista&&$.ui.hasScroll(pr.get(0),'left')?0:self.sizeDiff.height,soffsetw=ista?0:self.sizeDiff.width;var style={width:(self.size.width-soffsetw),height:(self.size.height-soffseth)},left=(parseInt(self.element.css('left'),10)+(self.position.left-self.originalPosition.left))||null,top=(parseInt(self.element.css('top'),10)+(self.position.top-self.originalPosition.top))||null;self.element.animate($.extend(style,top&&left?{top:top,left:left}:{}),{duration:o.animateDuration||"slow",easing:o.animateEasing||"swing",step:function(){var data={width:parseInt(self.element.css('width'),10),height:parseInt(self.element.css('height'),10),top:parseInt(self.element.css('top'),10),left:parseInt(self.element.css('left'),10)};if(pr)pr.css({width:data.width,height:data.height});self._updateCache(data);self._propagate("animate",e);}});}});$.ui.plugin.add("resizable","ghost",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize,cs=self.size;if(!pr)self.ghost=self.element.clone();else self.ghost=pr.clone();self.ghost.css({opacity:.25,display:'block',position:'relative',height:cs.height,width:cs.width,margin:0,left:0,top:0}).addClass('ui-resizable-ghost').addClass(typeof o.ghost=='string'?o.ghost:'');self.ghost.appendTo(self.helper);},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize;if(self.ghost)self.ghost.css({position:'relative',height:self.size.height,width:self.size.width});},stop:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize;if(self.ghost&&self.helper)self.helper.get(0).removeChild(self.ghost.get(0));}});$.ui.plugin.add("resizable","alsoResize",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),_store=function(exp){$(exp).each(function(){$(this).data("resizable-alsoresize",{width:parseInt($(this).width(),10),height:parseInt($(this).height(),10),left:parseInt($(this).css('left'),10),top:parseInt($(this).css('top'),10)});});};if(typeof(o.alsoResize)=='object'){if(o.alsoResize.length){o.alsoResize=o.alsoResize[0];_store(o.alsoResize);}
-else{$.each(o.alsoResize,function(exp,c){_store(exp);});}}else{_store(o.alsoResize);}},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),os=self.originalSize,op=self.originalPosition;var delta={height:(self.size.height-os.height)||0,width:(self.size.width-os.width)||0,top:(self.position.top-op.top)||0,left:(self.position.left-op.left)||0},_alsoResize=function(exp,c){$(exp).each(function(){var start=$(this).data("resizable-alsoresize"),style={},css=c&&c.length?c:['width','height','top','left'];$.each(css||['width','height','top','left'],function(i,prop){var sum=(start[prop]||0)+(delta[prop]||0);if(sum&&sum>=0)
-style[prop]=sum||null;});$(this).css(style);});};if(typeof(o.alsoResize)=='object'){$.each(o.alsoResize,function(exp,c){_alsoResize(exp,c);});}else{_alsoResize(o.alsoResize);}},stop:function(e,ui){$(this).removeData("resizable-alsoresize-start");}});})(jQuery);
\ No newline at end of file
+jQuery.ui||(function(c){var i=c.fn.remove,d=c.browser.mozilla&&(parseFloat(c.browser.version)<1.9);c.ui={version:"1.7.2",plugin:{add:function(k,l,n){var m=c.ui[k].prototype;for(var j in n){m.plugins[j]=m.plugins[j]||[];m.plugins[j].push([l,n[j]])}},call:function(j,l,k){var n=j.plugins[l];if(!n||!j.element[0].parentNode){return}for(var m=0;m<n.length;m++){if(j.options[n[m][0]]){n[m][1].apply(j.element,k)}}}},contains:function(k,j){return document.compareDocumentPosition?k.compareDocumentPosition(j)&16:k!==j&&k.contains(j)},hasScroll:function(m,k){if(c(m).css("overflow")=="hidden"){return false}var j=(k&&k=="left")?"scrollLeft":"scrollTop",l=false;if(m[j]>0){return true}m[j]=1;l=(m[j]>0);m[j]=0;return l},isOverAxis:function(k,j,l){return(k>j)&&(k<(j+l))},isOver:function(o,k,n,m,j,l){return c.ui.isOverAxis(o,n,j)&&c.ui.isOverAxis(k,m,l)},keyCode:{BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};if(d){var f=c.attr,e=c.fn.removeAttr,h="http://www.w3.org/2005/07/aaa",a=/^aria-/,b=/^wairole:/;c.attr=function(k,j,l){var m=l!==undefined;return(j=="role"?(m?f.call(this,k,j,"wairole:"+l):(f.apply(this,arguments)||"").replace(b,"")):(a.test(j)?(m?k.setAttributeNS(h,j.replace(a,"aaa:"),l):f.call(this,k,j.replace(a,"aaa:"))):f.apply(this,arguments)))};c.fn.removeAttr=function(j){return(a.test(j)?this.each(function(){this.removeAttributeNS(h,j.replace(a,""))}):e.call(this,j))}}c.fn.extend({remove:function(){c("*",this).add(this).each(function(){c(this).triggerHandler("remove")});return i.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","").unbind("selectstart.ui")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})},scrollParent:function(){var j;if((c.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){j=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(c.curCSS(this,"position",1))&&(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}else{j=this.parents().filter(function(){return(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!j.length?c(document):j}});c.extend(c.expr[":"],{data:function(l,k,j){return !!c.data(l,j[3])},focusable:function(k){var l=k.nodeName.toLowerCase(),j=c.attr(k,"tabindex");return(/input|select|textarea|button|object/.test(l)?!k.disabled:"a"==l||"area"==l?k.href||!isNaN(j):!isNaN(j))&&!c(k)["area"==l?"parents":"closest"](":hidden").length},tabbable:function(k){var j=c.attr(k,"tabindex");return(isNaN(j)||j>=0)&&c(k).is(":focusable")}});function g(m,n,o,l){function k(q){var p=c[m][n][q]||[];return(typeof p=="string"?p.split(/,?\s+/):p)}var j=k("getter");if(l.length==1&&typeof l[0]=="string"){j=j.concat(k("getterSetter"))}return(c.inArray(o,j)!=-1)}c.widget=function(k,j){var l=k.split(".")[0];k=k.split(".")[1];c.fn[k]=function(p){var n=(typeof p=="string"),o=Array.prototype.slice.call(arguments,1);if(n&&p.substring(0,1)=="_"){return this}if(n&&g(l,k,p,o)){var m=c.data(this[0],k);return(m?m[p].apply(m,o):undefined)}return this.each(function(){var q=c.data(this,k);(!q&&!n&&c.data(this,k,new c[l][k](this,p))._init());(q&&n&&c.isFunction(q[p])&&q[p].apply(q,o))})};c[l]=c[l]||{};c[l][k]=function(o,n){var m=this;this.namespace=l;this.widgetName=k;this.widgetEventPrefix=c[l][k].eventPrefix||k;this.widgetBaseClass=l+"-"+k;this.options=c.extend({},c.widget.defaults,c[l][k].defaults,c.metadata&&c.metadata.get(o)[k],n);this.element=c(o).bind("setData."+k,function(q,p,r){if(q.target==o){return m._setData(p,r)}}).bind("getData."+k,function(q,p){if(q.target==o){return m._getData(p)}}).bind("remove",function(){return m.destroy()})};c[l][k].prototype=c.extend({},c.widget.prototype,j);c[l][k].getterSetter="option"};c.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled")},option:function(l,m){var k=l,j=this;if(typeof l=="string"){if(m===undefined){return this._getData(l)}k={};k[l]=m}c.each(k,function(n,o){j._setData(n,o)})},_getData:function(j){return this.options[j]},_setData:function(j,k){this.options[j]=k;if(j=="disabled"){this.element[k?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",k)}},enable:function(){this._setData("disabled",false)},disable:function(){this._setData("disabled",true)},_trigger:function(l,m,n){var p=this.options[l],j=(l==this.widgetEventPrefix?l:this.widgetEventPrefix+l);m=c.Event(m);m.type=j;if(m.originalEvent){for(var k=c.event.props.length,o;k;){o=c.event.props[--k];m[o]=m.originalEvent[o]}}this.element.trigger(m,n);return !(c.isFunction(p)&&p.call(this.element[0],m,n)===false||m.isDefaultPrevented())}};c.widget.defaults={disabled:false};c.ui.mouse={_mouseInit:function(){var j=this;this.element.bind("mousedown."+this.widgetName,function(k){return j._mouseDown(k)}).bind("click."+this.widgetName,function(k){if(j._preventClickEvent){j._preventClickEvent=false;k.stopImmediatePropagation();return false}});if(c.browser.msie){this._mouseUnselectable=this.element.attr("unselectable");this.element.attr("unselectable","on")}this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);(c.browser.msie&&this.element.attr("unselectable",this._mouseUnselectable))},_mouseDown:function(l){l.originalEvent=l.originalEvent||{};if(l.originalEvent.mouseHandled){return}(this._mouseStarted&&this._mouseUp(l));this._mouseDownEvent=l;var k=this,m=(l.which==1),j=(typeof this.options.cancel=="string"?c(l.target).parents().add(l.target).filter(this.options.cancel).length:false);if(!m||j||!this._mouseCapture(l)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){k.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(l)&&this._mouseDelayMet(l)){this._mouseStarted=(this._mouseStart(l)!==false);if(!this._mouseStarted){l.preventDefault();return true}}this._mouseMoveDelegate=function(n){return k._mouseMove(n)};this._mouseUpDelegate=function(n){return k._mouseUp(n)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);(c.browser.safari||l.preventDefault());l.originalEvent.mouseHandled=true;return true},_mouseMove:function(j){if(c.browser.msie&&!j.button){return this._mouseUp(j)}if(this._mouseStarted){this._mouseDrag(j);return j.preventDefault()}if(this._mouseDistanceMet(j)&&this._mouseDelayMet(j)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,j)!==false);(this._mouseStarted?this._mouseDrag(j):this._mouseUp(j))}return !this._mouseStarted},_mouseUp:function(j){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=(j.target==this._mouseDownEvent.target);this._mouseStop(j)}return false},_mouseDistanceMet:function(j){return(Math.max(Math.abs(this._mouseDownEvent.pageX-j.pageX),Math.abs(this._mouseDownEvent.pageY-j.pageY))>=this.options.distance)},_mouseDelayMet:function(j){return this.mouseDelayMet},_mouseStart:function(j){},_mouseDrag:function(j){},_mouseStop:function(j){},_mouseCapture:function(j){return true}};c.ui.mouse.defaults={cancel:null,distance:1,delay:0}})(jQuery);;/* * jQuery UI Resizable 1.7.2
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ *	ui.core.js
+ */
+(function(c){c.widget("ui.resizable",c.extend({},c.ui.mouse,{_init:function(){var e=this,j=this.options;this.element.addClass("ui-resizable");c.extend(this,{_aspectRatio:!!(j.aspectRatio),aspectRatio:j.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:j.helper||j.ghost||j.animate?j.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){if(/relative/.test(this.element.css("position"))&&c.browser.opera){this.element.css({position:"relative",top:"auto",left:"auto"})}this.element.wrap(c('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=j.handles||(!c(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all"){this.handles="n,e,s,w,se,sw,ne,nw"}var k=this.handles.split(",");this.handles={};for(var f=0;f<k.length;f++){var h=c.trim(k[f]),d="ui-resizable-"+h;var g=c('<div class="ui-resizable-handle '+d+'"></div>');if(/sw|se|ne|nw/.test(h)){g.css({zIndex:++j.zIndex})}if("se"==h){g.addClass("ui-icon ui-icon-gripsmall-diagonal-se")}this.handles[h]=".ui-resizable-"+h;this.element.append(g)}}this._renderAxis=function(p){p=p||this.element;for(var m in this.handles){if(this.handles[m].constructor==String){this.handles[m]=c(this.handles[m],this.element).show()}if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var n=c(this.handles[m],this.element),o=0;o=/sw|ne|nw|se|n|s/.test(m)?n.outerHeight():n.outerWidth();var l=["padding",/ne|nw|n/.test(m)?"Top":/se|sw|s/.test(m)?"Bottom":/^e$/.test(m)?"Right":"Left"].join("");p.css(l,o);this._proportionallyResize()}if(!c(this.handles[m]).length){continue}}};this._renderAxis(this.element);this._handles=c(".ui-resizable-handle",this.element).disableSelection();this._handles.mouseover(function(){if(!e.resizing){if(this.className){var i=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)}e.axis=i&&i[1]?i[1]:"se"}});if(j.autoHide){this._handles.hide();c(this.element).addClass("ui-resizable-autohide").hover(function(){c(this).removeClass("ui-resizable-autohide");e._handles.show()},function(){if(!e.resizing){c(this).addClass("ui-resizable-autohide");e._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var d=function(f){c(f).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){d(this.element);var e=this.element;e.parent().append(this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")})).end().remove()}this.originalElement.css("resize",this.originalResizeStyle);d(this.originalElement)},_mouseCapture:function(e){var f=false;for(var d in this.handles){if(c(this.handles[d])[0]==e.target){f=true}}return this.options.disabled||!!f},_mouseStart:function(f){var i=this.options,e=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:c(document).scrollTop(),left:c(document).scrollLeft()};if(d.is(".ui-draggable")||(/absolute/).test(d.css("position"))){d.css({position:"absolute",top:e.top,left:e.left})}if(c.browser.opera&&(/relative/).test(d.css("position"))){d.css({position:"relative",top:"auto",left:"auto"})}this._renderProxy();var j=b(this.helper.css("left")),g=b(this.helper.css("top"));if(i.containment){j+=c(i.containment).scrollLeft()||0;g+=c(i.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:j,top:g};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:j,top:g};this.sizeDiff={width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:f.pageX,top:f.pageY};this.aspectRatio=(typeof i.aspectRatio=="number")?i.aspectRatio:((this.originalSize.width/this.originalSize.height)||1);var h=c(".ui-resizable-"+this.axis).css("cursor");c("body").css("cursor",h=="auto"?this.axis+"-resize":h);d.addClass("ui-resizable-resizing");this._propagate("start",f);return true},_mouseDrag:function(d){var g=this.helper,f=this.options,l={},p=this,i=this.originalMousePosition,m=this.axis;var q=(d.pageX-i.left)||0,n=(d.pageY-i.top)||0;var h=this._change[m];if(!h){return false}var k=h.apply(this,[d,q,n]),j=c.browser.msie&&c.browser.version<7,e=this.sizeDiff;if(this._aspectRatio||d.shiftKey){k=this._updateRatio(k,d)}k=this._respectSize(k,d);this._propagate("resize",d);g.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});if(!this._helper&&this._proportionallyResizeElements.length){this._proportionallyResize()}this._updateCache(k);this._trigger("resize",d,this.ui());return false},_mouseStop:function(g){this.resizing=false;var h=this.options,l=this;if(this._helper){var f=this._proportionallyResizeElements,d=f.length&&(/textarea/i).test(f[0].nodeName),e=d&&c.ui.hasScroll(f[0],"left")?0:l.sizeDiff.height,j=d?0:l.sizeDiff.width;var m={width:(l.size.width-j),height:(l.size.height-e)},i=(parseInt(l.element.css("left"),10)+(l.position.left-l.originalPosition.left))||null,k=(parseInt(l.element.css("top"),10)+(l.position.top-l.originalPosition.top))||null;if(!h.animate){this.element.css(c.extend(m,{top:k,left:i}))}l.helper.height(l.size.height);l.helper.width(l.size.width);if(this._helper&&!h.animate){this._proportionallyResize()}}c("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",g);if(this._helper){this.helper.remove()}return false},_updateCache:function(d){var e=this.options;this.offset=this.helper.offset();if(a(d.left)){this.position.left=d.left}if(a(d.top)){this.position.top=d.top}if(a(d.height)){this.size.height=d.height}if(a(d.width)){this.size.width=d.width}},_updateRatio:function(g,f){var h=this.options,i=this.position,e=this.size,d=this.axis;if(g.height){g.width=(e.height*this.aspectRatio)}else{if(g.width){g.height=(e.width/this.aspectRatio)}}if(d=="sw"){g.left=i.left+(e.width-g.width);g.top=null}if(d=="nw"){g.top=i.top+(e.height-g.height);g.left=i.left+(e.width-g.width)}return g},_respectSize:function(k,f){var i=this.helper,h=this.options,q=this._aspectRatio||f.shiftKey,p=this.axis,s=a(k.width)&&h.maxWidth&&(h.maxWidth<k.width),l=a(k.height)&&h.maxHeight&&(h.maxHeight<k.height),g=a(k.width)&&h.minWidth&&(h.minWidth>k.width),r=a(k.height)&&h.minHeight&&(h.minHeight>k.height);if(g){k.width=h.minWidth}if(r){k.height=h.minHeight}if(s){k.width=h.maxWidth}if(l){k.height=h.maxHeight}var e=this.originalPosition.left+this.originalSize.width,n=this.position.top+this.size.height;var j=/sw|nw|w/.test(p),d=/nw|ne|n/.test(p);if(g&&j){k.left=e-h.minWidth}if(s&&j){k.left=e-h.maxWidth}if(r&&d){k.top=n-h.minHeight}if(l&&d){k.top=n-h.maxHeight}var m=!k.width&&!k.height;if(m&&!k.left&&k.top){k.top=null}else{if(m&&!k.top&&k.left){k.left=null}}return k},_proportionallyResize:function(){var j=this.options;if(!this._proportionallyResizeElements.length){return}var f=this.helper||this.element;for(var e=0;e<this._proportionallyResizeElements.length;e++){var g=this._proportionallyResizeElements[e];if(!this.borderDif){var d=[g.css("borderTopWidth"),g.css("borderRightWidth"),g.css("borderBottomWidth"),g.css("borderLeftWidth")],h=[g.css("paddingTop"),g.css("paddingRight"),g.css("paddingBottom"),g.css("paddingLeft")];this.borderDif=c.map(d,function(k,m){var l=parseInt(k,10)||0,n=parseInt(h[m],10)||0;return l+n})}if(c.browser.msie&&!(!(c(f).is(":hidden")||c(f).parents(":hidden").length))){continue}g.css({height:(f.height()-this.borderDif[0]-this.borderDif[2])||0,width:(f.width()-this.borderDif[1]-this.borderDif[3])||0})}},_renderProxy:function(){var e=this.element,h=this.options;this.elementOffset=e.offset();if(this._helper){this.helper=this.helper||c('<div style="overflow:hidden;"></div>');var d=c.browser.msie&&c.browser.version<7,f=(d?1:0),g=(d?2:-1);this.helper.addClass(this._helper).css({width:this.element.outerWidth()+g,height:this.element.outerHeight()+g,position:"absolute",left:this.elementOffset.left-f+"px",top:this.elementOffset.top-f+"px",zIndex:++h.zIndex});this.helper.appendTo("body").disableSelection()}else{this.helper=this.element}},_change:{e:function(f,e,d){return{width:this.originalSize.width+e}},w:function(g,e,d){var i=this.options,f=this.originalSize,h=this.originalPosition;return{left:h.left+e,width:f.width-e}},n:function(g,e,d){var i=this.options,f=this.originalSize,h=this.originalPosition;return{top:h.top+d,height:f.height-d}},s:function(f,e,d){return{height:this.originalSize.height+d}},se:function(f,e,d){return c.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[f,e,d]))},sw:function(f,e,d){return c.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[f,e,d]))},ne:function(f,e,d){return c.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[f,e,d]))},nw:function(f,e,d){return c.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[f,e,d]))}},_propagate:function(e,d){c.ui.plugin.call(this,e,[d,this.ui()]);(e!="resize"&&this._trigger(e,d,this.ui()))},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}));c.extend(c.ui.resizable,{version:"1.7.2",eventPrefix:"resize",defaults:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,cancel:":input,option",containment:false,delay:0,distance:1,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1000}});c.ui.plugin.add("resizable","alsoResize",{start:function(e,f){var d=c(this).data("resizable"),g=d.options;_store=function(h){c(h).each(function(){c(this).data("resizable-alsoresize",{width:parseInt(c(this).width(),10),height:parseInt(c(this).height(),10),left:parseInt(c(this).css("left"),10),top:parseInt(c(this).css("top"),10)})})};if(typeof(g.alsoResize)=="object"&&!g.alsoResize.parentNode){if(g.alsoResize.length){g.alsoResize=g.alsoResize[0];_store(g.alsoResize)}else{c.each(g.alsoResize,function(h,i){_store(h)})}}else{_store(g.alsoResize)}},resize:function(f,h){var e=c(this).data("resizable"),i=e.options,g=e.originalSize,k=e.originalPosition;var j={height:(e.size.height-g.height)||0,width:(e.size.width-g.width)||0,top:(e.position.top-k.top)||0,left:(e.position.left-k.left)||0},d=function(l,m){c(l).each(function(){var p=c(this),q=c(this).data("resizable-alsoresize"),o={},n=m&&m.length?m:["width","height","top","left"];c.each(n||["width","height","top","left"],function(r,t){var s=(q[t]||0)+(j[t]||0);if(s&&s>=0){o[t]=s||null}});if(/relative/.test(p.css("position"))&&c.browser.opera){e._revertToRelativePosition=true;p.css({position:"absolute",top:"auto",left:"auto"})}p.css(o)})};if(typeof(i.alsoResize)=="object"&&!i.alsoResize.nodeType){c.each(i.alsoResize,function(l,m){d(l,m)})}else{d(i.alsoResize)}},stop:function(e,f){var d=c(this).data("resizable");if(d._revertToRelativePosition&&c.browser.opera){d._revertToRelativePosition=false;el.css({position:"relative"})}c(this).removeData("resizable-alsoresize-start")}});c.ui.plugin.add("resizable","animate",{stop:function(h,m){var n=c(this).data("resizable"),i=n.options;var g=n._proportionallyResizeElements,d=g.length&&(/textarea/i).test(g[0].nodeName),e=d&&c.ui.hasScroll(g[0],"left")?0:n.sizeDiff.height,k=d?0:n.sizeDiff.width;var f={width:(n.size.width-k),height:(n.size.height-e)},j=(parseInt(n.element.css("left"),10)+(n.position.left-n.originalPosition.left))||null,l=(parseInt(n.element.css("top"),10)+(n.position.top-n.originalPosition.top))||null;n.element.animate(c.extend(f,l&&j?{top:l,left:j}:{}),{duration:i.animateDuration,easing:i.animateEasing,step:function(){var o={width:parseInt(n.element.css("width"),10),height:parseInt(n.element.css("height"),10),top:parseInt(n.element.css("top"),10),left:parseInt(n.element.css("left"),10)};if(g&&g.length){c(g[0]).css({width:o.width,height:o.height})}n._updateCache(o);n._propagate("resize",h)}})}});c.ui.plugin.add("resizable","containment",{start:function(e,q){var s=c(this).data("resizable"),i=s.options,k=s.element;var f=i.containment,j=(f instanceof c)?f.get(0):(/parent/.test(f))?k.parent().get(0):f;if(!j){return}s.containerElement=c(j);if(/document/.test(f)||f==document){s.containerOffset={left:0,top:0};s.containerPosition={left:0,top:0};s.parentData={element:c(document),left:0,top:0,width:c(document).width(),height:c(document).height()||document.body.parentNode.scrollHeight}}else{var m=c(j),h=[];c(["Top","Right","Left","Bottom"]).each(function(p,o){h[p]=b(m.css("padding"+o))});s.containerOffset=m.offset();s.containerPosition=m.position();s.containerSize={height:(m.innerHeight()-h[3]),width:(m.innerWidth()-h[1])};var n=s.containerOffset,d=s.containerSize.height,l=s.containerSize.width,g=(c.ui.hasScroll(j,"left")?j.scrollWidth:l),r=(c.ui.hasScroll(j)?j.scrollHeight:d);s.parentData={element:j,left:n.left,top:n.top,width:g,height:r}}},resize:function(f,p){var s=c(this).data("resizable"),h=s.options,e=s.containerSize,n=s.containerOffset,l=s.size,m=s.position,q=s._aspectRatio||f.shiftKey,d={top:0,left:0},g=s.containerElement;if(g[0]!=document&&(/static/).test(g.css("position"))){d=n}if(m.left<(s._helper?n.left:0)){s.size.width=s.size.width+(s._helper?(s.position.left-n.left):(s.position.left-d.left));if(q){s.size.height=s.size.width/h.aspectRatio}s.position.left=h.helper?n.left:0}if(m.top<(s._helper?n.top:0)){s.size.height=s.size.height+(s._helper?(s.position.top-n.top):s.position.top);if(q){s.size.width=s.size.height*h.aspectRatio}s.position.top=s._helper?n.top:0}s.offset.left=s.parentData.left+s.position.left;s.offset.top=s.parentData.top+s.position.top;var k=Math.abs((s._helper?s.offset.left-d.left:(s.offset.left-d.left))+s.sizeDiff.width),r=Math.abs((s._helper?s.offset.top-d.top:(s.offset.top-n.top))+s.sizeDiff.height);var j=s.containerElement.get(0)==s.element.parent().get(0),i=/relative|absolute/.test(s.containerElement.css("position"));if(j&&i){k-=s.parentData.left}if(k+s.size.width>=s.parentData.width){s.size.width=s.parentData.width-k;if(q){s.size.height=s.size.width/s.aspectRatio}}if(r+s.size.height>=s.parentData.height){s.size.height=s.parentData.height-r;if(q){s.size.width=s.size.height*s.aspectRatio}}},stop:function(e,m){var p=c(this).data("resizable"),f=p.options,k=p.position,l=p.containerOffset,d=p.containerPosition,g=p.containerElement;var i=c(p.helper),q=i.offset(),n=i.outerWidth()-p.sizeDiff.width,j=i.outerHeight()-p.sizeDiff.height;if(p._helper&&!f.animate&&(/relative/).test(g.css("position"))){c(this).css({left:q.left-d.left-l.left,width:n,height:j})}if(p._helper&&!f.animate&&(/static/).test(g.css("position"))){c(this).css({left:q.left-d.left-l.left,width:n,height:j})}}});c.ui.plugin.add("resizable","ghost",{start:function(f,g){var d=c(this).data("resizable"),h=d.options,e=d.size;d.ghost=d.originalElement.clone();d.ghost.css({opacity:0.25,display:"block",position:"relative",height:e.height,width:e.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof h.ghost=="string"?h.ghost:"");d.ghost.appendTo(d.helper)},resize:function(e,f){var d=c(this).data("resizable"),g=d.options;if(d.ghost){d.ghost.css({position:"relative",height:d.size.height,width:d.size.width})}},stop:function(e,f){var d=c(this).data("resizable"),g=d.options;if(d.ghost&&d.helper){d.helper.get(0).removeChild(d.ghost.get(0))}}});c.ui.plugin.add("resizable","grid",{resize:function(d,l){var n=c(this).data("resizable"),g=n.options,j=n.size,h=n.originalSize,i=n.originalPosition,m=n.axis,k=g._aspectRatio||d.shiftKey;g.grid=typeof g.grid=="number"?[g.grid,g.grid]:g.grid;var f=Math.round((j.width-h.width)/(g.grid[0]||1))*(g.grid[0]||1),e=Math.round((j.height-h.height)/(g.grid[1]||1))*(g.grid[1]||1);if(/^(se|s|e)$/.test(m)){n.size.width=h.width+f;n.size.height=h.height+e}else{if(/^(ne)$/.test(m)){n.size.width=h.width+f;n.size.height=h.height+e;n.position.top=i.top-e}else{if(/^(sw)$/.test(m)){n.size.width=h.width+f;n.size.height=h.height+e;n.position.left=i.left-f}else{n.size.width=h.width+f;n.size.height=h.height+e;n.position.top=i.top-e;n.position.left=i.left-f}}}}});var b=function(d){return parseInt(d,10)||0};var a=function(d){return !isNaN(parseInt(d,10))}})(jQuery);;
diff --git a/tools/droiddoc/templates/assets/navtree.js b/tools/droiddoc/templates/assets/navtree.js
deleted file mode 100644
index f48e1dc..0000000
--- a/tools/droiddoc/templates/assets/navtree.js
+++ /dev/null
@@ -1,179 +0,0 @@
-
-function new_node(me, mom, text, link, children_data)
-{
-  var node = new Object();
-  node.children = Array();
-  node.children_data = children_data;
-  node.depth = mom.depth + 1;
-
-  node.li = document.createElement("li");
-  mom.get_children_ul().appendChild(node.li);
-
-  node.label_div = document.createElement("div");
-  node.li.appendChild(node.label_div);
-  node.label_div.style.paddingLeft = 10*node.depth + "px";
-  node.label_div.className = "label";
-
-  if (children_data == null) {
-    // 12 is the width of the triangle and padding extra space
-    node.label_div.style.paddingLeft = ((10*node.depth)+12) + "px";
-  } else {
-    node.label_div.style.paddingLeft = 10*node.depth + "px";
-    node.expand_toggle = document.createElement("a");
-    node.expand_toggle.href = "javascript:void(0)";
-    node.expand_toggle.onclick = function() {
-          if (node.expanded) {
-            $(node.get_children_ul()).slideUp("fast");
-            node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
-            node.expanded = false;
-          } else {
-            expand_node(me, node);
-          }
-       };
-    node.label_div.appendChild(node.expand_toggle);
-
-    node.plus_img = document.createElement("img");
-    node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
-    node.plus_img.className = "plus";
-    node.plus_img.border = "0";
-    node.expand_toggle.appendChild(node.plus_img);
-
-    node.expanded = false;
-  }
-
-  var a = document.createElement("a");
-  node.label_div.appendChild(a);
-  node.label = document.createTextNode(text);
-  a.appendChild(node.label);
-  if (link) {
-    a.href = me.toroot + link;
-  } else {
-    if (children_data != null) {
-      a.className = "nolink";
-      a.href = "javascript:void(0)";
-      a.onclick = node.expand_toggle.onclick;
-      // This next line shouldn't be necessary.  I'll buy a beer for the first
-      // person who figures out how to remove this line and have the link
-      // toggle shut on the first try. --joeo@android.com
-      node.expanded = false;
-    }
-  }
-  
-
-  node.children_ul = null;
-  node.get_children_ul = function() {
-      if (!node.children_ul) {
-        node.children_ul = document.createElement("ul");
-        node.children_ul.className = "children_ul";
-        node.children_ul.style.display = "none";
-        node.li.appendChild(node.children_ul);
-      }
-      return node.children_ul;
-    };
-
-  return node;
-}
-
-function expand_node(me, node)
-{
-  if (node.children_data && !node.expanded) {
-    if (node.children_visited) {
-      $(node.get_children_ul()).slideDown("fast");
-    } else {
-      get_node(me, node);
-      $(node.get_children_ul()).slideDown("fast");
-    }
-    node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
-    node.expanded = true;
-  }
-}
-
-function get_node(me, mom)
-{
-  mom.children_visited = true;
-  for (var i in mom.children_data) {
-    var node_data = mom.children_data[i];
-    mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
-        node_data[2]);
-  }
-}
-
-function this_page_relative(toroot)
-{
-  var full = document.location.pathname;
-  var file = "";
-  if (toroot.substr(0, 1) == "/") {
-    if (full.substr(0, toroot.length) == toroot) {
-      return full.substr(toroot.length);
-    } else {
-      // the file isn't under toroot.  Fail.
-      return null;
-    }
-  } else {
-    if (toroot != "./") {
-      toroot = "./" + toroot;
-    }
-    do {
-      if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
-        var pos = full.lastIndexOf("/");
-        file = full.substr(pos) + file;
-        full = full.substr(0, pos);
-        toroot = toroot.substr(0, toroot.length-3);
-      }
-    } while (toroot != "" && toroot != "/");
-    return file.substr(1);
-  }
-}
-
-function find_page(url, data)
-{
-  var nodes = data;
-  var result = null;
-  for (var i in nodes) {
-    var d = nodes[i];
-    if (d[1] == url) {
-      return new Array(i);
-    }
-    else if (d[2] != null) {
-      result = find_page(url, d[2]);
-      if (result != null) {
-        return (new Array(i).concat(result));
-      }
-    }
-  }
-  return null;
-}
-
-function init_navtree(navtree_id, toroot, root_nodes)
-{
-  var me = new Object();
-  me.toroot = toroot;
-  me.node = new Object();
-
-  me.node.li = document.getElementById(navtree_id);
-  me.node.children_data = root_nodes;
-  me.node.children = new Array();
-  me.node.children_ul = document.createElement("ul");
-  me.node.get_children_ul = function() { return me.node.children_ul; };
-  //me.node.children_ul.className = "children_ul";
-  me.node.li.appendChild(me.node.children_ul);
-  me.node.depth = 0;
-
-  get_node(me, me.node);
-
-  me.this_page = this_page_relative(toroot);
-  me.breadcrumbs = find_page(me.this_page, root_nodes);
-  if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
-    var mom = me.node;
-    for (var i in me.breadcrumbs) {
-      var j = me.breadcrumbs[i];
-      mom = mom.children[j];
-      expand_node(me, mom);
-    }
-    mom.label_div.className = mom.label_div.className + " selected";
-    addLoadEvent(function() {
-      scrollIntoView("nav-tree");
-      });
-  }
-}
-
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/class.cs b/tools/droiddoc/templates/class.cs
index 1077886..9bc2ba0 100644
--- a/tools/droiddoc/templates/class.cs
+++ b/tools/droiddoc/templates/class.cs
@@ -2,7 +2,7 @@
 <?cs include:"macros.cs" ?>
 <html>
 <?cs include:"head_tag.cs" ?>
-<body>
+<body class="<?cs var:class.since ?>">
 <script type="text/javascript">
 function toggleInherited(linkObj, expand) {
     var base = linkObj.getAttribute("id");
@@ -26,7 +26,6 @@
     return false;
 }
 </script>
-
 <?cs include:"header.cs" ?>
 
 <div class="g-unit" id="doc-content">
@@ -102,12 +101,14 @@
 <?cs if:inhmethods ?>
   <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#inhmethods">Inherited Methods</a>
 <?cs /if ?>
-</nobr>
 <?cs if:inhattrs || inhconstants || inhfields || inhmethods || subcount(class.subclasses.direct) || subcount(class.subclasses.indirect) ?>
 &#124; <a href="#" onclick="return toggleAllSummaryInherited(this)">[Expand All]</a>
 <?cs /if ?>
+</div><!-- end sum-details-links -->
+<div class="api-level">
+  <?cs call:since_tags(class) ?>
 </div>
-</div>
+</div><!-- end api-info-block -->
 
 <?cs # this next line must be exactly like this to be parsed by eclipse ?>
 <!-- ======== START OF CLASS DATA ======== -->
@@ -136,8 +137,9 @@
 
 </div><!-- end header -->
 
+<div id="naMessage"></div>
 
-<div id="jd-content">
+<div id="jd-content" class="api apilevel-<?cs var:class.since ?>">
 <table class="jd-inheritance-table">
 <?cs set:colspan = subcount(class.inheritance) ?>
 <?cs each:supr = class.inheritance ?>
@@ -189,7 +191,8 @@
 <?cs def:write_method_summary(methods) ?>
 <?cs set:count = #1 ?>
 <?cs each:method = methods ?>
-    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+	 <?cs # The apilevel-N class MUST BE LAST in the sequence of class names ?>
+    <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:method.since ?>" >
         <td class="jd-typecol"><nobr>
             <?cs var:method.abstract ?>
             <?cs var:method.synchronized ?>
@@ -212,7 +215,7 @@
 <?cs def:write_field_summary(fields) ?>
 <?cs set:count = #1 ?>
     <?cs each:field=fields ?>
-      <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+      <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:field.since ?>" >
           <td class="jd-typecol"><nobr>
           <?cs var:field.scope ?>
           <?cs var:field.static ?>
@@ -228,7 +231,7 @@
 <?cs def:write_constant_summary(fields) ?>
 <?cs set:count = #1 ?>
     <?cs each:field=fields ?>
-    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+    <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:field.since ?>" >
         <td class="jd-typecol"><?cs call:type_link(field.type) ?></td>
         <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a></td>
         <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?></td>
@@ -245,7 +248,7 @@
         <td><nobr><em>Description</em></nobr></td>
     </tr>
     <?cs each:attr=attrs ?>
-    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+    <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:attr.since ?>" >
         <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:attr.href ?>"><?cs var:attr.name ?></a></td>
         <td class="jd-linkcol"><?cs each:m=attr.methods ?>
             <a href="<?cs var:toroot ?><?cs var:m.href ?>"><?cs var:m.name ?></a>
@@ -260,13 +263,13 @@
 <?cs def:write_inners_summary(classes) ?>
 <?cs set:count = #1 ?>
   <?cs each:cl=class.inners ?>
-    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+    <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:cl.since ?>" >
       <td class="jd-typecol"><nobr>
-        <?cs var:class.scope ?>
-        <?cs var:class.static ?> 
-        <?cs var:class.final ?> 
-        <?cs var:class.abstract ?>
-        <?cs var:class.kind ?></nobr></td>
+        <?cs var:cl.scope ?>
+        <?cs var:cl.static ?> 
+        <?cs var:cl.final ?> 
+        <?cs var:cl.abstract ?>
+        <?cs var:cl.kind ?></nobr></td>
       <td class="jd-linkcol"><?cs call:type_link(cl.type) ?></td>
       <td class="jd-descrcol" width="100%"><?cs call:short_descr(cl) ?>&nbsp;</td>
     </tr>
@@ -302,7 +305,8 @@
   <div style="clear:left;">Inherited XML Attributes</div></th></tr>
 <?cs each:cl=class.inherited ?>
 <?cs if:subcount(cl.attrs) ?>
-<tr><td colspan="12">
+<tr class="api apilevel-<?cs var:cl.since ?>" >
+<td colspan="12">
 <?cs call:expando_trigger("inherited-attrs-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
 <a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
 <div id="inherited-attrs-<?cs var:cl.qualified ?>">
@@ -326,7 +330,7 @@
 <table id="enumconstants" class="jd-sumtable"><tr><th colspan="12">Enum Values</th></tr>
 <?cs set:count = #1 ?>
     <?cs each:field=class.enumConstants ?>
-    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+    <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:field.since ?>" >
         <td class="jd-descrcol"><?cs call:type_link(field.type) ?>&nbsp;</td>
         <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a>&nbsp;</td>
         <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?>&nbsp;</td>
@@ -352,7 +356,8 @@
   <div style="clear:left;">Inherited Constants</div></th></tr>
 <?cs each:cl=class.inherited ?>
 <?cs if:subcount(cl.constants) ?>
-<tr><td colspan="12">
+<tr class="api apilevel-<?cs var:cl.since ?>" >
+<td colspan="12">
 <?cs call:expando_trigger("inherited-constants-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
 <a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
 <div id="inherited-constants-<?cs var:cl.qualified ?>">
@@ -387,7 +392,8 @@
   <div style="clear:left;">Inherited Fields</div></th></tr>
 <?cs each:cl=class.inherited ?>
 <?cs if:subcount(cl.fields) ?>
-<tr><td colspan="12">
+<tr class="api apilevel-<?cs var:cl.since ?>" >
+<td colspan="12">
 <?cs call:expando_trigger("inherited-fields-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
 <a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
 <div id="inherited-fields-<?cs var:cl.qualified ?>">
@@ -446,7 +452,8 @@
   <div style="clear:left;">Inherited Methods</div></th></tr>
 <?cs each:cl=class.inherited ?>
 <?cs if:subcount(cl.methods) ?>
-<tr><td colspan="12"><?cs call:expando_trigger("inherited-methods-"+cl.qualified, "closed") ?>
+<tr class="api apilevel-<?cs var:cl.since ?>" >
+<td colspan="12"><?cs call:expando_trigger("inherited-methods-"+cl.qualified, "closed") ?>
 From <?cs var:cl.kind ?> <a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
 <div id="inherited-methods-<?cs var:cl.qualified ?>">
   <div id="inherited-methods-<?cs var:cl.qualified ?>-list"
@@ -472,7 +479,8 @@
 <?cs # this next line must be exactly like this to be parsed by eclipse ?>
 <?cs # the A tag in the next line must remain where it is, so that Eclipse can parse the docs ?>
 <A NAME="<?cs var:field.anchor ?>"></A>
-<div class="jd-details"> 
+<?cs # The apilevel-N class MUST BE LAST in the sequence of class names ?>
+<div class="jd-details api apilevel-<?cs var:field.since ?>"> 
     <h4 class="jd-details-title">
       <span class="normal">
         <?cs var:field.scope ?> 
@@ -482,7 +490,11 @@
       </span>
         <?cs var:field.name ?>
     </h4>
-    <div class="jd-details-descr"><?cs call:description(field) ?>
+      <div class="api-level">
+        <?cs call:since_tags(field) ?>
+      </div>
+    <div class="jd-details-descr">
+      <?cs call:description(field) ?>
     <?cs if:subcount(field.constantValue) ?>
         <div class="jd-tagdata">
         <span class="jd-tagtitle">Constant Value: </span>
@@ -505,7 +517,8 @@
 <?cs each:method=methods ?>
 <?cs # the A tag in the next line must remain where it is, so that Eclipse can parse the docs ?>
 <A NAME="<?cs var:method.anchor ?>"></A>
-<div class="jd-details"> 
+<?cs # The apilevel-N class MUST BE LAST in the sequence of class names ?>
+<div class="jd-details api apilevel-<?cs var:method.since ?>"> 
     <h4 class="jd-details-title">
       <span class="normal">
         <?cs var:method.scope ?> 
@@ -518,7 +531,12 @@
       <span class="sympad"><?cs var:method.name ?></span>
       <span class="normal">(<?cs call:parameter_list(method.params) ?>)</span>
     </h4>
-    <div class="jd-details-descr"><?cs call:description(method) ?></div>
+      <div class="api-level">
+        <?cs call:since_tags(method) ?>
+      </div>
+    <div class="jd-details-descr">
+      <?cs call:description(method) ?>
+    </div>
 </div>
 <?cs /each ?>
 <?cs /def ?>
@@ -527,8 +545,13 @@
 <?cs each:attr=attrs ?>
 <?cs # the A tag in the next line must remain where it is, so that Eclipse can parse the docs ?>
 <A NAME="<?cs var:attr.anchor ?>"></A>
-<div class="jd-details">
-    <h4 class="jd-details-title"><?cs var:attr.name ?></h4>
+<?cs # The apilevel-N class MUST BE LAST in the sequence of class names ?>
+<div class="jd-details api apilevel-<?cs var:attr.since ?>"> 
+    <h4 class="jd-details-title"><?cs var:attr.name ?>
+    </h4>
+      <div class="api-level">
+        <?cs call:since_tags(attr) ?>
+      </div>
     <div class="jd-details-descr">
         <?cs call:description(attr) ?>
 
diff --git a/tools/droiddoc/templates/classes.cs b/tools/droiddoc/templates/classes.cs
index abe8e4e..5a8315f 100644
--- a/tools/droiddoc/templates/classes.cs
+++ b/tools/droiddoc/templates/classes.cs
@@ -22,7 +22,7 @@
 <table class="jd-sumtable">
     <?cs set:cur_row = #0 ?>
     <?cs each:cl = letter ?>
-        <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+        <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:cl.since ?>" >
             <td class="jd-linkcol"><?cs call:type_link(cl.type) ?></td>
             <td class="jd-descrcol" width="100%"><?cs call:short_descr(cl) ?>&nbsp;</td>
         </tr>
diff --git a/tools/droiddoc/templates/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..a2ea30a 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 
@@ -14,15 +11,14 @@
 <link href="<?cs var:toroot ?>assets/android-developer-docs.css" rel="stylesheet" type="text/css" /><?cs 
 /if ?>
 <script src="<?cs var:toroot ?>assets/search_autocomplete.js" type="text/javascript"></script>
-<script src="<?cs var:toroot ?>reference/lists.js" type="text/javascript"></script>
 <script src="<?cs var:toroot ?>assets/jquery-resizable.min.js" type="text/javascript"></script>
 <script src="<?cs var:toroot ?>assets/android-developer-docs.js" type="text/javascript"></script>
 <script type="text/javascript">
   setToRoot("<?cs var:toroot ?>");
 </script><?cs 
 if:reference ?>
-<script src="<?cs var:toroot ?>navtree_data.js" type="text/javascript"></script>
-<script src="<?cs var:toroot ?>assets/navtree.js" type="text/javascript"></script><?cs 
+<script src="<?cs var:toroot ?>assets/android-developer-reference.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>navtree_data.js" type="text/javascript"></script><?cs 
 /if ?>
 <noscript>
   <style type="text/css">
diff --git a/tools/droiddoc/templates/macros.cs b/tools/droiddoc/templates/macros.cs
index aec9c22..14dc90e 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
@@ -112,9 +115,13 @@
   /if ?>
 <?cs /def ?>
 
+<?cs # print the API Level ?><?cs
+def:since_tags(obj) ?>
+  Since: <a href="<?cs var:toroot ?>guide/appendix/api-levels.html#level<?cs var:obj.since ?>">API Level <?cs var:obj.since ?></a>
+<?cs /def ?>
 
 <?cs # Print the long-form description for something.
-       Uses the following fields: deprecated descr seeAlso ?><?cs 
+       Uses the following fields: deprecated descr seeAlso since ?><?cs
 def:description(obj) ?><?cs 
   call:deprecated_warning(obj) ?>
   <div class="jd-tagdata jd-tagdescr"><p><?cs call:tag_list(obj.descr) ?></p></div><?cs 
@@ -161,7 +168,7 @@
       </table>
   </div><?cs 
   /if ?><?cs 
-  call:see_also_tags(obj.seeAlso) ?><?cs 
+  call:see_also_tags(obj.seeAlso) ?><?cs
 /def ?>
 
 <?cs # A table of links to classes with descriptions, as in a package file or the nested classes ?><?cs
@@ -169,7 +176,7 @@
   set:count = #1 ?>
   <table class="jd-sumtable-expando"><?cs
       each:cl=classes ?>
-        <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+        <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:cl.type.since ?>" >
               <td class="jd-linkcol"><?cs call:type_link(cl.type) ?></td>
               <td class="jd-descrcol" width="100%"><?cs call:short_descr(cl) ?>&nbsp;</td>
           </tr><?cs set:count = count + #1 ?><?cs
@@ -177,32 +184,39 @@
   </table><?cs 
 /def ?>
 
-<?cs # A list of links to classes, for use in the side navigation of packages ?><?cs 
+<?cs # A list of links to classes, for use in the side navigation of classes when viewing a package (panel nav) ?><?cs 
 def:class_link_list(label, classes) ?><?cs 
   if:subcount(classes) ?>
     <li><h2><?cs var:label ?></h2>
       <ul><?cs 
       each:cl=classes ?>
-        <li><?cs call:type_link(cl.type) ?></li><?cs 
+        <li class="api apilevel-<?cs var:cl.type.since ?>"><?cs call:type_link(cl.type) ?></li><?cs 
       /each ?>
       </ul>
     </li><?cs 
   /if ?><?cs 
 /def ?>
 
-<?cs # A list of links to classes, for use in the side navigation of classes ?><?cs 
+<?cs # A list of links to classes, for use in the side navigation of classes when viewing a class (panel nav) ?><?cs 
 def:list(label, classes) ?><?cs 
   if:subcount(classes) ?>
     <li><h2><?cs var:label ?></h2>
       <ul><?cs 
       each:cl=classes ?>
-          <li <?cs if:class.name == cl.label?>class="selected"<?cs /if ?>><?cs call:type_link(cl) ?></li><?cs 
+          <li class="<?cs if:class.name == cl.label?>selected <?cs /if ?>api apilevel-<?cs var:cl.since ?>"><?cs call:type_link(cl) ?></li><?cs 
       /each ?>
       </ul>
     </li><?cs 
   /if ?><?cs 
 /def ?>
 
+<?cs # A list of links to packages, for use in the side navigation of packages (panel nav) ?><?cs 
+def:package_link_list(packages) ?><?cs 
+  each:pkg=packages ?>
+    <li class="<?cs if:(class.package.name == pkg.name) || (package.name == pkg.name)?>selected <?cs /if ?>api apilevel-<?cs var:pkg.since ?>"><?cs call:package_link(pkg) ?></li><?cs 
+  /each ?><?cs
+/def ?>
+
 <?cs # An expando trigger ?><?cs 
 def:expando_trigger(id, default) ?>
   <a href="#" onclick="return toggleInherited(this, null)" id="<?cs var:id ?>" class="jd-expando-trigger closed"
@@ -230,108 +244,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/package-descr.cs b/tools/droiddoc/templates/package-descr.cs
index 385ce23..08fee18 100644
--- a/tools/droiddoc/templates/package-descr.cs
+++ b/tools/droiddoc/templates/package-descr.cs
@@ -2,22 +2,28 @@
 <?cs include:"macros.cs" ?>
 <html>
 <?cs include:"head_tag.cs" ?>
+<body class="<?cs var:package.since ?>">
 <?cs include:"header.cs" ?>
 
 <div class="g-unit" id="doc-content">
 
+<div id="api-info-block">
+<div class="api-level">
+  <?cs call:since_tags(package) ?>
+</div>
+</div>
+
 <div id="jd-header">
-  <strong>
-    <div class="jd-page_title-prefix">package</div>
-  </strong>
+  package
   <h1><?cs var:package.name ?></b></h1>
   <div class="jd-nav">
-      <a class="jd-navlink" href="package-summary.html">Classes</a> |
-      Description
+      <a class="jd-navlink" href="package-summary.html">Classes</a> | Description
   </div>
 </div><!-- end header -->
 
-<div id="jd-content">
+<div id="naMessage"></div>
+
+<div id="jd-content" class="api apilevel-<?cs var:package.since ?>">
 <div class="jd-descr">
 <p><?cs call:tag_list(package.descr) ?></p>
 </div>
diff --git a/tools/droiddoc/templates/package.cs b/tools/droiddoc/templates/package.cs
index 7d1936d..b29bc77 100644
--- a/tools/droiddoc/templates/package.cs
+++ b/tools/droiddoc/templates/package.cs
@@ -2,28 +2,35 @@
 <?cs include:"macros.cs" ?>
 <html>
 <?cs include:"head_tag.cs" ?>
+<body class="<?cs var:package.since ?>">
 <?cs include:"header.cs" ?>
 
 <div class="g-unit" id="doc-content">
 
+<div id="api-info-block">
+<div class="api-level">
+  <?cs call:since_tags(package) ?>
+</div>
+</div>
+
 <div id="jd-header">
   package
   <h1><?cs var:package.name ?></h1>
-
   <div class="jd-nav">
       <?cs if:subcount(package.shortDescr) ?>
-      Classes |
-      <a class="jd-navlink" href="package-descr.html">Description</a>
+        Classes | <a class="jd-navlink" href="package-descr.html">Description</a>
       <?cs /if ?>
   </div>
-</div>
+</div><!-- end header -->
 
-<div id="jd-content">
+<div id="naMessage"></div>
+
+<div id="jd-content" class="api apilevel-<?cs var:package.since ?>">
 
 <?cs if:subcount(package.shortDescr) ?>
   <div class="jd-descr">
-  <p><?cs call:tag_list(package.shortDescr) ?>
-  <span class="jd-more"><a href="package-descr.html">more...</a></span></p>
+  <p><?cs call:tag_list(package.shortDescr) ?></p>
+  <p><span class="jd-more"><a href="package-descr.html">more...</a></span></p>
   </div>
 <?cs /if ?>
 
diff --git a/tools/droiddoc/templates/packages.cs b/tools/droiddoc/templates/packages.cs
index a358dca..c2d8c75 100644
--- a/tools/droiddoc/templates/packages.cs
+++ b/tools/droiddoc/templates/packages.cs
@@ -20,7 +20,7 @@
 <?cs set:count = #1 ?>
 <table class="jd-sumtable">
 <?cs each:pkg = docs.packages ?>
-    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+    <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:pkg.since ?>" >
         <td class="jd-linkcol"><?cs call:package_link(pkg) ?></td>
         <td class="jd-descrcol" width="100%"><?cs call:tag_list(pkg.shortDescr) ?>&nbsp;</td>
     </tr>
diff --git a/tools/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/droiddoc/test/stubs/func.sh b/tools/droiddoc/test/stubs/func.sh
index 1ad4bd5..ea4fe75 100644
--- a/tools/droiddoc/test/stubs/func.sh
+++ b/tools/droiddoc/test/stubs/func.sh
@@ -26,21 +26,22 @@
     STUBS_DIR=$3
 
     OBJ_DIR=out/stubs/$ID
+    PLATFORM=${HOST_OS}-${HOST_ARCH}
 
     rm -rf $OBJ_DIR &> /dev/null
     mkdir -p $OBJ_DIR
 
     find $SRC_DIR -name '*.java' > $OBJ_DIR/javadoc-src-list
     ( \
-        LD_LIBRARY_PATH=out/host/darwin-x86/lib \
+        LD_LIBRARY_PATH=out/host/$PLATFORM/lib \
         javadoc \
             \@$OBJ_DIR/javadoc-src-list \
             -J-Xmx512m \
-            -J-Djava.library.path=out/host/darwin-x86/lib \
+            -J-Djava.library.path=out/host/$PLATFORM/lib \
              \
             -quiet \
             -doclet DroidDoc \
-            -docletpath out/host/darwin-x86/framework/clearsilver.jar:out/host/darwin-x86/framework/droiddoc.jar \
+            -docletpath out/host/$PLATFORM/framework/clearsilver.jar:out/host/$PLATFORM/framework/droiddoc.jar:out/host/$PLATFORM/framework/apicheck.jar \
             -templatedir tools/droiddoc/templates \
             -classpath out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \
             -sourcepath $SRC_DIR:out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \
diff --git a/tools/droiddoc/test/stubs/run.sh b/tools/droiddoc/test/stubs/run.sh
index f237a7d..2ea15a6 100755
--- a/tools/droiddoc/test/stubs/run.sh
+++ b/tools/droiddoc/test/stubs/run.sh
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-DIR=tools/droiddoc/test/stubs
+DIR=build/tools/droiddoc/test/stubs
 
 pushd $TOP
 
diff --git a/tools/kcm/kcm.cpp b/tools/kcm/kcm.cpp
index 3e6320b..23ac377 100644
--- a/tools/kcm/kcm.cpp
+++ b/tools/kcm/kcm.cpp
@@ -198,7 +198,7 @@
         return 1;
     }
 
-    int out = open(outfilename, O_RDWR|O_CREAT|O_TRUNC, 0660);
+    int out = open(outfilename, O_RDWR|O_CREAT|O_TRUNC, 0664);
     if (out == -1) {
         fprintf(stderr, "kcm: error opening file for write: %s\n", outfilename);
         return 1;
diff --git a/tools/releasetools/amend_generator.py b/tools/releasetools/amend_generator.py
new file mode 100644
index 0000000..70e71d4
--- /dev/null
+++ b/tools/releasetools/amend_generator.py
@@ -0,0 +1,211 @@
+# 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_static", "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_static", "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_static", "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.DeleteFiles([i[1] for i in symlink_list])
+    self.script.extend(["symlink %s %s" % (i[0], self._FileRoot(i[1]))
+                        for i in sorted(symlink_list)])
+
+  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:
+      if isinstance(i, tuple):
+        sourcefn, targetfn = i
+      else:
+        sourcefn = i
+        targetfn = i
+      try:
+        if input_path is None:
+          data = input_zip.read(os.path.join("OTA/bin", sourcefn))
+        else:
+          data = open(os.path.join(input_path, sourcefn)).read()
+        common.ZipWriteStr(output_zip, targetfn, data, perms=0755)
+      except (IOError, KeyError), e:
+        raise ExternalError("unable to include binary %s: %s" % (i, e))
diff --git a/tools/releasetools/both_generator.py b/tools/releasetools/both_generator.py
new file mode 100644
index 0000000..df2a659
--- /dev/null
+++ b/tools/releasetools/both_generator.py
@@ -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.
+
+import edify_generator
+import amend_generator
+
+class BothGenerator(object):
+  def __init__(self, version):
+    self.version = version
+    self.edify = edify_generator.EdifyGenerator(version)
+    self.amend = amend_generator.AmendGenerator()
+
+  def MakeTemporary(self):
+    x = BothGenerator(self.version)
+    x.edify = self.edify.MakeTemporary()
+    x.amend = self.amend.MakeTemporary()
+    return x
+
+  def AppendScript(self, other):
+    self.edify.AppendScript(other.edify)
+    self.amend.AppendScript(other.amend)
+
+  def _DoBoth(self, name, *args):
+    getattr(self.edify, name)(*args)
+    getattr(self.amend, name)(*args)
+
+  def AssertSomeFingerprint(self, *a): self._DoBoth("AssertSomeFingerprint", *a)
+  def AssertOlderBuild(self, *a): self._DoBoth("AssertOlderBuild", *a)
+  def AssertDevice(self, *a): self._DoBoth("AssertDevice", *a)
+  def AssertSomeBootloader(self, *a): self._DoBoth("AssertSomeBootloader", *a)
+  def ShowProgress(self, *a): self._DoBoth("ShowProgress", *a)
+  def PatchCheck(self, *a): self._DoBoth("PatchCheck", *a)
+  def CacheFreeSpaceCheck(self, *a): self._DoBoth("CacheFreeSpaceCheck", *a)
+  def Mount(self, *a): self._DoBoth("Mount", *a)
+  def UnpackPackageDir(self, *a): self._DoBoth("UnpackPackageDir", *a)
+  def Comment(self, *a): self._DoBoth("Comment", *a)
+  def Print(self, *a): self._DoBoth("Print", *a)
+  def FormatPartition(self, *a): self._DoBoth("FormatPartition", *a)
+  def DeleteFiles(self, *a): self._DoBoth("DeleteFiles", *a)
+  def ApplyPatch(self, *a): self._DoBoth("ApplyPatch", *a)
+  def WriteFirmwareImage(self, *a): self._DoBoth("WriteFirmwareImage", *a)
+  def WriteRawImage(self, *a): self._DoBoth("WriteRawImage", *a)
+  def SetPermissions(self, *a): self._DoBoth("SetPermissions", *a)
+  def SetPermissionsRecursive(self, *a): self._DoBoth("SetPermissionsRecursive", *a)
+  def MakeSymlinks(self, *a): self._DoBoth("MakeSymlinks", *a)
+  def AppendExtra(self, *a): self._DoBoth("AppendExtra", *a)
+
+  def AddToZip(self, input_zip, output_zip, input_path=None):
+    self._DoBoth("AddToZip", input_zip, output_zip, input_path)
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 705ed84..2e3138c 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import errno
 import getopt
 import getpass
 import os
@@ -20,6 +21,7 @@
 import subprocess
 import sys
 import tempfile
+import zipfile
 
 # missing in Python 2.4 and before
 if not hasattr(os, "SEEK_SET"):
@@ -27,7 +29,7 @@
 
 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 = []
@@ -44,57 +46,81 @@
   return subprocess.Popen(args, **kwargs)
 
 
-def LoadBoardConfig(fn):
-  """Parse a board_config.mk file looking for lines that specify the
-  maximum size of various images, and parse them into the
-  OPTIONS.max_image_size dict."""
+def LoadMaxSizes():
+  """Load the maximum allowable images sizes from the input
+  target_files size."""
   OPTIONS.max_image_size = {}
-  for line in open(fn):
-    line = line.strip()
-    m = re.match(r"BOARD_(BOOT|RECOVERY|SYSTEM|USERDATA)IMAGE_MAX_SIZE"
-                 r"\s*:=\s*(\d+)", line)
-    if not m: continue
-
-    OPTIONS.max_image_size[m.group(1).lower() + ".img"] = int(m.group(2))
+  try:
+    for line in open(os.path.join(OPTIONS.input_tmp, "META", "imagesizes.txt")):
+      pieces = line.split()
+      if len(pieces) != 2: continue
+      image = pieces[0]
+      size = int(pieces[1])
+      OPTIONS.max_image_size[image + ".img"] = size
+  except IOError, e:
+    if e.errno == errno.ENOENT:
+      pass
 
 
 def BuildAndAddBootableImage(sourcedir, targetname, output_zip):
   """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()
@@ -118,7 +144,7 @@
 
   tmp = tempfile.mkdtemp(prefix="targetfiles-")
   OPTIONS.tempfiles.append(tmp)
-  p = Run(["unzip", "-q", filename, "-d", tmp], stdout=subprocess.PIPE)
+  p = Run(["unzip", "-o", "-q", filename, "-d", tmp], stdout=subprocess.PIPE)
   p.communicate()
   if p.returncode != 0:
     raise ExternalError("failed to unzip input target-files \"%s\"" %
@@ -131,22 +157,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 +201,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 +215,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 +244,8 @@
 
 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.
 
   -v  (--verbose)
       Show command lines being executed.
@@ -252,15 +287,13 @@
     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
     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 +304,111 @@
       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)
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..00abde4 100755
--- a/tools/releasetools/img_from_target_files
+++ b/tools/releasetools/img_from_target_files
@@ -21,8 +21,7 @@
 Usage:  img_from_target_files [flags] input_target_files output_image_zip
 
   -b  (--board_config)  <file>
-      Specifies a BoardConfig.mk file containing image max sizes
-      against which the generated image files are checked.
+      Deprecated.
 
 """
 
@@ -96,7 +95,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):
@@ -109,10 +108,10 @@
 
   def option_handler(o, a):
     if o in ("-b", "--board_config"):
-      common.LoadBoardConfig(a)
-      return True
+      pass       # deprecated
     else:
       return False
+    return True
 
   args = common.ParseOptions(argv, __doc__,
                              extra_opts="b:",
@@ -123,15 +122,15 @@
     common.Usage(__doc__)
     sys.exit(1)
 
+  OPTIONS.input_tmp = common.UnzipTemp(args[0])
+
+  common.LoadMaxSizes()
   if not OPTIONS.max_image_size:
     print
-    print "  WARNING:  No board config specified; will not check image"
-    print "  sizes against limits.  Use -b to make sure the generated"
-    print "  images don't exceed partition sizes."
+    print "  WARNING:  Failed to load max image sizes; will not enforce"
+    print "  image size limits."
     print
 
-  OPTIONS.input_tmp = common.UnzipTemp(args[0])
-
   output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
 
   common.AddBoot(output_zip)
diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files
index dbac03d..4aaad37 100755
--- a/tools/releasetools/ota_from_target_files
+++ b/tools/releasetools/ota_from_target_files
@@ -22,8 +22,7 @@
 Usage:  ota_from_target_files [flags] input_target_files output_ota_package
 
   -b  (--board_config)  <file>
-      Specifies a BoardConfig.mk file containing image max sizes
-      against which the generated image files are checked.
+      Deprecated.
 
   -k  (--package_key)  <key>
       Key to use to sign the package (default is
@@ -33,6 +32,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 +66,9 @@
 import zipfile
 
 import common
+import amend_generator
+import edify_generator
+import both_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,122 @@
   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)
-
-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]))
+  if m:
+    bootloaders = m.group(1).split("|")
+    script.AssertSomeBootloader(*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,))
+def MakeRecoveryPatch(output_zip, recovery_img, boot_img):
+  """Generate a binary patch that creates the recovery image starting
+  with the boot image.  (Most of the space in these images is just the
+  kernel, which is identical for the two, so the resulting patch
+  should be efficient.)  Add it to the output zip, along with a shell
+  script that is run from init.rc on first boot to actually do the
+  patching and install the new recovery image.
+
+  recovery_img and boot_img should be File objects for the
+  corresponding images.
+
+  Returns an Item for the shell script, which must be made
+  executable.
+  """
+
+  patch = Difference(recovery_img, boot_img, "imgdiff")
+  common.ZipWriteStr(output_zip, "system/recovery-from-boot.p", patch)
+  Item.Get("system/recovery-from-boot.p", dir=False)
+
+  # Images with different content will have a different first page, so
+  # we check to see if this recovery has already been installed by
+  # testing just the first 2k.
+  HEADER_SIZE = 2048
+  header_sha1 = sha.sha(recovery_img.data[:HEADER_SIZE]).hexdigest()
+  sh = """#!/system/bin/sh
+if ! applypatch -c MTD:recovery:%(header_size)d:%(header_sha1)s; then
+  log -t recovery "Installing new recovery image"
+  applypatch MTD:boot:%(boot_size)d:%(boot_sha1)s MTD:recovery %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p
+else
+  log -t recovery "Recovery image already installed"
+fi
+""" % { 'boot_size': boot_img.size,
+        'boot_sha1': boot_img.sha1,
+        'header_size': HEADER_SIZE,
+        'header_sha1': header_sha1,
+        'recovery_size': recovery_img.size,
+        'recovery_sha1': recovery_img.sha1 }
+  common.ZipWriteStr(output_zip, "system/etc/install-recovery.sh", sh)
+  return Item.Get("system/etc/install-recovery.sh", dir=False)
 
 
 def WriteFullOTAPackage(input_zip, output_zip):
-  script = []
+  if OPTIONS.script_mode == "auto":
+    script = both_generator.BothGenerator(2)
+  elif OPTIONS.script_mode == "amend":
+    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(2)
 
-  ts = GetBuildProp("ro.build.date.utc", input_zip)
-  script.append("run_program PACKAGE:check_prereq %s" % (ts,))
-  IncludeBinary("check_prereq", input_zip, output_zip)
+  if not OPTIONS.omit_prereq:
+    ts = GetBuildProp("ro.build.date.utc", input_zip)
+    script.AssertOlderBuild(ts)
 
   AppendAssertions(script, input_zip)
 
-  script.append("format BOOT:")
-  script.append("show_progress 0.1 0")
+  script.ShowProgress(0.1, 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")
+  try:
+    common.ZipWriteStr(output_zip, "radio.img", input_zip.read("RADIO/image"))
+    script.WriteFirmwareImage("radio", "radio.img")
+  except KeyError:
+    print "warning: no radio image in input target_files; not flashing radio"
 
-  script.append("format SYSTEM:")
-  script.append("copy_dir PACKAGE:system SYSTEM:")
+  script.ShowProgress(0.5, 0)
+
+  if OPTIONS.wipe_user_data:
+    script.FormatPartition("userdata")
+
+  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)
+  boot_img = File("boot.img", common.BuildBootableImage(
+      os.path.join(OPTIONS.input_tmp, "BOOT")))
+  recovery_img = File("recovery.img", common.BuildBootableImage(
+      os.path.join(OPTIONS.input_tmp, "RECOVERY")))
+  i = MakeRecoveryPatch(output_zip, recovery_img, boot_img)
 
-  FixPermissions(script)
+  Item.GetMetadata()
 
-  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")
+  # GetMetadata uses the data in android_filesystem_config.h to assign
+  # the uid/gid/mode of all files.  We want to override that for the
+  # recovery patching shell script to make it executable.
+  i.uid = 0
+  i.gid = 0
+  i.mode = 0544
+  Item.Get("system").SetPermissions(script)
 
-  AddScript(script, output_zip)
+  common.CheckSize(boot_img.data, "boot.img")
+  common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
+  script.ShowProgress(0.2, 0)
+
+  script.WriteRawImage("boot", "boot.img")
+  script.ShowProgress(0.2, 10)
+
+  if OPTIONS.extra_script is not None:
+    script.AppendExtra(OPTIONS.extra_script)
+
+  script.AddToZip(input_zip, output_zip)
 
 
 class File(object):
@@ -365,7 +408,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 +423,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 +436,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 +465,42 @@
     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,))
 
   print "Loading target..."
   target_data = LoadSystemFiles(target_zip)
@@ -433,20 +517,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,23 +547,24 @@
   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")
@@ -491,66 +580,110 @@
 
   AppendAssertions(script, target_zip)
 
+  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.append("\n# ---- start making changes here\n")
-
-  DeleteFiles(script, [SubstituteRoot(i[0]) for i in verbatim_targets])
+    script.PatchCheck("/"+fn, tf.sha1, sf.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")
+
+  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] +
+                     ["/system/recovery.img"])
+
+  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)
-    print "recovery image changed; including."
+    # Is it better to generate recovery as a patch from the current
+    # boot image, or from the previous recovery image?  For large
+    # updates with significant kernel changes, probably the former.
+    # For small updates where the kernel hasn't changed, almost
+    # certainly the latter.  We pick the first option.  Future
+    # complicated schemes may let us effectively use both.
+    #
+    # A wacky possibility: as long as there is room in the boot
+    # partition, include the binaries and image files from recovery in
+    # the boot image (though not in the ramdisk) so they can be used
+    # as fodder for constructing the recovery image.
+    recovery_sh_item = MakeRecoveryPatch(output_zip,
+                                         target_recovery, target_boot)
+    print "recovery image changed; including as patch from boot."
   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)
+    script.ShowProgress(0.3, 10)
+    script.Print("Writing radio image...")
+    script.WriteFirmwareImage("radio", "radio.img")
+    common.ZipWriteStr(output_zip, "radio.img", target_radio)
     print "radio image changed; including."
   else:
     print "radio image unchanged; skipping."
 
+  script.Print("Patching system files...")
   pb_apply = progress_bar_total * 0.7 * \
              (total_patched_size /
-              float(total_patched_size+total_verbatim_size))
+              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 = []
-  FixPermissions(temp_script)
+  temp_script = script.MakeTemporary()
+  Item.GetMetadata()
+  if updating_recovery:
+    recovery_sh_item.uid = 0
+    recovery_sh_item.gid = 0
+    recovery_sh_item.mode = 0544
+  Item.Get("system").SetPermissions(temp_script)
 
   # Note that this call will mess up the tree of Items, so make sure
   # we're done with it.
@@ -564,14 +697,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("Finishing up...")
 
   # Create all the symlinks that don't already exist, or point to
   # somewhere different than what we want.  Delete each symlink before
@@ -583,55 +719,71 @@
         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:")
+  if OPTIONS.extra_script is not None:
+    scirpt.AppendExtra(OPTIONS.extra_script)
 
-  AddScript(script, output_zip)
+  script.AddToZip(target_zip, output_zip)
 
 
 def main(argv):
 
   def option_handler(o, a):
     if o in ("-b", "--board_config"):
-      common.LoadBoardConfig(a)
-      return True
+      pass   # deprecated
     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:
     common.Usage(__doc__)
     sys.exit(1)
 
-  if not OPTIONS.max_image_size:
-    print
-    print "  WARNING:  No board config specified; will not check image"
-    print "  sizes against limits.  Use -b to make sure the generated"
-    print "  images don't exceed partition sizes."
-    print
+  if OPTIONS.script_mode not in ("amend", "edify", "auto"):
+    raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
+
+  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])
+
+  common.LoadMaxSizes()
+  if not OPTIONS.max_image_size:
+    print
+    print "  WARNING:  Failed to load max image sizes; will not enforce"
+    print "  image size limits."
+    print
+
   OPTIONS.target_tmp = OPTIONS.input_tmp
   input_zip = zipfile.ZipFile(args[0], "r")
   if OPTIONS.package_key:
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index b632924..158845c 100755
--- a/tools/releasetools/sign_target_files_apks
+++ b/tools/releasetools/sign_target_files_apks
@@ -47,6 +47,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 +69,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 +83,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 +101,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,44 +196,112 @@
   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")
+
+  if mapped_keys:
+    print "using:\n   ", "\n   ".join(mapped_keys)
+    print "for OTA package verification"
+  else:
+    mapped_keys.append(
+        OPTIONS.key_map["build/target/product/security/testkey"] + ".x509.pem")
+    print "META/otakeys.txt has no keys; using", mapped_keys[0]
+
+  # 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):
@@ -160,16 +324,28 @@
     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_opts="s:e:d:k:ot:",
                              extra_long_opts=["signapk_jar=",
                                               "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 +355,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..caf7935 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);
@@ -297,9 +304,14 @@
         pkcs7.encodeSignedData(out);
     }
 
-    /** Copy all the files in a manifest from input to output. */
+    /**
+     * Copy all the files in a manifest from input to output.  We set
+     * the modification times in the output to a fixed time, so as to
+     * reduce variation in the output file and make incremental OTAs
+     * more efficient.
+     */
     private static void copyFiles(Manifest manifest,
-            JarFile in, JarOutputStream out) throws IOException {
+        JarFile in, JarOutputStream out, long timestamp) throws IOException {
         byte[] buffer = new byte[4096];
         int num;
 
@@ -308,15 +320,16 @@
         Collections.sort(names);
         for (String name : names) {
             JarEntry inEntry = in.getJarEntry(name);
+            JarEntry outEntry = null;
             if (inEntry.getMethod() == JarEntry.STORED) {
                 // Preserve the STORED method of the input entry.
-                out.putNextEntry(new JarEntry(inEntry));
+                outEntry = new JarEntry(inEntry);
             } else {
                 // Create a new entry so that the compressed len is recomputed.
-                JarEntry je = new JarEntry(name);
-                je.setTime(inEntry.getTime());
-                out.putNextEntry(je);
+                outEntry = new JarEntry(name);
             }
+            outEntry.setTime(timestamp);
+            out.putNextEntry(outEntry);
 
             InputStream data = in.getInputStream(inEntry);
             while ((num = data.read(buffer)) > 0) {
@@ -373,7 +386,7 @@
             writeSignatureBlock(signature, publicKey, outputJar);
 
             // Everything else
-            copyFiles(manifest, inputJar, outputJar);
+            copyFiles(manifest, inputJar, outputJar, timestamp);
         } catch (Exception e) {
             e.printStackTrace();
             System.exit(1);
diff --git a/tools/zipalign/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp
index 9e3cb66..058f9ed 100644
--- a/tools/zipalign/ZipAlign.cpp
+++ b/tools/zipalign/ZipAlign.cpp
@@ -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;
 }
-