am 873782cc: am 9e3259f5: am fb67f05d: Merge changes I53b2ada9,I30794ea5

* commit '873782ccd44349c766f5d74e65b3b323618dd6ed':
  Disable emulator from platform builds.
  envsetup.sh: Use prebuilt emulator binaries if available.
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e83f2e7..46e652b 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -200,6 +200,12 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/SprintDM.apk)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/omadm)
 
+# GCC 4.8
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/*.o)
+
 # KLP I mean KitKat now API 19.
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)
@@ -223,8 +229,22 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)
 
+# L development
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)
+
+# L development
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)
+
 # Add ro.product.cpu.abilist{32,64} to build.prop.
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)
+
+# Unset TARGET_PREFER_32_BIT_APPS for 64 bit targets.
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)
 
 # Adding dalvik.vm.dex2oat-flags to eng builds
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/build.prop)
diff --git a/core/Makefile b/core/Makefile
index 55391dd..75573ce 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -22,7 +22,7 @@
 # src:dest pair is the first one to match the same dest"
 #$(1): the src:dest pair
 define check-product-copy-files
-$(if $(filter %.apk, $(1)),$(error \
+$(if $(filter %.apk, $(call word-colon, 2, $(1))),$(error \
     Prebuilt apk found in PRODUCT_COPY_FILES: $(1), use BUILD_PREBUILT instead!))
 endef
 # filter out the duplicate <source file>:<dest file> pairs.
@@ -109,7 +109,7 @@
 build_desc := $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT) $(PLATFORM_VERSION) $(BUILD_ID) $(BUILD_NUMBER) $(BUILD_VERSION_TAGS)
 $(INSTALLED_BUILD_PROP_TARGET): PRIVATE_BUILD_DESC := $(build_desc)
 
-# The string used to uniquely identify this build;  used by the OTA server.
+# The string used to uniquely identify the combined build and product; used by the OTA server.
 ifeq (,$(strip $(BUILD_FINGERPRINT)))
   BUILD_FINGERPRINT := $(PRODUCT_BRAND)/$(TARGET_PRODUCT)/$(TARGET_DEVICE):$(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)
 endif
@@ -117,6 +117,15 @@
   $(error BUILD_FINGERPRINT cannot contain spaces: "$(BUILD_FINGERPRINT)")
 endif
 
+# The string used to uniquely identify the system build; used by the OTA server.
+# This purposefully excludes any product-specific variables.
+ifeq (,$(strip $(BUILD_THUMBPRINT)))
+  BUILD_THUMBPRINT := $(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)
+endif
+ifneq ($(words $(BUILD_THUMBPRINT)),1)
+  $(error BUILD_THUMBPRINT cannot contain spaces: "$(BUILD_THUMBPRINT)")
+endif
+
 # Display parameters shown under Settings -> About Phone
 ifeq ($(TARGET_BUILD_VARIANT),user)
   # User builds should show:
@@ -165,10 +174,17 @@
 else
 system_prop_file := $(wildcard $(TARGET_DEVICE_DIR)/system.prop)
 endif
-
 $(INSTALLED_BUILD_PROP_TARGET): $(BUILDINFO_SH) $(INTERNAL_BUILD_ID_MAKEFILE) $(BUILD_SYSTEM)/version_defaults.mk $(system_prop_file)
 	@echo Target buildinfo: $@
 	@mkdir -p $(dir $@)
+	$(hide) echo > $@
+ifneq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_OEM_PROPERTIES),)
+	$(hide) echo "#" >> $@; \
+	        echo "# PRODUCT_OEM_PROPERTIES" >> $@; \
+	        echo "#" >> $@;
+	$(hide) $(foreach prop,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_OEM_PROPERTIES), \
+		echo "import /oem/oem.prop $(prop)" >> $@;)
+endif
 	$(hide) TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)" \
 			TARGET_DEVICE="$(TARGET_DEVICE)" \
 			PRODUCT_NAME="$(TARGET_PRODUCT)" \
@@ -185,9 +201,11 @@
 			PLATFORM_VERSION="$(PLATFORM_VERSION)" \
 			PLATFORM_SDK_VERSION="$(PLATFORM_SDK_VERSION)" \
 			PLATFORM_VERSION_CODENAME="$(PLATFORM_VERSION_CODENAME)" \
+			PLATFORM_VERSION_ALL_CODENAMES="$(PLATFORM_VERSION_ALL_CODENAMES)" \
 			BUILD_VERSION_TAGS="$(BUILD_VERSION_TAGS)" \
 			TARGET_BOOTLOADER_BOARD_NAME="$(TARGET_BOOTLOADER_BOARD_NAME)" \
 			BUILD_FINGERPRINT="$(BUILD_FINGERPRINT)" \
+			BUILD_THUMBPRINT="$(BUILD_THUMBPRINT)" \
 			TARGET_BOARD_PLATFORM="$(TARGET_BOARD_PLATFORM)" \
 			TARGET_CPU_ABI_LIST="$(TARGET_CPU_ABI_LIST)" \
 			TARGET_CPU_ABI_LIST_32_BIT="$(TARGET_CPU_ABI_LIST_32_BIT)" \
@@ -195,7 +213,7 @@
 			TARGET_CPU_ABI="$(TARGET_CPU_ABI)" \
 			TARGET_CPU_ABI2="$(TARGET_CPU_ABI2)" \
 			TARGET_AAPT_CHARACTERISTICS="$(TARGET_AAPT_CHARACTERISTICS)" \
-	        bash $(BUILDINFO_SH) > $@
+	        bash $(BUILDINFO_SH) >> $@
 	$(hide) $(foreach file,$(system_prop_file), \
 		if [ -f "$(file)" ]; then \
 			echo "#" >> $@; \
@@ -211,7 +229,7 @@
 		        echo "#" >> $@; )
 	$(hide) $(foreach line,$(ADDITIONAL_BUILD_PROPERTIES), \
 		echo "$(line)" >> $@;)
-	$(hide) build/tools/post_process_props.py $@
+	$(hide) build/tools/post_process_props.py $@ $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_PROPERTY_BLACKLIST)
 
 build_desc :=
 
@@ -622,6 +640,10 @@
 endif
 INTERNAL_USERIMAGES_BINARY_PATHS := $(sort $(dir $(INTERNAL_USERIMAGES_DEPS)))
 
+ifeq (true,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY))
+INTERNAL_USERIMAGES_DEPS += $(BUILD_VERITY_TREE) $(APPEND2SIMG) $(VERITY_SIGNER)
+endif
+
 SELINUX_FC := $(TARGET_ROOT_OUT)/file_contexts
 INTERNAL_USERIMAGES_DEPS += $(SELINUX_FC)
 
@@ -635,6 +657,7 @@
 $(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),$(hide) echo "cache_size=$(BOARD_CACHEIMAGE_PARTITION_SIZE)" >> $(1))
 $(if $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "vendor_fs_type=$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
 $(if $(BOARD_VENDORIMAGE_PARTITION_SIZE),$(hide) echo "vendor_size=$(BOARD_VENDORIMAGE_PARTITION_SIZE)" >> $(1))
+$(if $(BOARD_OEMIMAGE_PARTITION_SIZE),$(hide) echo "oem_size=$(BOARD_OEMIMAGE_PARTITION_SIZE)" >> $(1))
 $(if $(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG),$(hide) echo "extfs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG)" >> $(1))
 $(if $(mkyaffs2_extra_flags),$(hide) echo "mkyaffs2_extra_flags=$(mkyaffs2_extra_flags)" >> $(1))
 $(hide) echo "selinux_fc=$(SELINUX_FC)" >> $(1)
@@ -656,6 +679,23 @@
 recovery_binary := $(call intermediates-dir-for,EXECUTABLES,recovery)/recovery
 recovery_resources_common := $(call include-path-for, recovery)/res
 
+ifneq (,$(filter xxxhdpi,$(PRODUCT_AAPT_CONFIG_SP)))
+recovery_resources_common := $(recovery_resources_common)-xxxhdpi
+else ifneq (,$(filter xxhdpi,$(PRODUCT_AAPT_CONFIG_SP)))
+recovery_resources_common := $(recovery_resources_common)-xxhdpi
+else ifneq (,$(filter xhdpi,$(PRODUCT_AAPT_CONFIG_SP)))
+recovery_resources_common := $(recovery_resources_common)-xhdpi
+else ifneq (,$(filter hdpi,$(PRODUCT_AAPT_CONFIG_SP)))
+recovery_resources_common := $(recovery_resources_common)-hdpi
+else ifneq (,$(filter mdpi,$(PRODUCT_AAPT_CONFIG_SP)))
+recovery_resources_common := $(recovery_resources_common)-mdpi
+else
+# xhdpi is closest in size to the single set of resources we had
+# before, so make that the default if PRODUCT_AAPT_CONFIG doesn't
+# specify a dpi we have.
+recovery_resources_common := $(recovery_resources_common)-xhdpi
+endif
+
 # Select the 18x32 font on high-density devices; and the 12x22 font on
 # other devices.  Note that the font selected here can be overridden
 # for a particular device by putting a font.png in its private
@@ -740,7 +780,9 @@
 	$(hide) cp -f $(recovery_initrc) $(TARGET_RECOVERY_ROOT_OUT)/
 	$(hide) -cp $(TARGET_ROOT_OUT)/init.recovery.*.rc $(TARGET_RECOVERY_ROOT_OUT)/
 	$(hide) cp -f $(recovery_binary) $(TARGET_RECOVERY_ROOT_OUT)/sbin/
-	$(hide) cp -rf $(recovery_resources_common) $(TARGET_RECOVERY_ROOT_OUT)/
+	$(hide) mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/res
+	$(hide) rm -rf $(TARGET_RECOVERY_ROOT_OUT)/res/*
+	$(hide) cp -rf $(recovery_resources_common)/* $(TARGET_RECOVERY_ROOT_OUT)/res
 	$(hide) cp -f $(recovery_font) $(TARGET_RECOVERY_ROOT_OUT)/res/images/font.png
 	$(hide) $(foreach item,$(recovery_resources_private), \
 	  cp -rf $(item) $(TARGET_RECOVERY_ROOT_OUT)/)
@@ -822,7 +864,12 @@
 define build-systemimage-target
   @echo "Target system fs image: $(1)"
   @mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt
-  $(call generate-userimage-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt, skip_fsck=true)
+  $(call generate-userimage-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt, \
+      skip_fsck=true \
+      verity=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY) \
+      verity_block_device=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_PARTITION) \
+      verity_key=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY) \
+      verity_signer_cmd=$(VERITY_SIGNER))
   $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
       ./build/tools/releasetools/build_image.py \
       $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1)
@@ -1111,7 +1158,9 @@
 	  $(HOST_OUT_EXECUTABLES)/mkuserimg.sh \
 	  $(HOST_OUT_EXECUTABLES)/make_ext4fs \
 	  $(HOST_OUT_EXECUTABLES)/simg2img \
-	  $(HOST_OUT_EXECUTABLES)/e2fsck
+	  $(HOST_OUT_EXECUTABLES)/e2fsck \
+	  $(HOST_OUT_EXECUTABLES)/xdelta3 \
+	  $(HOST_OUT_EXECUTABLES)/syspatch_host
 
 OTATOOLS := $(DISTTOOLS) \
 	  $(HOST_OUT_EXECUTABLES)/aapt
@@ -1267,7 +1316,12 @@
 	$(hide) echo "use_set_metadata=1" >> $(zip_root)/META/misc_info.txt
 	$(hide) echo "multistage_support=1" >> $(zip_root)/META/misc_info.txt
 	$(hide) echo "update_rename_support=1" >> $(zip_root)/META/misc_info.txt
+ifneq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_OEM_PROPERTIES),)
+	# OTA scripts are only interested in fingerprint related properties
+	$(hide) echo "oem_fingerprint_properties=$(filter ro.product.brand ro.product.name ro.product.device, $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_OEM_PROPERTIES))" >> $(zip_root)/META/misc_info.txt
+endif
 	$(call generate-userimage-prop-dictionary, $(zip_root)/META/misc_info.txt)
+	$(hide) ./build/tools/releasetools/make_recovery_patch $(zip_root) $(zip_root)
 	@# Zip everything up, preserving symlinks
 	$(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
 	@# Run fs_config on all the system, boot ramdisk, and recovery ramdisk files in the zip, and save the output
@@ -1305,8 +1359,10 @@
 	@echo "Package OTA: $@"
 	$(hide) MKBOOTIMG=$(BOARD_CUSTOM_MKBOOTIMG) \
 	   ./build/tools/releasetools/ota_from_target_files -v \
+	   --block \
 	   -p $(HOST_OUT) \
 	   -k $(KEY_CERT_PAIR) \
+	   $(if $(OEM_OTA_CONFIG), -o $(OEM_OTA_CONFIG)) \
 	   $(BUILT_TARGET_FILES_PACKAGE) $@
 
 .PHONY: otapackage
@@ -1323,18 +1379,10 @@
 
 INTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 
-ifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),)
-# default to common dir for device vendor
-$(INTERNAL_UPDATE_PACKAGE_TARGET): extensions := $(TARGET_DEVICE_DIR)/../common
-else
-$(INTERNAL_UPDATE_PACKAGE_TARGET): extensions := $(TARGET_RELEASETOOLS_EXTENSIONS)
-endif
-
 $(INTERNAL_UPDATE_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(DISTTOOLS)
 	@echo "Package: $@"
 	$(hide) MKBOOTIMG=$(BOARD_CUSTOM_MKBOOTIMG) \
 	   ./build/tools/releasetools/img_from_target_files -v \
-	   -s $(extensions) \
 	   -p $(HOST_OUT) \
 	   $(BUILT_TARGET_FILES_PACKAGE) $@
 
@@ -1390,7 +1438,7 @@
 # the dependency will be set up later in build/core/main.mk.
 $(EMMA_META_ZIP) :
 	@echo "Collecting Emma coverage meta files."
-	$(hide) find $(TARGET_COMMON_OUT_ROOT) -name "coverage.em" | \
+	$(hide) find $(TARGET_COMMON_OUT_ROOT) $(HOST_COMMON_OUT_ROOT) -name "coverage.em" | \
 		zip -@ -q $@
 
 endif # EMMA_INSTRUMENT=true
@@ -1406,7 +1454,8 @@
 	@echo "Packaging Proguard obfuscation dictionary files."
 	$(hide) dict_files=`find $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS -name proguard_dictionary`; \
 		if [ -n "$$dict_files" ]; then \
-		  zip -q $@ $$dict_files; \
+		  unobfuscated_jars=$${dict_files//proguard_dictionary/classes.jar}; \
+		  zip -q $@ $$dict_files $$unobfuscated_jars; \
 		else \
 		  touch $(dir $@)/dummy; \
 		  (cd $(dir $@) && zip -q $(notdir $@) dummy); \
diff --git a/core/base_rules.mk b/core/base_rules.mk
index c68fba7..0a67d42 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -120,6 +120,8 @@
   else
   ifeq (true,$(LOCAL_PROPRIETARY_MODULE))
     partition_tag := _VENDOR
+  else ifeq (true,$(LOCAL_OEM_MODULE))
+    partition_tag := _OEM
   else
     # The definition of should-install-to-system will be different depending
     # on which goal (e.g., sdk or just droid) is being built.
@@ -429,10 +431,10 @@
 
 # This is set by packages that are linking to other packages that export
 # shared libraries, allowing them to make use of the code in the linked apk.
-LOCAL_APK_LIBRARIES := $(strip $(LOCAL_APK_LIBRARIES))
-ifdef LOCAL_APK_LIBRARIES
+apk_libraries := $(sort $(LOCAL_APK_LIBRARIES) $(LOCAL_RES_LIBRARIES))
+ifneq ($(apk_libraries),)
   link_apk_libraries := \
-      $(foreach lib,$(LOCAL_APK_LIBRARIES), \
+      $(foreach lib,$(apk_libraries), \
         $(call intermediates-dir-for, \
               APPS,$(lib),,COMMON)/classes.jar)
 
diff --git a/core/binary.mk b/core/binary.mk
index 9dab23f..d5709ca 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -141,6 +141,8 @@
 
 ifeq ($(strip $(LOCAL_ADDRESS_SANITIZER)),true)
   LOCAL_CLANG := true
+  # Frame pointer based unwinder in ASan requires ARM frame setup.
+  LOCAL_ARM_MODE := arm
   my_cflags += $(ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS)
   my_ldflags += $(ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS)
   my_shared_libraries += $(ADDRESS_SANITIZER_CONFIG_EXTRA_SHARED_LIBRARIES)
@@ -450,6 +452,9 @@
 proto_generated_objects := $(addprefix $(proto_generated_obj_dir)/, \
     $(patsubst %.proto,%.pb.o,$(proto_sources_fullpath)))
 
+# Auto-export the generated proto source dir.
+LOCAL_EXPORT_C_INCLUDE_DIRS += $(proto_generated_cc_sources_dir)
+
 # Ensure the transform-proto-to-cc rule is only defined once in multilib build.
 ifndef $(my_prefix)_$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_proto_defined
 $(proto_generated_cc_sources): PRIVATE_PROTO_INCLUDES := $(TOP)
@@ -459,6 +464,8 @@
 	$(transform-proto-to-cc)
 
 $(proto_generated_headers): $(proto_generated_cc_sources_dir)/%.pb.h: $(proto_generated_cc_sources_dir)/%.pb.cc
+	# This is just a dummy rule to make sure gmake doesn't skip updating the dependents.
+	@echo "Updated header file $<."
 
 $(my_prefix)_$(LOCAL_MODULE_CLASS)_$(LOCAL_MODULE)_proto_defined := true
 endif  # transform-proto-to-cc rule included only once
@@ -914,7 +921,8 @@
 ###########################################################
 export_includes := $(intermediates)/export_includes
 $(export_includes): PRIVATE_EXPORT_C_INCLUDE_DIRS := $(LOCAL_EXPORT_C_INCLUDE_DIRS)
-$(export_includes) : $(LOCAL_MODULE_MAKEFILE)
+# Make sure .pb.h are already generated before any dependent source files get compiled.
+$(export_includes) : $(LOCAL_MODULE_MAKEFILE) $(proto_generated_headers)
 	@echo Export includes file: $< -- $@
 	$(hide) mkdir -p $(dir $@) && rm -f $@
 ifdef LOCAL_EXPORT_C_INCLUDE_DIRS
diff --git a/core/build_id.mk b/core/build_id.mk
index aaf4185..221a2f3 100644
--- a/core/build_id.mk
+++ b/core/build_id.mk
@@ -18,6 +18,6 @@
 # (like "CRB01").  It must be a single word, and is
 # capitalized by convention.
 
-BUILD_ID := OPENMASTER
+BUILD_ID := MASTER
 
 DISPLAY_BUILD_NUMBER := true
diff --git a/core/clang/config.mk b/core/clang/config.mk
index c598e7d..c0be37a 100644
--- a/core/clang/config.mk
+++ b/core/clang/config.mk
@@ -69,9 +69,10 @@
 CLANG_CONFIG_EXTRA_TARGET_C_INCLUDES := $(LLVM_PREBUILTS_HEADER_PATH) $(TARGET_OUT_HEADERS)/clang
 
 # Address sanitizer clang config
-ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS := -fsanitize=address
+ADDRESS_SANITIZER_RUNTIME_LIBRARY := libclang_rt.asan_$(TARGET_ARCH)_android
+ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS := -fsanitize=address -fno-omit-frame-pointer
 ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS := -Wl,-u,__asan_preinit
-ADDRESS_SANITIZER_CONFIG_EXTRA_SHARED_LIBRARIES := libdl libasan_preload
+ADDRESS_SANITIZER_CONFIG_EXTRA_SHARED_LIBRARIES := libdl $(ADDRESS_SANITIZER_RUNTIME_LIBRARY)
 ADDRESS_SANITIZER_CONFIG_EXTRA_STATIC_LIBRARIES := libasan
 
 # This allows us to use the superset of functionality that compiler-rt
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index bf8ab08..1902d57 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -88,9 +88,11 @@
 LOCAL_STRIP_MODULE:=
 LOCAL_JNI_SHARED_LIBRARIES:=
 LOCAL_JNI_SHARED_LIBRARIES_ABI:=
+LOCAL_PREBUILT_JNI_LIBS:=
 LOCAL_JAR_MANIFEST:=
 LOCAL_INSTRUMENTATION_FOR:=
 LOCAL_APK_LIBRARIES:=
+LOCAL_RES_LIBRARIES:=
 LOCAL_MANIFEST_INSTRUMENTATION_FOR:=
 LOCAL_AIDL_INCLUDES:=
 LOCAL_JARJAR_RULES:=
@@ -126,6 +128,7 @@
 LOCAL_PROTO_JAVA_OUTPUT_PARAMS:=
 LOCAL_NO_CRT:=
 LOCAL_PROPRIETARY_MODULE:=
+LOCAL_OEM_MODULE:=
 LOCAL_PRIVILEGED_MODULE:=
 LOCAL_MODULE_OWNER:=
 LOCAL_CTS_TEST_PACKAGE:=
@@ -143,6 +146,7 @@
 LOCAL_POST_INSTALL_CMD:=
 LOCAL_DIST_BUNDLED_BINARIES:=
 LOCAL_HAL_STATIC_LIBRARIES:=
+LOCAL_RMTYPEDEFS:=
 LOCAL_NO_SYNTAX_CHECK:=
 LOCAL_NO_STATIC_ANALYZER:=
 LOCAL_32_BIT_ONLY:= # '',true
diff --git a/core/combo/TARGET_linux-arm.mk b/core/combo/TARGET_linux-arm.mk
index 8c8a01a..d882ea6 100644
--- a/core/combo/TARGET_linux-arm.mk
+++ b/core/combo/TARGET_linux-arm.mk
@@ -114,7 +114,7 @@
 # into no-op in some builds while mesg is defined earlier. So we explicitly
 # disable "-Wunused-but-set-variable" here.
 ifneq ($(filter 4.6 4.6.% 4.7 4.7.% 4.8, $($(combo_2nd_arch_prefix)TARGET_GCC_VERSION)),)
-$(combo_2nd_arch_prefix)TARGET_GLOBAL_CFLAGS += -Wno-unused-but-set-variable -fno-builtin-sin \
+$(combo_2nd_arch_prefix)TARGET_GLOBAL_CFLAGS += -fno-builtin-sin \
 			-fno-strict-volatile-bitfields
 endif
 
diff --git a/core/combo/include/arch/darwin-x86/AndroidConfig.h b/core/combo/include/arch/darwin-x86/AndroidConfig.h
index 44de4cd..54f3750 100644
--- a/core/combo/include/arch/darwin-x86/AndroidConfig.h
+++ b/core/combo/include/arch/darwin-x86/AndroidConfig.h
@@ -56,13 +56,6 @@
 #define HAVE_FORKEXEC
 
 /*
- * Process out-of-memory adjustment.  Set if running on Linux,
- * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
- * badness adjustment.
- */
-/* #define HAVE_OOM_ADJ */
-
-/*
  * IPC model.  Choose one:
  *
  * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
diff --git a/core/combo/include/arch/freebsd-x86/AndroidConfig.h b/core/combo/include/arch/freebsd-x86/AndroidConfig.h
index 0734661..4267508 100644
--- a/core/combo/include/arch/freebsd-x86/AndroidConfig.h
+++ b/core/combo/include/arch/freebsd-x86/AndroidConfig.h
@@ -61,13 +61,6 @@
 #define HAVE_FORKEXEC
 
 /*
- * Process out-of-memory adjustment.  Set if running on Linux,
- * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
- * badness adjustment.
- */
-/* #define HAVE_OOM_ADJ */
-
-/*
  * IPC model.  Choose one:
  *
  * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
diff --git a/core/combo/include/arch/linux-arm/AndroidConfig.h b/core/combo/include/arch/linux-arm/AndroidConfig.h
index 9253e7c..35eecfe 100644
--- a/core/combo/include/arch/linux-arm/AndroidConfig.h
+++ b/core/combo/include/arch/linux-arm/AndroidConfig.h
@@ -69,13 +69,6 @@
 #define HAVE_FORKEXEC
 
 /*
- * Process out-of-memory adjustment.  Set if running on Linux,
- * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
- * badness adjustment.
- */
-#define HAVE_OOM_ADJ
-
-/*
  * IPC model.  Choose one:
  *
  * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
diff --git a/core/combo/include/arch/linux-mips/AndroidConfig.h b/core/combo/include/arch/linux-mips/AndroidConfig.h
index 2758153..33472d2 100644
--- a/core/combo/include/arch/linux-mips/AndroidConfig.h
+++ b/core/combo/include/arch/linux-mips/AndroidConfig.h
@@ -69,13 +69,6 @@
 #define HAVE_FORKEXEC
 
 /*
- * Process out-of-memory adjustment.  Set if running on Linux,
- * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
- * badness adjustment.
- */
-#define HAVE_OOM_ADJ
-
-/*
  * IPC model.  Choose one:
  *
  * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
diff --git a/core/combo/include/arch/linux-ppc/AndroidConfig.h b/core/combo/include/arch/linux-ppc/AndroidConfig.h
index e6f9489..c68f354 100644
--- a/core/combo/include/arch/linux-ppc/AndroidConfig.h
+++ b/core/combo/include/arch/linux-ppc/AndroidConfig.h
@@ -56,13 +56,6 @@
 #define HAVE_FORKEXEC
 
 /*
- * Process out-of-memory adjustment.  Set if running on Linux,
- * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
- * badness adjustment.
- */
-#define HAVE_OOM_ADJ
-
-/*
  * IPC model.  Choose one:
  *
  * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
diff --git a/core/combo/include/arch/linux-x86/AndroidConfig.h b/core/combo/include/arch/linux-x86/AndroidConfig.h
index 0740186..79b9cef 100644
--- a/core/combo/include/arch/linux-x86/AndroidConfig.h
+++ b/core/combo/include/arch/linux-x86/AndroidConfig.h
@@ -56,13 +56,6 @@
 #define HAVE_FORKEXEC
 
 /*
- * Process out-of-memory adjustment.  Set if running on Linux,
- * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
- * badness adjustment.
- */
-#define HAVE_OOM_ADJ
-
-/*
  * IPC model.  Choose one:
  *
  * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
diff --git a/core/combo/include/arch/target_linux-x86/AndroidConfig.h b/core/combo/include/arch/target_linux-x86/AndroidConfig.h
index f55134a..d44a317 100644
--- a/core/combo/include/arch/target_linux-x86/AndroidConfig.h
+++ b/core/combo/include/arch/target_linux-x86/AndroidConfig.h
@@ -55,13 +55,6 @@
 #define HAVE_FORKEXEC
 
 /*
- * Process out-of-memory adjustment.  Set if running on Linux,
- * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
- * badness adjustment.
- */
-#define HAVE_OOM_ADJ
-
-/*
  * IPC model.  Choose one:
  *
  * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
diff --git a/core/combo/include/arch/windows/AndroidConfig.h b/core/combo/include/arch/windows/AndroidConfig.h
index 0a52674..204740d 100644
--- a/core/combo/include/arch/windows/AndroidConfig.h
+++ b/core/combo/include/arch/windows/AndroidConfig.h
@@ -83,13 +83,6 @@
 #endif
 
 /*
- * Process out-of-memory adjustment.  Set if running on Linux,
- * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
- * badness adjustment.
- */
-/* #define HAVE_OOM_ADJ */
-
-/*
  * IPC model.  Choose one:
  *
  * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
diff --git a/core/config.mk b/core/config.mk
index 31833fb..61f1d79 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -37,8 +37,7 @@
 	$(TOPDIR)frameworks/native/include \
 	$(TOPDIR)frameworks/native/opengl/include \
 	$(TOPDIR)frameworks/av/include \
-	$(TOPDIR)frameworks/base/include \
-	$(TOPDIR)external/skia/include
+	$(TOPDIR)frameworks/base/include
 SRC_HOST_HEADERS:=$(TOPDIR)tools/include
 SRC_LIBRARIES:= $(TOPDIR)libs
 SRC_SERVERS:= $(TOPDIR)servers
@@ -390,6 +389,10 @@
 LLVM_RS_CC := $(HOST_OUT_EXECUTABLES)/llvm-rs-cc$(HOST_EXECUTABLE_SUFFIX)
 BCC_COMPAT := $(HOST_OUT_EXECUTABLES)/bcc_compat$(HOST_EXECUTABLE_SUFFIX)
 LINT := prebuilts/sdk/tools/lint
+RMTYPEDEFS := $(HOST_OUT_EXECUTABLES)/rmtypedefs
+APPEND2SIMG := $(HOST_OUT_EXECUTABLES)/append2simg
+VERITY_SIGNER := $(HOST_OUT_EXECUTABLES)/verity_signer
+BUILD_VERITY_TREE := $(HOST_OUT_EXECUTABLES)/build_verity_tree
 
 # ACP is always for the build OS, not for the host OS
 ACP := $(BUILD_OUT_EXECUTABLES)/acp$(BUILD_EXECUTABLE_SUFFIX)
@@ -561,6 +564,7 @@
     $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/android.jar)))
 
 INTERNAL_PLATFORM_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/public_api.txt
+INTERNAL_PLATFORM_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/removed.txt
 
 # This is the standard way to name a directory containing prebuilt target
 # objects. E.g., prebuilt/$(TARGET_PREBUILT_TAG)/libc.so
diff --git a/core/definitions.mk b/core/definitions.mk
index e9871d6..8f8aa4d 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -1553,7 +1553,7 @@
 $(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)
 $(hide) if [ -d "$(PRIVATE_SOURCE_INTERMEDIATES_DIR)" ]; then \
-	    find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list; \
+          find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(PRIVATE_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
@@ -1582,8 +1582,13 @@
     $(foreach pkg, $(PRIVATE_JAR_PACKAGES), \
         -not -path $(PRIVATE_CLASS_INTERMEDIATES_DIR)/$(subst .,/,$(pkg))) \
     | xargs rm -rf)
-$(hide) jar $(if $(strip $(PRIVATE_JAR_MANIFEST)),-cfm,-cf) \
-    $@ $(PRIVATE_JAR_MANIFEST) -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .
+$(if $(PRIVATE_RMTYPEDEFS), $(hide) $(RMTYPEDEFS) -v $(PRIVATE_CLASS_INTERMEDIATES_DIR))
+$(if $(PRIVATE_JAR_MANIFEST), \
+    $(hide) sed -e 's/%BUILD_NUMBER%/$(BUILD_NUMBER)/' \
+            $(PRIVATE_JAR_MANIFEST) > $(dir $@)/manifest.mf && \
+        jar -cfm $@ $(dir $@)/manifest.mf \
+            -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) ., \
+    $(hide) jar -cf $@ -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .)
 endef
 
 define transform-java-to-classes.jar
@@ -1633,8 +1638,13 @@
     $(foreach pkg, $(PRIVATE_JAR_PACKAGES), \
         -not -path $(PRIVATE_CLASS_INTERMEDIATES_DIR)/$(subst .,/,$(pkg))) \
     | xargs rm -rf)
-$(hide) jar $(if $(strip $(PRIVATE_JAR_MANIFEST)),-cfm,-cf) \
-    $@ $(PRIVATE_JAR_MANIFEST) -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .
+$(if $(PRIVATE_RMTYPEDEFS), $(hide) $(RMTYPEDEFS) -v $(PRIVATE_CLASS_INTERMEDIATES_DIR))
+$(if $(PRIVATE_JAR_MANIFEST), \
+    $(hide) sed -e 's/%BUILD_NUMBER%/$(BUILD_NUMBER)/' \
+            $(PRIVATE_JAR_MANIFEST) > $(dir $@)/manifest.mf && \
+        jar -cfm $@ $(dir $@)/manifest.mf \
+            -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) ., \
+    $(hide) jar -cf $@ -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .)
 $(hide) mv $(PRIVATE_CLASS_INTERMEDIATES_DIR)/newstamp $(PRIVATE_CLASS_INTERMEDIATES_DIR)/stamp
 endef
 
@@ -1656,9 +1666,10 @@
 define transform-classes.jar-to-dex
 @echo "target Dex: $(PRIVATE_MODULE)"
 @mkdir -p $(dir $@)
+$(hide) rm -f $(dir $@)/classes*.dex
 $(hide) $(DX) \
     $(if $(findstring windows,$(HOST_OS)),,-JXms16M -JXmx2048M) \
-    --dex --output=$@ \
+    --dex --output=$(dir $@) \
     $(incremental_dex) \
     $(if $(NO_OPTIMIZE_DX), \
         --no-optimize) \
@@ -1718,11 +1729,7 @@
 
 #TODO: update the manifest to point to the dex file
 define add-dex-to-package
-$(if $(filter classes.dex,$(notdir $(PRIVATE_DEX_FILE))),\
-$(hide) zip -qj $@ $(PRIVATE_DEX_FILE),\
-$(hide) _adtp_classes_dex=$(dir $(PRIVATE_DEX_FILE))classes.dex; \
-cp $(PRIVATE_DEX_FILE) $$_adtp_classes_dex && \
-zip -qj $@ $$_adtp_classes_dex && rm -f $$_adtp_classes_dex)
+$(hide) zip -qj $@ $(dir $(PRIVATE_DEX_FILE))/classes*.dex
 endef
 
 # Add java resources added by the current module.
@@ -2129,17 +2136,19 @@
 #    $(1)  target
 #    $(2)  stable api file
 #    $(3)  api file to be tested
-#    $(4)  arguments for apicheck
-#    $(5)  command to run if apicheck failed
-#    $(6)  target dependent on this api check
-#    $(7)  additional dependencies
+#    $(4)  stable removed api file
+#    $(5)  removed api file to be tested
+#    $(6)  arguments for apicheck
+#    $(7)  command to run if apicheck failed
+#    $(8)  target dependent on this api check
+#    $(9)  additional dependencies
 define check-api
-$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(strip $(1))-timestamp: $(2) $(3) $(APICHECK) $(7)
+$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(strip $(1))-timestamp: $(2) $(3) $(4) $(APICHECK) $(9)
 	@echo "Checking API:" $(1)
-	$(hide) ( $(APICHECK_COMMAND) $(4) $(2) $(3) || ( $(5) ; exit 38 ) )
+	$(hide) ( $(APICHECK_COMMAND) $(6) $(2) $(3) $(4) $(5) || ( $(7) ; exit 38 ) )
 	$(hide) mkdir -p $$(dir $$@)
 	$(hide) touch $$@
-$(6): $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(strip $(1))-timestamp
+$(8): $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(strip $(1))-timestamp
 endef
 
 ## Whether to build from source if prebuilt alternative exists
diff --git a/core/droiddoc.mk b/core/droiddoc.mk
index d5ddf08..2a9cfc6 100644
--- a/core/droiddoc.mk
+++ b/core/droiddoc.mk
@@ -131,15 +131,13 @@
 $(full_target): PRIVATE_IN_CUSTOM_ASSET_DIR := $(LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR)/$(LOCAL_DROIDDOC_CUSTOM_ASSET_DIR)
 $(full_target): PRIVATE_OUT_ASSET_DIR := $(out_dir)/$(LOCAL_DROIDDOC_ASSET_DIR)
 $(full_target): PRIVATE_OUT_CUSTOM_ASSET_DIR := $(out_dir)/$(LOCAL_DROIDDOC_CUSTOM_ASSET_DIR)
+
+html_dir_files :=
 ifneq ($(strip $(LOCAL_DROIDDOC_HTML_DIR)),)
 $(full_target): PRIVATE_DROIDDOC_HTML_DIR := -htmldir $(LOCAL_PATH)/$(LOCAL_DROIDDOC_HTML_DIR)
+html_dir_files := $(shell find $(LOCAL_PATH)/$(LOCAL_DROIDDOC_HTML_DIR) -type f)
 else
-$(full_target): PRIVATE_DROIDDOC_HTML_DIR := 
-endif
-ifneq ($(strip $(LOCAL_ADDITIONAL_HTML_DIR)),)
-$(full_target): PRIVATE_ADDITIONAL_HTML_DIR := -htmldir2 $(LOCAL_PATH)/$(LOCAL_ADDITIONAL_HTML_DIR)
-else
-$(full_target): PRIVATE_ADDITIONAL_HTML_DIR :=
+$(full_target): PRIVATE_DROIDDOC_HTML_DIR :=
 endif
 ifneq ($(strip $(LOCAL_ADDITIONAL_HTML_DIR)),)
 $(full_target): PRIVATE_ADDITIONAL_HTML_DIR := -htmldir2 $(LOCAL_PATH)/$(LOCAL_ADDITIONAL_HTML_DIR)
@@ -150,8 +148,6 @@
 # TODO: not clear if this is used any more
 $(full_target): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
 
-html_dir_files := $(shell find $(LOCAL_PATH)/$(LOCAL_DROIDDOC_HTML_DIR) -type f)
-
 $(full_target): $(full_src_files) $(droiddoc_templates) $(droiddoc) $(html_dir_files) $(full_java_lib_deps) $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	@echo Docs droiddoc: $(PRIVATE_OUT_DIR)
 	$(hide) mkdir -p $(dir $@)
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 4cb389b..88f35a6 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -107,6 +107,7 @@
 TARGET_COPY_OUT_SYSTEM := system
 TARGET_COPY_OUT_DATA := data
 TARGET_COPY_OUT_VENDOR := system/vendor
+TARGET_COPY_OUT_OEM := oem
 TARGET_COPY_OUT_ROOT := root
 TARGET_COPY_OUT_RECOVERY := recovery
 
@@ -207,6 +208,7 @@
 HOST_OUT_INTERMEDIATE_LIBRARIES := $(HOST_OUT_INTERMEDIATES)/lib
 HOST_OUT_NOTICE_FILES := $(HOST_OUT_INTERMEDIATES)/NOTICE_FILES
 HOST_OUT_COMMON_INTERMEDIATES := $(HOST_COMMON_OUT_ROOT)/obj
+HOST_OUT_FAKE := $(HOST_OUT)/fake_packages
 
 HOST_OUT_GEN := $(HOST_OUT)/gen
 HOST_OUT_COMMON_GEN := $(HOST_COMMON_OUT_ROOT)/gen
@@ -281,6 +283,14 @@
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES := $(TARGET_OUT_VENDOR)/lib
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_APPS := $(TARGET_OUT_VENDOR_APPS)
 
+TARGET_OUT_OEM := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_OEM)
+TARGET_OUT_OEM_EXECUTABLES:= $(TARGET_OUT_OEM)/bin
+TARGET_OUT_OEM_SHARED_LIBRARIES:= $(TARGET_OUT_OEM)/lib
+# We don't expect Java libraries in the oem.img.
+# TARGET_OUT_OEM_JAVA_LIBRARIES:= $(TARGET_OUT_OEM)/framework
+TARGET_OUT_OEM_APPS:= $(TARGET_OUT_OEM)/app
+TARGET_OUT_OEM_ETC := $(TARGET_OUT_OEM)/etc
+
 TARGET_OUT_UNSTRIPPED := $(PRODUCT_OUT)/symbols
 TARGET_OUT_EXECUTABLES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/bin
 TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/lib
diff --git a/core/host_java_library.mk b/core/host_java_library.mk
index e5ebb11..2a8f18f 100644
--- a/core/host_java_library.mk
+++ b/core/host_java_library.mk
@@ -20,21 +20,64 @@
 
 #######################################
 include $(BUILD_SYSTEM)/host_java_library_common.mk
+#######################################
+
+# Enable emma instrumentation only if the module asks so.
+ifeq (true,$(LOCAL_EMMA_INSTRUMENT))
+ifneq (true,$(EMMA_INSTRUMENT))
+LOCAL_EMMA_INSTRUMENT :=
+endif
+endif
+
+full_classes_compiled_jar := $(intermediates.COMMON)/classes-full-debug.jar
+emma_intermediates_dir := $(intermediates.COMMON)/emma_out
+# emma is hardcoded to use the leaf name of its input for the output file --
+# only the output directory can be changed
+full_classes_emma_jar := $(emma_intermediates_dir)/lib/$(notdir $(full_classes_compiled_jar))
+
+LOCAL_INTERMEDIATE_TARGETS += \
+    $(full_classes_compiled_jar) \
+    $(full_classes_emma_jar)
+
+#######################################
 include $(BUILD_SYSTEM)/base_rules.mk
 #######################################
 
-$(full_classes_compiled_jar): PRIVATE_JAVAC_DEBUG_FLAGS := -g
+ifeq (true,$(LOCAL_EMMA_INSTRUMENT))
+$(full_classes_emma_jar): PRIVATE_EMMA_COVERAGE_FILE := $(intermediates.COMMON)/coverage.em
+$(full_classes_emma_jar): PRIVATE_EMMA_INTERMEDIATES_DIR := $(emma_intermediates_dir)
+ifdef LOCAL_EMMA_COVERAGE_FILTER
+$(full_classes_emma_jar): PRIVATE_EMMA_COVERAGE_FILTER := $(LOCAL_EMMA_COVERAGE_FILTER)
+else
+# by default, avoid applying emma instrumentation onto emma classes itself,
+# otherwise there will be exceptions thrown
+$(full_classes_emma_jar): PRIVATE_EMMA_COVERAGE_FILTER := *,-emma,-emmarun,-com.vladium.*
+endif
+# this rule will generate both $(PRIVATE_EMMA_COVERAGE_FILE) and
+# $(full_classes_emma_jar)
+$(full_classes_emma_jar) : $(full_classes_compiled_jar) | $(EMMA_JAR)
+	$(transform-classes.jar-to-emma)
 
-java_alternative_checked_module :=
+$(LOCAL_BUILT_MODULE) : $(full_classes_emma_jar)
+	@echo Copying: $@
+	$(hide) $(ACP) -fp $< $@
+
+else # LOCAL_EMMA_INSTRUMENT
+# Directly build into LOCAL_BUILT_MODULE.
+full_classes_compiled_jar := $(LOCAL_BUILT_MODULE)
+endif # LOCAL_EMMA_INSTRUMENT
+
+$(full_classes_compiled_jar): PRIVATE_JAVAC_DEBUG_FLAGS := -g
 
 # The layers file allows you to enforce a layering between java packages.
 # Run build/tools/java-layers.py for more details.
 layers_file := $(addprefix $(LOCAL_PATH)/, $(LOCAL_JAVA_LAYERS_FILE))
 
-$(LOCAL_BUILT_MODULE): PRIVATE_JAVA_LAYERS_FILE := $(layers_file)
-$(LOCAL_BUILT_MODULE): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS)
-$(LOCAL_BUILT_MODULE): PRIVATE_JAR_EXCLUDE_FILES :=
-$(LOCAL_BUILT_MODULE): PRIVATE_JAVA_LAYERS_FILE := $(layers_file)
-$(LOCAL_BUILT_MODULE): $(java_sources) $(java_resource_sources) $(full_java_lib_deps) \
+$(full_classes_compiled_jar): PRIVATE_JAVA_LAYERS_FILE := $(layers_file)
+$(full_classes_compiled_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS)
+$(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_FILES :=
+$(full_classes_compiled_jar): PRIVATE_JAR_PACKAGES :=
+$(full_classes_compiled_jar): PRIVATE_RMTYPEDEFS :=
+$(full_classes_compiled_jar): $(java_sources) $(java_resource_sources) $(full_java_lib_deps) \
 		$(jar_manifest_file) $(proto_java_sources_file_stamp) $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-host-java-to-package)
diff --git a/core/install_jni_libs.mk b/core/install_jni_libs.mk
index 464a2a2..b9e70b1 100644
--- a/core/install_jni_libs.mk
+++ b/core/install_jni_libs.mk
@@ -5,9 +5,11 @@
 #   rs_compatibility_jni_libs (from java.mk)
 #   my_module_path (from base_rules.mk)
 #   partition_tag (from base_rules.mk)
+#   my_prebuilt_src_file (from prebuilt_internal.mk)
 #
 # Output variables:
-#   jni_shared_libraries, jni_shared_libraries_abi, if we are going to embed the libraries into the apk.
+#   jni_shared_libraries, jni_shared_libraries_abi, if we are going to embed the libraries into the apk;
+#   my_extracted_jni_libs, if we extract jni libs from prebuilt apk.
 #
 
 jni_shared_libraries := \
@@ -33,6 +35,10 @@
 my_embed_jni := true
 endif
 
+# App-specific lib path.
+my_app_lib_path :=  $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET$(partition_tag)_OUT_SHARED_LIBRARIES)/$(basename $(LOCAL_INSTALLED_MODULE_STEM))
+my_extracted_jni_libs :=
+
 ifdef my_embed_jni
 # App explicitly requires the prebuilt NDK stl shared libraies.
 # The NDK stl shared libraries should never go to the system image.
@@ -74,12 +80,42 @@
 else
 my_leading_separator :=
 endif
-my_app_lib_path :=  $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET$(partition_tag)_OUT_SHARED_LIBRARIES)/$(basename $(LOCAL_INSTALLED_MODULE_STEM))
 $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD += \
   $(my_leading_separator)mkdir -p $(my_app_lib_path) \
   $(foreach lib, $(my_jni_filenames), ;ln -sf ../$(lib) $(my_app_lib_path)/$(lib))
 
 # Clear jni_shared_libraries to not embed it into the apk.
 jni_shared_libraries :=
-endif # $(jni_shared_libraries) not empty
+endif  # $(jni_shared_libraries) not empty
 endif  # my_embed_jni
+
+ifdef LOCAL_PREBUILT_JNI_LIBS
+# Install prebuilt JNI libs to the app specific lib path.
+# Files like @path/to/libfoo.so (path inside the apk) are JNI libs extracted from the prebuilt apk;
+# Files like path/to/libfoo.so (path relative to LOCAL_PATH) are prebuilts in the source tree.
+my_extracted_jni_libs := $(patsubst @%,%, \
+    $(filter @%, $(LOCAL_PREBUILT_JNI_LIBS)))
+ifdef my_extracted_jni_libs
+ifndef my_prebuilt_src_file
+$(error No prebuilt apk to extract prebuilt jni libraries $(my_extracted_jni_libs))
+endif
+# We use the first jni lib file as dependency.
+my_installed_prebuilt_jni := $(my_app_lib_path)/$(notdir $(firstword $(my_extracted_jni_libs)))
+$(my_installed_prebuilt_jni): PRIVATE_JNI_LIBS := $(my_extracted_jni_libs)
+$(my_installed_prebuilt_jni): $(my_prebuilt_src_file)
+	@echo "Extract JNI libs ($@ <- $<)"
+	@mkdir -p $(dir $@)
+	$(hide) unzip -j -o -d $(dir $@) $< $(PRIVATE_JNI_LIBS) && touch $@
+
+$(LOCAL_INSTALLED_MODULE) : | $(my_installed_prebuilt_jni)
+endif
+
+my_prebulit_jni_libs := $(addprefix $(LOCAL_PATH)/, \
+    $(filter-out @%, $(LOCAL_PREBUILT_JNI_LIBS)))
+ifdef my_prebulit_jni_libs
+$(foreach lib, $(my_prebulit_jni_libs), \
+    $(eval $(call copy-one-file, $(lib), $(my_app_lib_path)/$(notdir $(lib)))))
+
+$(LOCAL_INSTALLED_MODULE) : | $(addprefix $(my_app_lib_path)/, $(notdir $(my_prebulit_jni_libs)))
+endif
+endif  # LOCAL_PREBULT_JNI_LIBS
diff --git a/core/java.mk b/core/java.mk
index 8863ac2..824ae6a 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -72,10 +72,10 @@
 # Choose leaf name for the compiled jar file.
 ifeq ($(LOCAL_EMMA_INSTRUMENT),true)
 full_classes_compiled_jar_leaf := classes-no-debug-var.jar
-built_dex_intermediate_leaf := classes-no-local.dex
+built_dex_intermediate_leaf := no-local
 else
 full_classes_compiled_jar_leaf := classes-full-debug.jar
-built_dex_intermediate_leaf := classes-with-local.dex
+built_dex_intermediate_leaf := with-local
 endif
 
 ifeq ($(LOCAL_PROGUARD_ENABLED),disabled)
@@ -96,7 +96,7 @@
 # only the output directory can be changed
 full_classes_emma_jar := $(emma_intermediates_dir)/lib/$(jarjar_leaf)
 full_classes_proguard_jar := $(intermediates.COMMON)/$(proguard_jar_leaf)
-built_dex_intermediate := $(intermediates.COMMON)/$(built_dex_intermediate_leaf)
+built_dex_intermediate := $(intermediates.COMMON)/$(built_dex_intermediate_leaf)/classes.dex
 full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar
 
 ifeq ($(LOCAL_MODULE_CLASS)$(LOCAL_SRC_FILES)$(LOCAL_STATIC_JAVA_LIBRARIES)$(LOCAL_SOURCE_FILES_ALL_GENERATED),APPS)
@@ -321,6 +321,10 @@
 $(full_classes_compiled_jar): PRIVATE_JAVA_LAYERS_FILE := $(layers_file)
 $(full_classes_compiled_jar): PRIVATE_WARNINGS_ENABLE := $(LOCAL_WARNINGS_ENABLE)
 
+ifdef LOCAL_RMTYPEDEFS
+$(full_classes_compiled_jar): | $(RMTYPEDEFS)
+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,
@@ -328,6 +332,7 @@
 $(full_classes_compiled_jar): PRIVATE_JAVACFLAGS := $(LOCAL_JAVACFLAGS)
 $(full_classes_compiled_jar): PRIVATE_JAR_EXCLUDE_FILES := $(LOCAL_JAR_EXCLUDE_FILES)
 $(full_classes_compiled_jar): PRIVATE_JAR_PACKAGES := $(LOCAL_JAR_PACKAGES)
+$(full_classes_compiled_jar): PRIVATE_RMTYPEDEFS := $(LOCAL_RMTYPEDEFS)
 $(full_classes_compiled_jar): PRIVATE_DONT_DELETE_JAR_META_INF := $(LOCAL_DONT_DELETE_JAR_META_INF)
 $(full_classes_compiled_jar): $(java_sources) $(java_resource_sources) $(full_java_lib_deps) \
         $(jar_manifest_file) $(layers_file) $(RenderScript_file_stamp) \
@@ -470,7 +475,9 @@
 	$(transform-classes.jar-to-dex)
 $(built_dex): $(built_dex_intermediate) | $(ACP)
 	@echo Copying: $@
-	$(hide) $(ACP) -fp $< $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) rm -f $(dir $@)/classes*.dex
+	$(hide) $(ACP) -fp $(dir $<)/classes*.dex $(dir $@)
 ifneq ($(GENERATE_DEX_DEBUG),)
 	$(install-dex-debug)
 endif
diff --git a/core/main.mk b/core/main.mk
index cee8542..21a2fe3 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -189,12 +189,8 @@
 # java version is really openjdk
 ifeq ($(shell echo '$(java_version_str)' | grep -i openjdk),)
 $(info ************************************************************)
-$(info You are attempting to build with an unsupported JDK.)
-$(info $(space))
-$(info This build requires OpenJDK, but you are using:)
+$(info You asked for an OpenJDK 7 build but your version is)
 $(info $(java_version_str).)
-$(info Please follow the machine setup instructions at)
-$(info $(space)$(space)$(space)$(space)https://source.android.com/source/download.html)
 $(info ************************************************************)
 $(error stop)
 endif # java version is not OpenJdk
@@ -393,7 +389,7 @@
 sdk_repo_goal := $(strip $(filter sdk_repo,$(MAKECMDGOALS)))
 MAKECMDGOALS := $(strip $(filter-out sdk_repo,$(MAKECMDGOALS)))
 
-ifneq ($(words $(filter-out $(INTERNAL_MODIFIER_TARGETS) checkbuild target-files-package,$(MAKECMDGOALS))),1)
+ifneq ($(words $(filter-out $(INTERNAL_MODIFIER_TARGETS) checkbuild emulator_tests target-files-package,$(MAKECMDGOALS))),1)
 $(error The 'sdk' target may not be specified with any other targets)
 endif
 
@@ -956,6 +952,7 @@
   $(foreach f,$(INSTALLED_RADIOIMAGE_TARGET), \
     $(call dist-for-goals, droidcore, $(f)))
 
+  ifneq ($(ANDROID_BUILD_EMBEDDED),true)
   ifneq ($(TARGET_BUILD_PDK),true)
     $(call dist-for-goals, droidcore, \
       $(APPS_ZIP) \
@@ -963,6 +960,7 @@
       $(PACKAGE_STATS_FILE) \
     )
   endif
+  endif
 
   ifeq ($(EMMA_INSTRUMENT),true)
     $(EMMA_META_ZIP) : $(INSTALLED_SYSTEMIMAGE)
@@ -1026,7 +1024,7 @@
 
 .PHONY: clean
 clean:
-	@rm -rf $(OUT_DIR)
+	@rm -rf $(OUT_DIR)/*
 	@echo "Entire build directory removed."
 
 .PHONY: clobber
diff --git a/core/package_internal.mk b/core/package_internal.mk
index 5771e98..598c065 100644
--- a/core/package_internal.mk
+++ b/core/package_internal.mk
@@ -233,6 +233,7 @@
 
 $(proguard_options_file): $(R_file_stamp)
 
+resource_export_package :=
 ifdef LOCAL_EXPORT_PACKAGE_RESOURCES
 # Put this module's resources into a PRODUCT-agnositc package that
 # other packages can use to build their own PRODUCT-agnostic R.java (etc.)
@@ -286,9 +287,19 @@
 framework_res_package_export_deps := \
     $(dir $(framework_res_package_export))src/R.stamp
 endif # LOCAL_SDK_RES_VERSION
-$(R_file_stamp): $(framework_res_package_export_deps)
+all_library_res_package_exports := \
+    $(framework_res_package_export) \
+    $(foreach lib,$(LOCAL_RES_LIBRARIES),\
+        $(call intermediates-dir-for,APPS,$(lib),,COMMON)/package-export.apk)
+
+all_library_res_package_export_deps := \
+    $(framework_res_package_export_deps) \
+    $(foreach lib,$(LOCAL_RES_LIBRARIES),\
+        $(call intermediates-dir-for,APPS,$(lib),,COMMON)/src/R.stamp)
+
+$(resource_export_package) $(R_file_stamp): $(all_library_res_package_export_deps)
 $(LOCAL_INTERMEDIATE_TARGETS): \
-    PRIVATE_AAPT_INCLUDES := $(framework_res_package_export)
+    PRIVATE_AAPT_INCLUDES := $(all_library_res_package_exports)
 endif # LOCAL_NO_STANDARD_LIBRARIES
 
 ifneq ($(full_classes_jar),)
diff --git a/core/pathmap.mk b/core/pathmap.mk
index ee79647..aaeb905 100644
--- a/core/pathmap.mk
+++ b/core/pathmap.mk
@@ -29,10 +29,8 @@
 pathmap_INCL := \
     bootloader:bootable/bootloader/legacy/include \
     camera:system/media/camera/include \
-    corecg:external/skia/include/core \
     frameworks-base:frameworks/base/include \
     frameworks-native:frameworks/native/include \
-    graphics:external/skia/include/core \
     libc:bionic/libc/include \
     libhardware:hardware/libhardware/include \
     libhardware_legacy:hardware/libhardware_legacy/include \
@@ -51,6 +49,7 @@
     audio-route:system/media/audio_route/include \
     wilhelm:frameworks/wilhelm/include \
     wilhelm-ut:frameworks/wilhelm/src/ut \
+    mediandk:frameworks/av/media/ndk/ \
     speex:external/speex/include
 
 #
@@ -116,15 +115,24 @@
         v17/leanback
 
 #
+# A list of all source roots under frameworks/support.
+#
+FRAMEWORKS_MULTIDEX_SUBDIRS := \
+        multidex \
+        multidex/instrumentation
+
+#
 # A version of FRAMEWORKS_SUPPORT_SUBDIRS that is expanded to full paths from
 # the root of the tree.
 #
 FRAMEWORKS_SUPPORT_JAVA_SRC_DIRS := \
-	$(addprefix frameworks/support/,$(FRAMEWORKS_SUPPORT_SUBDIRS))
+	$(addprefix frameworks/support/,$(FRAMEWORKS_SUPPORT_SUBDIRS)) \
+        $(addprefix frameworks/,$(FRAMEWORKS_MULTIDEX_SUBDIRS))
 
 #
 # A list of support library modules.
 #
 FRAMEWORKS_SUPPORT_JAVA_LIBRARIES := \
-    $(foreach dir,$(FRAMEWORKS_SUPPORT_SUBDIRS),android-support-$(subst /,-,$(dir)))
+    $(foreach dir,$(FRAMEWORKS_SUPPORT_SUBDIRS),android-support-$(subst /,-,$(dir))) \
+    $(foreach dir,$(FRAMEWORKS_MULTIDEX_SUBDIRS),android-support-$(subst /,-,$(dir)))
 
diff --git a/core/pdk_config.mk b/core/pdk_config.mk
index b0cccc9..f5fdd15 100644
--- a/core/pdk_config.mk
+++ b/core/pdk_config.mk
@@ -18,9 +18,14 @@
 
 # if PDK_FUSION_PLATFORM_ZIP is specified, do not override.
 ifndef PDK_FUSION_PLATFORM_ZIP
+# Most PDK project paths should be using vendor/pdk/TARGET_DEVICE
+# but some legacy ones (e.g. mini_armv7a_neon generic PDK) were setup
+# with vendor/pdk/TARGET_PRODUCT.
 _pdk_fusion_default_platform_zip = $(wildcard \
 vendor/pdk/$(TARGET_DEVICE)/$(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT)/platform/platform.zip \
-vendor/pdk/$(TARGET_DEVICE)/$(patsubst aosp_%,full_%,$(TARGET_PRODUCT))-$(TARGET_BUILD_VARIANT)/platform/platform.zip)
+vendor/pdk/$(TARGET_DEVICE)/$(patsubst aosp_%,full_%,$(TARGET_PRODUCT))-$(TARGET_BUILD_VARIANT)/platform/platform.zip \
+vendor/pdk/$(TARGET_PRODUCT)/$(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT)/platform/platform.zip \
+vendor/pdk/$(TARGET_PRODUCT)/$(patsubst aosp_%,full_%,$(TARGET_PRODUCT))-$(TARGET_BUILD_VARIANT)/platform/platform.zip)
 ifneq (,$(_pdk_fusion_default_platform_zip))
 PDK_FUSION_PLATFORM_ZIP := $(word 1, $(_pdk_fusion_default_platform_zip))
 TARGET_BUILD_PDK := true
diff --git a/core/prebuilt_internal.mk b/core/prebuilt_internal.mk
index 8efb89e..bf519ee 100644
--- a/core/prebuilt_internal.mk
+++ b/core/prebuilt_internal.mk
@@ -105,6 +105,7 @@
 
 endif  # LOCAL_STRIP_MODULE not true
 
+ifeq ($(LOCAL_MODULE_CLASS),APPS)
 PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES))
 
 rs_compatibility_jni_libs :=
@@ -124,11 +125,9 @@
   $(built_module) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem
 endif
 ifeq ($(LOCAL_CERTIFICATE),)
-  ifneq ($(filter APPS,$(LOCAL_MODULE_CLASS)),)
-    # It is now a build error to add a prebuilt .apk without
-    # specifying a key for it.
-    $(error No LOCAL_CERTIFICATE specified for prebuilt "$(my_prebuilt_src_file)")
-  endif
+  # It is now a build error to add a prebuilt .apk without
+  # specifying a key for it.
+  $(error No LOCAL_CERTIFICATE specified for prebuilt "$(my_prebuilt_src_file)")
 else ifeq ($(LOCAL_CERTIFICATE),PRESIGNED)
   # The magic string "PRESIGNED" means this package is already checked
   # signed with its release key.
@@ -152,8 +151,6 @@
   $(built_module) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem
 endif
 
-ifneq ($(filter APPS,$(LOCAL_MODULE_CLASS)),)
-
 # Disable dex-preopt of prebuilts to save space
 LOCAL_DEX_PREOPT := false
 
@@ -175,6 +172,9 @@
 # Sign and align non-presigned .apks.
 $(built_module) : $(my_prebuilt_src_file) | $(ACP) $(ZIPALIGN) $(SIGNAPK_JAR)
 	$(transform-prebuilt-to-target)
+ifdef my_extracted_jni_libs
+	$(hide) zip -d $@ 'lib/*.so'  # strip embedded JNI libraries.
+endif
 ifneq ($(LOCAL_CERTIFICATE),PRESIGNED)
 	$(sign-package)
 endif
diff --git a/core/product.mk b/core/product.mk
index 174b429..1a7685c 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -100,6 +100,11 @@
     PRODUCT_RUNTIMES \
     PRODUCT_BOOT_JARS \
     PRODUCT_DEX_PREOPT_IMAGE_IN_DATA \
+    PRODUCT_SUPPORTS_VERITY \
+    PRODUCT_OEM_PROPERTIES \
+    PRODUCT_SYSTEM_PROPERTY_BLACKLIST \
+    PRODUCT_VERITY_PARTITION \
+    PRODUCT_VERITY_SIGNING_KEY
 
 define dump-product
 $(info ==== $(1) ====)\
diff --git a/core/product_config.mk b/core/product_config.mk
index 61bdfb4..171c394 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -319,8 +319,10 @@
 # Everyone gets nodpi assets which are density-independent.
 PRODUCT_AAPT_CONFIG += nodpi
 
+# Keep a copy of the space-separated config
+PRODUCT_AAPT_CONFIG_SP := $(PRODUCT_AAPT_CONFIG)
+
 # Convert spaces to commas.
-comma := ,
 PRODUCT_AAPT_CONFIG := \
     $(subst $(space),$(comma),$(strip $(PRODUCT_AAPT_CONFIG)))
 PRODUCT_AAPT_PREF_CONFIG := \
diff --git a/core/tasks/apicheck.mk b/core/tasks/apicheck.mk
index 00b78b9..4711d17 100644
--- a/core/tasks/apicheck.mk
+++ b/core/tasks/apicheck.mk
@@ -42,6 +42,8 @@
     checkapi-last, \
     $(SRC_API_DIR)/$(last_released_sdk_version).txt, \
     $(INTERNAL_PLATFORM_API_FILE), \
+    frameworks/base/api/removed.txt, \
+    $(INTERNAL_PLATFORM_REMOVED_API_FILE), \
     -hide 2 -hide 3 -hide 4 -hide 5 -hide 6 -hide 24 -hide 25 -hide 26 -hide 27 \
     -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
     -error 16 -error 17 -error 18 , \
@@ -56,6 +58,8 @@
     checkapi-current, \
     frameworks/base/api/current.txt, \
     $(INTERNAL_PLATFORM_API_FILE), \
+    frameworks/base/api/removed.txt, \
+    $(INTERNAL_PLATFORM_REMOVED_API_FILE), \
     -error 2 -error 3 -error 4 -error 5 -error 6 \
     -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
     -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
@@ -69,5 +73,7 @@
 update-api: $(INTERNAL_PLATFORM_API_FILE) | $(ACP)
 	@echo Copying current.txt
 	$(hide) $(ACP) $(INTERNAL_PLATFORM_API_FILE) frameworks/base/api/current.txt
+	@echo Copying removed.txt
+	$(hide) $(ACP) $(INTERNAL_PLATFORM_REMOVED_API_FILE) frameworks/base/api/removed.txt
 
 endif
diff --git a/core/tasks/oem_image.mk b/core/tasks/oem_image.mk
new file mode 100644
index 0000000..26b9aba
--- /dev/null
+++ b/core/tasks/oem_image.mk
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2014 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.
+#
+
+# We build oem.img only if it's asked for.
+ifneq ($(filter $(MAKECMDGOALS),oem_image),)
+ifndef BOARD_OEMIMAGE_PARTITION_SIZE
+$(error BOARD_OEMIMAGE_PARTITION_SIZE is not set.)
+endif
+
+INTERNAL_OEMIMAGE_FILES := \
+    $(filter $(TARGET_OUT_OEM)/%,$(ALL_DEFAULT_INSTALLED_MODULES))
+
+oemimage_intermediates := \
+    $(call intermediates-dir-for,PACKAGING,oem)
+BUILT_OEMIMAGE_TARGET := $(PRODUCT_OUT)/oem.img
+# We just build this directly to the install location.
+INSTALLED_OEMIMAGE_TARGET := $(BUILT_OEMIMAGE_TARGET)
+
+$(INSTALLED_OEMIMAGE_TARGET) : $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_OEMIMAGE_FILES)
+	$(call pretty,"Target oem fs image: $@")
+	@mkdir -p $(TARGET_OUT_OEM)
+	@mkdir -p $(oemimage_intermediates) && rm -rf $(oemimage_intermediates)/oem_image_info.txt
+	$(call generate-userimage-prop-dictionary, $(oemimage_intermediates)/oem_image_info.txt, skip_fsck=true)
+	$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
+	  ./build/tools/releasetools/build_image.py \
+	  $(TARGET_OUT_OEM) $(oemimage_intermediates)/oem_image_info.txt $@
+	$(hide) $(call assert-max-image-size,$@,$(BOARD_OEMIMAGE_PARTITION_SIZE))
+
+.PHONY: oem_image
+oem_image : $(INSTALLED_OEMIMAGE_TARGET)
+$(call dist-for-goals, oem_image, $(INSTALLED_OEMIMAGE_TARGET))
+
+endif  # oem_image in $(MAKECMDGOALS)
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index 4929abb..432391f 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -41,7 +41,7 @@
   # which is the version that we reveal to the end user.
   # Update this value when the platform version changes (rather
   # than overriding it somewhere else).  Can be an arbitrary string.
-  PLATFORM_VERSION := KKWT
+  PLATFORM_VERSION := L
 endif
 
 ifeq "" "$(PLATFORM_SDK_VERSION)"
@@ -59,7 +59,12 @@
 ifeq "" "$(PLATFORM_VERSION_CODENAME)"
   # This is the current development code-name, if the build is not a final
   # release build.  If this is a final release build, it is simply "REL".
-  PLATFORM_VERSION_CODENAME := KKWT
+  PLATFORM_VERSION_CODENAME := L
+
+  # This is all of the development codenames that are active.  Should be either
+  # the same as PLATFORM_VERSION_CODENAME or a comma-separated list of additional
+  # codenames after PLATFORM_VERSION_CODENAME.
+  PLATFORM_VERSION_ALL_CODENAMES := $(PLATFORM_VERSION_CODENAME),KKWT
 endif
 
 ifeq "" "$(DEFAULT_APP_TARGET_SDK)"
diff --git a/envsetup.sh b/envsetup.sh
index a7693c0..45768c1 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -208,6 +208,24 @@
     unset ANDROID_HOST_OUT
     export ANDROID_HOST_OUT=$(get_abs_build_var HOST_OUT)
 
+    # If prebuilts/android-emulator/<system>/ exists, prepend it to our PATH
+    # to ensure that the corresponding 'emulator' binaries are used.
+    case $(uname -s) in
+        Darwin)
+            ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/darwin-x86_64
+            ;;
+        Linux)
+            ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/linux-x86_64
+            ;;
+        *)
+            ANDROID_EMULATOR_PREBUILTS=
+            ;;
+    esac
+    if [ -n "$ANDROID_EMULATOR_PREBUILTS" -a -d "$ANDROID_EMULATOR_PREBUILTS" ]; then
+        PATH=$ANDROID_EMULATOR_PREBUILTS:$PATH
+        export ANDROID_EMULATOR_PREBUILTS
+    fi
+
     # needed for building linux on MacOS
     # TODO: fix the path
     #export HOST_EXTRACFLAGS="-I "$T/system/kernel_headers/host_include
@@ -568,12 +586,12 @@
 complete -F _lunch lunch
 
 # Configures the build to build unbundled apps.
-# Run tapas with one ore more app names (from LOCAL_PACKAGE_NAME)
+# Run tapas with one or more app names (from LOCAL_PACKAGE_NAME)
 function tapas()
 {
-    local arch=$(echo -n $(echo $* | xargs -n 1 echo | \grep -E '^(arm|x86|mips|armv5)$'))
-    local variant=$(echo -n $(echo $* | xargs -n 1 echo | \grep -E '^(user|userdebug|eng)$'))
-    local apps=$(echo -n $(echo $* | xargs -n 1 echo | \grep -E -v '^(user|userdebug|eng|arm|x86|mips|armv5)$'))
+    local arch="$(echo $* | xargs -n 1 echo | \grep -E '^(arm|x86|mips|armv5)$' | xargs)"
+    local variant="$(echo $* | xargs -n 1 echo | \grep -E '^(user|userdebug|eng)$' | xargs)"
+    local apps="$(echo $* | xargs -n 1 echo | \grep -E -v '^(user|userdebug|eng|arm|x86|mips|armv5)$' | xargs)"
 
     if [ $(echo $arch | wc -w) -gt 1 ]; then
         echo "tapas: Error: Multiple build archs supplied: $arch"
diff --git a/target/board/generic/device.mk b/target/board/generic/device.mk
index fe64bcb..06a7d8a 100644
--- a/target/board/generic/device.mk
+++ b/target/board/generic/device.mk
@@ -25,6 +25,9 @@
 PRODUCT_COPY_FILES := \
     device/generic/goldfish/data/etc/apns-conf.xml:system/etc/apns-conf.xml \
     device/generic/goldfish/camera/media_profiles.xml:system/etc/media_profiles.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:system/etc/media_codecs_google_audio.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:system/etc/media_codecs_google_telephony.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:system/etc/media_codecs_google_video.xml \
     device/generic/goldfish/camera/media_codecs.xml:system/etc/media_codecs.xml \
     hardware/libhardware_legacy/audio/audio_policy.conf:system/etc/audio_policy.conf
 
diff --git a/target/board/generic_arm64/device.mk b/target/board/generic_arm64/device.mk
index 354fb2a..11a6a31 100644
--- a/target/board/generic_arm64/device.mk
+++ b/target/board/generic_arm64/device.mk
@@ -25,6 +25,9 @@
 PRODUCT_COPY_FILES := \
     device/generic/goldfish/data/etc/apns-conf.xml:system/etc/apns-conf.xml \
     device/generic/goldfish/camera/media_profiles.xml:system/etc/media_profiles.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:system/etc/media_codecs_google_audio.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:system/etc/media_codecs_google_telephony.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:system/etc/media_codecs_google_video.xml \
     device/generic/goldfish/camera/media_codecs.xml:system/etc/media_codecs.xml
 
 PRODUCT_PACKAGES := \
diff --git a/target/board/generic_mips/device.mk b/target/board/generic_mips/device.mk
index b182c4c..590f422 100644
--- a/target/board/generic_mips/device.mk
+++ b/target/board/generic_mips/device.mk
@@ -25,6 +25,9 @@
 PRODUCT_COPY_FILES := \
     device/generic/goldfish/data/etc/apns-conf.xml:system/etc/apns-conf.xml \
     device/generic/goldfish/camera/media_profiles.xml:system/etc/media_profiles.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:system/etc/media_codecs_google_audio.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:system/etc/media_codecs_google_telephony.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:system/etc/media_codecs_google_video.xml \
     device/generic/goldfish/camera/media_codecs.xml:system/etc/media_codecs.xml \
     hardware/libhardware_legacy/audio/audio_policy.conf:system/etc/audio_policy.conf
 
diff --git a/target/board/generic_mips64/device.mk b/target/board/generic_mips64/device.mk
index 58fe976..015686e 100644
--- a/target/board/generic_mips64/device.mk
+++ b/target/board/generic_mips64/device.mk
@@ -25,6 +25,9 @@
 PRODUCT_COPY_FILES := \
     device/generic/goldfish/data/etc/apns-conf.xml:system/etc/apns-conf.xml \
     device/generic/goldfish/camera/media_profiles.xml:system/etc/media_profiles.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:system/etc/media_codecs_google_audio.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:system/etc/media_codecs_google_telephony.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:system/etc/media_codecs_google_video.xml \
     device/generic/goldfish/camera/media_codecs.xml:system/etc/media_codecs.xml \
     hardware/libhardware_legacy/audio/audio_policy.conf:system/etc/audio_policy.conf
 
diff --git a/target/board/generic_x86/device.mk b/target/board/generic_x86/device.mk
index 089f584..b5b0faf 100644
--- a/target/board/generic_x86/device.mk
+++ b/target/board/generic_x86/device.mk
@@ -25,6 +25,9 @@
 PRODUCT_COPY_FILES := \
     device/generic/goldfish/data/etc/apns-conf.xml:system/etc/apns-conf.xml \
     device/generic/goldfish/camera/media_profiles.xml:system/etc/media_profiles.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:system/etc/media_codecs_google_audio.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:system/etc/media_codecs_google_telephony.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:system/etc/media_codecs_google_video.xml \
     device/generic/goldfish/camera/media_codecs.xml:system/etc/media_codecs.xml
 
 PRODUCT_PACKAGES := \
diff --git a/target/board/generic_x86_64/device.mk b/target/board/generic_x86_64/device.mk
index 5b002bd..aa0e5a4 100755
--- a/target/board/generic_x86_64/device.mk
+++ b/target/board/generic_x86_64/device.mk
@@ -25,6 +25,9 @@
 PRODUCT_COPY_FILES := \
     device/generic/goldfish/data/etc/apns-conf.xml:system/etc/apns-conf.xml \
     device/generic/goldfish/camera/media_profiles.xml:system/etc/media_profiles.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:system/etc/media_codecs_google_audio.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:system/etc/media_codecs_google_telephony.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:system/etc/media_codecs_google_video.xml \
     device/generic/goldfish/camera/media_codecs.xml:system/etc/media_codecs.xml \
     system/core/rootdir/init.zygote64.rc:root/init.zygote64.rc
 
diff --git a/target/board/vbox_x86/device.mk b/target/board/vbox_x86/device.mk
index a44a87f..b51f801 100644
--- a/target/board/vbox_x86/device.mk
+++ b/target/board/vbox_x86/device.mk
@@ -29,6 +29,9 @@
 PRODUCT_COPY_FILES := \
     device/generic/goldfish/data/etc/apns-conf.xml:system/etc/apns-conf.xml \
     device/generic/goldfish/camera/media_profiles.xml:system/etc/media_profiles.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:system/etc/media_codecs_google_audio.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:system/etc/media_codecs_google_telephony.xml \
+    frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:system/etc/media_codecs_google_video.xml \
     device/generic/goldfish/camera/media_codecs.xml:system/etc/media_codecs.xml \
     build/target/board/vbox_x86/init.vbox_x86.rc:root/init.vbox_x86.rc \
     $(LOCAL_KERNEL):kernel
diff --git a/target/product/AndroidProducts.mk b/target/product/AndroidProducts.mk
index fc407d3..a714f1f 100644
--- a/target/product/AndroidProducts.mk
+++ b/target/product/AndroidProducts.mk
@@ -63,5 +63,7 @@
     $(LOCAL_DIR)/sdk.mk \
     $(LOCAL_DIR)/sdk_x86.mk \
     $(LOCAL_DIR)/sdk_mips.mk \
-    $(LOCAL_DIR)/large_emu_hw.mk
+    $(LOCAL_DIR)/sdk_arm64.mk \
+    $(LOCAL_DIR)/sdk_x86_64.mk \
+    $(LOCAL_DIR)/sdk_mips64.mk
 endif
diff --git a/target/product/base.mk b/target/product/base.mk
index 4b8326d..6dd77a4 100644
--- a/target/product/base.mk
+++ b/target/product/base.mk
@@ -35,12 +35,14 @@
     fsck_msdos \
     ime \
     input \
+    inputflinger \
     javax.obex \
     libandroid \
     libandroid_runtime \
     libandroid_servers \
     libaudioeffect_jni \
     libaudioflinger \
+    libaudiopolicy \
     libbundlewrapper \
     libcamera_client \
     libcameraservice \
@@ -48,6 +50,7 @@
     libeffectproxy \
     libeffects \
     libinput \
+    libinputflinger \
     libiprouteutil \
     libjnigraphics \
     libldnhncr \
@@ -77,6 +80,8 @@
     libutils \
     libvisualizer \
     libvorbisidec \
+    libmediandk \
+    libwifi-service \
     media \
     media_cmd \
     mediaserver \
@@ -93,7 +98,6 @@
     racoon \
     run-as \
     schedtest \
-    screenshot \
     sdcard \
     services \
     settings \
diff --git a/target/product/core.mk b/target/product/core.mk
index 8c88b94..a88e92f 100644
--- a/target/product/core.mk
+++ b/target/product/core.mk
@@ -22,16 +22,33 @@
 PRODUCT_PACKAGES += \
     BasicDreams \
     Browser \
+    Calculator \
+    Calendar \
+    CalendarProvider \
+    CertInstaller \
     Contacts \
+    DeskClock \
     DocumentsUI \
     DownloadProviderUi \
+    Email \
+    Exchange2 \
     ExternalStorageProvider \
+    FusedLocation \
+    InputDevices \
     KeyChain \
+    Keyguard \
+    LatinIME \
+    Launcher2 \
+    ManagedProvisioning \
     PicoTts \
     PacProcessor \
     libpac \
+    PrintSpooler \
     ProxyHandler \
+    QuickSearchBox \
+    Settings \
     SharedStorageBackup \
+    TeleService \
     VpnDialogs
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/core_base.mk)
diff --git a/target/product/core_64_bit.mk b/target/product/core_64_bit.mk
index 5d7abcb..971b6bd 100644
--- a/target/product/core_64_bit.mk
+++ b/target/product/core_64_bit.mk
@@ -29,11 +29,5 @@
 # This line must be parsed before the one in core_minimal.mk
 PRODUCT_DEFAULT_PROPERTY_OVERRIDES += ro.zygote=zygote32_64
 
-# Temporary hack to prefer launching processes as 32 bit
-# instead of 64 bit.
-#
-# STOPSHIP: Revert this to allow products to select it themselves
-TARGET_PREFER_32_BIT_APPS := true
-
 TARGET_SUPPORTS_32_BIT_APPS := true
 TARGET_SUPPORTS_64_BIT_APPS := true
diff --git a/target/product/core_base.mk b/target/product/core_base.mk
index 69bde63..d63e39f 100644
--- a/target/product/core_base.mk
+++ b/target/product/core_base.mk
@@ -30,7 +30,6 @@
     libandroidfw \
     libaudiopreprocessing \
     libaudioutils \
-    libbcc \
     libfilterpack_imageproc \
     libgabi++ \
     libkeystore \
@@ -39,7 +38,6 @@
     libportable \
     libpowermanager \
     libspeexresampler \
-    libstagefright_chromium_http \
     libstagefright_soft_aacdec \
     libstagefright_soft_aacenc \
     libstagefright_soft_amrdec \
@@ -63,7 +61,8 @@
     mms-common \
     requestsync \
     telephony-common \
-    voip-common
+    voip-common \
+    wifi-service
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/core_minimal.mk)
 # Override the PRODUCT_BOOT_JARS set in core_minimal.mk. The order matters.
@@ -82,4 +81,5 @@
     android.policy \
     services \
     apache-xml \
-    webviewchromium
+    webviewchromium \
+    wifi-service
diff --git a/target/product/core_minimal.mk b/target/product/core_minimal.mk
index fc69973..7fbdee6 100644
--- a/target/product/core_minimal.mk
+++ b/target/product/core_minimal.mk
@@ -45,6 +45,7 @@
     iptables \
     keystore \
     keystore.default \
+    libbcc \
     libOpenMAXAL \
     libOpenSLES \
     libdownmix \
@@ -58,7 +59,9 @@
     screencap \
     sensorservice \
     uiautomator \
-    webview
+    uncrypt \
+    webview \
+    wifi-service
 
 # The order of PRODUCT_BOOT_JARS matters.
 PRODUCT_BOOT_JARS := \
@@ -73,10 +76,11 @@
     android.policy \
     services \
     apache-xml \
-    webviewchromium
+    webviewchromium \
+    wifi-service
 
-PRODUCT_RUNTIMES := runtime_libdvm_default
-PRODUCT_RUNTIMES += runtime_libart
+PRODUCT_RUNTIMES := runtime_libart_default
+PRODUCT_RUNTIMES += runtime_libdvm
 
 PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
     ro.zygote=zygote32
diff --git a/target/product/core_tiny.mk b/target/product/core_tiny.mk
index 3e6cbd3..9c40206 100644
--- a/target/product/core_tiny.mk
+++ b/target/product/core_tiny.mk
@@ -21,21 +21,12 @@
     ContactsProvider \
     CertInstaller \
     FusedLocation \
-    InputDevices \
-    bluetooth-health \
-    hostapd \
-    wpa_supplicant.conf
+    InputDevices
 
 PRODUCT_PACKAGES += \
-    audio \
     clatd \
     clatd.conf \
-    dhcpcd.conf \
-    network \
-    pand \
-    pppd \
-    sdptool \
-    wpa_supplicant
+    pppd
 
 PRODUCT_PACKAGES += \
     audio.primary.default \
@@ -71,7 +62,8 @@
     make_ext4fs \
     screencap \
     sensorservice \
-    uiautomator
+    uiautomator \
+    uncrypt
 
 # The order matters
 PRODUCT_BOOT_JARS := \
@@ -92,11 +84,10 @@
 PRODUCT_PROPERTY_OVERRIDES += \
     ro.carrier=unknown
 
+$(call inherit-product, $(SRC_TARGET_DIR)/product/base.mk)
 $(call inherit-product-if-exists, frameworks/base/data/fonts/fonts.mk)
 
 # Overrides
 PRODUCT_BRAND := tiny
 PRODUCT_DEVICE := tiny
 PRODUCT_NAME := core_tiny
-
-$(call inherit-product, $(SRC_TARGET_DIR)/product/base.mk)
diff --git a/target/product/full_base.mk b/target/product/full_base.mk
index 059697e..1daa523 100644
--- a/target/product/full_base.mk
+++ b/target/product/full_base.mk
@@ -26,7 +26,6 @@
     libWnnEngDic \
     libWnnJpnDic \
     libwnndict \
-    VideoEditor \
     WAPPushManager
 
 PRODUCT_PACKAGES += \
diff --git a/target/product/full_base_telephony.mk b/target/product/full_base_telephony.mk
index f98e9a2..2fd2ce8 100644
--- a/target/product/full_base_telephony.mk
+++ b/target/product/full_base_telephony.mk
@@ -27,7 +27,8 @@
     ro.com.android.dataroaming=true
 
 PRODUCT_COPY_FILES := \
-    device/generic/goldfish/data/etc/apns-conf.xml:system/etc/apns-conf.xml
+    device/generic/goldfish/data/etc/apns-conf.xml:system/etc/apns-conf.xml \
+    frameworks/native/data/etc/handheld_core_hardware.xml:system/etc/permissions/handheld_core_hardware.xml
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony.mk)
diff --git a/target/product/generic_no_telephony.mk b/target/product/generic_no_telephony.mk
index 12797f4..b4f2a7b 100644
--- a/target/product/generic_no_telephony.mk
+++ b/target/product/generic_no_telephony.mk
@@ -17,33 +17,15 @@
 # This is a generic phone product that isn't specialized for a specific device.
 # It includes the base Android platform.
 
-PRODUCT_POLICY := android.policy_phone
-
 PRODUCT_PACKAGES := \
-    DeskClock \
     Bluetooth \
-    Calculator \
-    Calendar \
     Camera2 \
-    CertInstaller \
-    Email \
-    Exchange2 \
-    FusedLocation \
     Gallery2 \
-    InputDevices \
-    Keyguard \
-    LatinIME \
-    Launcher2 \
     Music \
     MusicFX \
     OneTimeInitializer \
-    PrintSpooler \
     Provision \
-    QuickSearchBox \
-    Settings \
     SystemUI \
-    TeleService \
-    CalendarProvider \
     WallpaperCropper
 
 PRODUCT_PACKAGES += \
diff --git a/target/product/large_emu_hw.mk b/target/product/large_emu_hw.mk
deleted file mode 100644
index a918c1d..0000000
--- a/target/product/large_emu_hw.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# Copyright (C) 2007 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# This is a generic product for devices with large display but not specialized
-# for a specific device. It includes the base Android platform.
-
-PRODUCT_POLICY := android.policy_mid
-
-PRODUCT_PACKAGES := \
-    CarHome \
-    DeskClock \
-    Bluetooth \
-    Calculator \
-    Calendar \
-    CertInstaller \
-    Email \
-    Exchange2 \
-    Gallery2 \
-    LatinIME \
-    Launcher2 \
-    Music \
-    Provision \
-    QuickSearchBox \
-    Settings \
-    Sync \
-    Updater \
-    CalendarProvider \
-    SyncProvider \
-    bluetooth-health \
-    hostapd \
-    wpa_supplicant.conf
-
-
-$(call inherit-product, $(SRC_TARGET_DIR)/product/core.mk)
-
-# Overrides
-PRODUCT_BRAND := generic
-PRODUCT_DEVICE := generic
-PRODUCT_NAME := large_emu_hw
diff --git a/target/product/sdk.mk b/target/product/sdk.mk
index 5926f19..2e576c1 100644
--- a/target/product/sdk.mk
+++ b/target/product/sdk.mk
@@ -14,56 +14,38 @@
 # limitations under the License.
 #
 
-PRODUCT_POLICY := android.policy_phone
 PRODUCT_PROPERTY_OVERRIDES :=
 
 PRODUCT_PACKAGES := \
-	Calculator \
-	DeskClock \
-	Email \
-	Exchange2 \
-	FusedLocation \
-	Gallery \
-	Keyguard \
-	Music \
-	Mms \
-	OpenWnn \
-	PrintSpooler \
-	libWnnEngDic \
-	libWnnJpnDic \
-	libwnndict \
-	TeleService \
-	PinyinIME \
-	Protips \
-	SoftKeyboard \
-	SystemUI \
-	Launcher2 \
+	ApiDemos \
+	CubeLiveWallpapers \
+	CustomLocale \
 	Development \
 	DevelopmentSettings \
+	Dialer \
+	EmulatorSmokeTests \
 	Fallback \
-	Settings \
-	SdkSetup \
-	CustomLocale \
-	sqlite3 \
-	InputDevices \
-	LatinIME \
-	CertInstaller \
-	LiveWallpapersPicker \
-	ApiDemos \
+	Gallery \
 	GestureBuilder \
-	CubeLiveWallpapers \
-	QuickSearchBox \
-	WidgetPreview \
+	LegacyCamera \
 	librs_jni \
-	ConnectivityTest \
-	GpsLocationTest \
-	CalendarProvider \
-	Calendar \
+	libwnndict \
+	libWnnEngDic \
+	libWnnJpnDic \
+	LiveWallpapersPicker \
+	Mms \
+	Music \
+	OpenWnn \
+	PinyinIME \
+	Protips \
+	rild \
+	SdkSetup \
 	SmokeTest \
 	SmokeTestApp \
-	rild \
-	LegacyCamera \
-	Dialer
+	SoftKeyboard \
+	sqlite3 \
+	SystemUI \
+	WidgetPreview
 
 # Define the host tools and libs that are parts of the SDK.
 -include sdk/build/product_sdk.mk
@@ -83,6 +65,9 @@
 	frameworks/base/data/sounds/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
 	frameworks/native/data/etc/handheld_core_hardware.xml:system/etc/permissions/handheld_core_hardware.xml \
 	device/generic/goldfish/camera/media_profiles.xml:system/etc/media_profiles.xml \
+	frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:system/etc/media_codecs_google_audio.xml \
+	frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:system/etc/media_codecs_google_telephony.xml \
+	frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:system/etc/media_codecs_google_video.xml \
 	device/generic/goldfish/camera/media_codecs.xml:system/etc/media_codecs.xml \
 	frameworks/native/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml:system/etc/permissions/android.hardware.touchscreen.multitouch.jazzhand.xml \
 	frameworks/native/data/etc/android.hardware.camera.autofocus.xml:system/etc/permissions/android.hardware.camera.autofocus.xml \
@@ -103,14 +88,17 @@
 $(call inherit-product-if-exists, frameworks/webview/chromium/chromium.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/core.mk)
 
-# Overrides
-PRODUCT_BRAND := generic
-PRODUCT_NAME := sdk
-PRODUCT_DEVICE := generic
+# include available languages for TTS in the system image
+-include external/svox/pico/lang/PicoLangDeDeInSystem.mk
+-include external/svox/pico/lang/PicoLangEnGBInSystem.mk
+-include external/svox/pico/lang/PicoLangEnUsInSystem.mk
+-include external/svox/pico/lang/PicoLangEsEsInSystem.mk
+-include external/svox/pico/lang/PicoLangFrFrInSystem.mk
+-include external/svox/pico/lang/PicoLangItItInSystem.mk
 
 # locale + densities. en_US is both first and in alphabetical order to
 # ensure this is the default locale.
-PRODUCT_LOCALES = \
+PRODUCT_LOCALES := \
 	en_US \
 	ldpi \
 	hdpi \
@@ -174,10 +162,7 @@
 	zh_CN \
 	zh_TW
 
-# include available languages for TTS in the system image
--include external/svox/pico/lang/PicoLangDeDeInSystem.mk
--include external/svox/pico/lang/PicoLangEnGBInSystem.mk
--include external/svox/pico/lang/PicoLangEnUsInSystem.mk
--include external/svox/pico/lang/PicoLangEsEsInSystem.mk
--include external/svox/pico/lang/PicoLangFrFrInSystem.mk
--include external/svox/pico/lang/PicoLangItItInSystem.mk
+# Overrides
+PRODUCT_BRAND := generic
+PRODUCT_NAME := sdk
+PRODUCT_DEVICE := generic
diff --git a/target/product/sdk_arm64.mk b/target/product/sdk_arm64.mk
new file mode 100644
index 0000000..582a779
--- /dev/null
+++ b/target/product/sdk_arm64.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This is a build configuration for a full-featured build of the
+# Open-Source part of the tree. It's geared toward a US-centric
+# build quite specifically for the emulator, and might not be
+# entirely appropriate to inherit from for on-device configurations.
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk.mk)
+
+# Overrides
+PRODUCT_BRAND := generic_arm64
+PRODUCT_NAME := sdk_arm64
+PRODUCT_DEVICE := generic_arm64
+PRODUCT_MODEL := Android SDK built for arm64
diff --git a/target/product/sdk_mips64.mk b/target/product/sdk_mips64.mk
new file mode 100644
index 0000000..c00de06
--- /dev/null
+++ b/target/product/sdk_mips64.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This is a build configuration for a full-featured build of the
+# Open-Source part of the tree. It's geared toward a US-centric
+# build quite specifically for the emulator, and might not be
+# entirely appropriate to inherit from for on-device configurations.
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk.mk)
+
+# Overrides
+PRODUCT_BRAND := generic_mips64
+PRODUCT_NAME := sdk_mips64
+PRODUCT_DEVICE := generic_mips64
+PRODUCT_MODEL := Android SDK built for mips64
diff --git a/target/product/sdk_x86.mk b/target/product/sdk_x86.mk
index 873d0c0..6c5e746 100644
--- a/target/product/sdk_x86.mk
+++ b/target/product/sdk_x86.mk
@@ -19,7 +19,7 @@
 # build quite specifically for the emulator, and might not be
 # entirely appropriate to inherit from for on-device configurations.
 
-include $(SRC_TARGET_DIR)/product/sdk.mk
+$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk.mk)
 
 # Overrides
 PRODUCT_BRAND := generic_x86
diff --git a/target/product/sdk_x86_64.mk b/target/product/sdk_x86_64.mk
new file mode 100644
index 0000000..3816cf9
--- /dev/null
+++ b/target/product/sdk_x86_64.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This is a build configuration for a full-featured build of the
+# Open-Source part of the tree. It's geared toward a US-centric
+# build quite specifically for the emulator, and might not be
+# entirely appropriate to inherit from for on-device configurations.
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk.mk)
+
+# Overrides
+PRODUCT_BRAND := generic_x86_64
+PRODUCT_NAME := sdk_x86_64
+PRODUCT_DEVICE := generic_x86_64
+PRODUCT_MODEL := Android SDK built for x86_64
diff --git a/target/product/security/Android.mk b/target/product/security/Android.mk
new file mode 100644
index 0000000..5a40397
--- /dev/null
+++ b/target/product/security/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+
+#######################################
+# verity_key
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := verity_key
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+include $(BUILD_PREBUILT)
diff --git a/target/product/security/verity_key b/target/product/security/verity_key
new file mode 100644
index 0000000..8db965f
--- /dev/null
+++ b/target/product/security/verity_key
Binary files differ
diff --git a/target/product/security/verity_private_dev_key b/target/product/security/verity_private_dev_key
new file mode 100644
index 0000000..92528e9
--- /dev/null
+++ b/target/product/security/verity_private_dev_key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDQxdVrH2RB1eg5
+17/gBmLzW1Ds10RG6ctNZMhxppMOLnEZViKGv1VNRhxqK/JKTv2UujgZ94SJcDub
+G+DwAwaGZKQqDYUa0VU2cng8TYPcnYGPdJ7Usckp6tdg64vns7e+VVf0dOyEovR+
+JyeYUz05OhUMYP9xJIhpA2XnXe5Ekb9iTFSYo9uBpoXDD4IY7aOqUxSbv9wMtyIp
+dl+oTm0+kqRRi4KoxGHV0CzDseEUuWG/Kp/7aVF9Sg45NcC6KYvrGysUKA+Bt09O
+feDn/HRpT9SfRElJa5DRms33UBUtnom15F4yd4vvFgubB0nkPOUuwfZhTFfgeuY4
+H2bHkjKbAgMBAAECggEAMpFYqkPGQvQO9cO+ZALoAM4Dgfp6PTrv1WUt7+lLAUpa
+dqqYXk8F2Fu9EjJm03ziix237QI5Bhk7Nsy/5SK2d+L0qILx1JcTrsZ3PRQBdnRo
+J1k2B4qwkQii9oTXNF4hiWaekUWo7E+ULOJLAuhWkf/xjTgJZ1xT9iuuiSYFSnIa
+9ABNH0vCaKEkW/4ri6fdtXmO26C/ltJlnozl86x07PIFh4uBas7/40E8ykFP00CS
+zdhMh+2DGyCb1Q0eJ1IfGILNatkLNEd2BHgQ7qNBkN9yShZfhvIPblr5gSUlZplX
+diV20ZGLAfByKWgZZWKkwl9KzaisL/J/4dr2UlSVEQKBgQDxAYTsgoTkkP0TKzr3
+i3ljT8OuVOj6TwZVBJYe2MIJ3veivS3gWB53FpsKthbib7y8ifIakn15mQkNCK5R
+7H7F5lvZCNnB6shY5Dz7nLJxKLALcAg+d12l3gTbFQeFDs0iQQJF7P8hs/GPF7kY
+Layb7EF0uzYjyHJCKtFdaZaeZwKBgQDdwvCb7NJVeGTcE97etL+8acu9y4GlqKEF
+o0Vkw8TjNKj/KuDkbkAk9hXxU1ZCmDU3y6r8CVHYl0Sqh08plEhkYB/j3sFy81zY
+3xu/rLFysBwjeJHHlPjRTYkdKr9pABmm8NIEShvu9u8i+mpOhjbX72HxZL+i4Fou
+gz58wEdBrQKBgG8CfyKdn+7UJe3tbLTXRquK8xxauhGJ0uXYPfmpZ/8596C7OOVs
+UWQTQoj1hKb6RtolRCIfNbKL3hJl3D2aDG7Fg6r9m6fpqCzhvIE9FShwUF6EVRfI
+zZb4JA5xqkwMnEpZ3V0uI/p3Mx3xFG3ho+8SLLhC/1YOHysBI/y+BQWjAoGAYiqQ
+PkXYWhOAeleleeqDUdF3al3y1zVNimRbLJ7owjcmdEYz5YrUhEgXMIvWjIY6UKes
+2gL6IynbMK3TIjHM1fojQ8jw04TdXfdtnizBJGbHHgCab8IHXwe2oZ2xu7ZapKbI
+ITP5J5BSDabSdk49attB/Qy/NEeiRCK+/5RSNsUCgYAg6vX9VqMEkhPHeoFfdLGD
+EQPPN6QLrQ4Zif0GKxH96znNSv0rXdNp9t0kyapdgzMuCwIEuOkCSiKgmfjTWnYO
+qh5HMUuD2VbfWwI9jVujQMRmqiaFF7VxxA1bP5j1hJlI6cn1Fjlpi+NsNZN4nm3Q
+92SEwX2vDgjrU0NAtFFL1Q==
+-----END PRIVATE KEY-----
diff --git a/target/product/verity.mk b/target/product/verity.mk
new file mode 100644
index 0000000..4a1ca5e
--- /dev/null
+++ b/target/product/verity.mk
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2014 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.
+#
+
+# Provides dependencies necessary for verified boot
+
+PRODUCT_SUPPORTS_VERITY := true
+PRODUCT_VERITY_SIGNING_KEY := build/target/product/security/verity_private_dev_key
+
+PRODUCT_PACKAGES += \
+        verity_key
diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh
index 46a73f8..593e5b5 100755
--- a/tools/buildinfo.sh
+++ b/tools/buildinfo.sh
@@ -8,6 +8,7 @@
 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.all_codenames=$PLATFORM_VERSION_ALL_CODENAMES"
 echo "ro.build.version.release=$PLATFORM_VERSION"
 echo "ro.build.date=`date`"
 echo "ro.build.date.utc=`date +%s`"
@@ -46,9 +47,10 @@
 echo "# ro.build.product is obsolete; use ro.product.device"
 echo "ro.build.product=$TARGET_DEVICE"
 
-echo "# Do not try to parse ro.build.description or .fingerprint"
+echo "# Do not try to parse description, fingerprint, or thumbprint"
 echo "ro.build.description=$PRIVATE_BUILD_DESC"
 echo "ro.build.fingerprint=$BUILD_FINGERPRINT"
+echo "ro.build.thumbprint=$BUILD_THUMBPRINT"
 echo "ro.build.characteristics=$TARGET_AAPT_CHARACTERISTICS"
 
 echo "# end build properties"
diff --git a/tools/droiddoc/templates-ds/package.cs b/tools/droiddoc/templates-ds/package.cs
index ea3e4f4..d67d5d9 100644
--- a/tools/droiddoc/templates-ds/package.cs
+++ b/tools/droiddoc/templates-ds/package.cs
@@ -45,6 +45,7 @@
   <?cs /if ?>
 <?cs /def ?>
 
+<?cs call:class_table("Annotations", package.annotations) ?>
 <?cs call:class_table("Interfaces", package.interfaces) ?>
 <?cs call:class_table("Classes", package.classes) ?>
 <?cs call:class_table("Enums", package.enums) ?>
diff --git a/tools/droiddoc/templates-sac/package.cs b/tools/droiddoc/templates-sac/package.cs
index 99eaff2..abd49f1 100644
--- a/tools/droiddoc/templates-sac/package.cs
+++ b/tools/droiddoc/templates-sac/package.cs
@@ -45,6 +45,7 @@
   <?cs /if ?>
 <?cs /def ?>
 
+<?cs call:class_table("Annotations", package.annotations) ?>
 <?cs call:class_table("Interfaces", package.interfaces) ?>
 <?cs call:class_table("Classes", package.classes) ?>
 <?cs call:class_table("Enums", package.enums) ?>
diff --git a/tools/droiddoc/templates-sdk/assets/css/resourcecards.css b/tools/droiddoc/templates-sdk/assets/css/resourcecards.css
new file mode 100644
index 0000000..1222b04
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/assets/css/resourcecards.css
@@ -0,0 +1,371 @@
+/* content layout */
+.resource-widget.resource-flow-layout {
+  display: inline-block;
+  margin-right: -20px;
+  /* clearfix idiom */ }
+  .resource-widget.resource-flow-layout.col-1 {
+    width: 60px; }
+  .resource-widget.resource-flow-layout.col-2 {
+    width: 120px; }
+  .resource-widget.resource-flow-layout.col-3 {
+    width: 180px; }
+  .resource-widget.resource-flow-layout.col-4 {
+    width: 240px; }
+  .resource-widget.resource-flow-layout.col-5 {
+    width: 300px; }
+  .resource-widget.resource-flow-layout.col-6 {
+    width: 360px; }
+  .resource-widget.resource-flow-layout.col-7 {
+    width: 420px; }
+  .resource-widget.resource-flow-layout.col-8 {
+    width: 480px; }
+  .resource-widget.resource-flow-layout.col-9 {
+    width: 540px; }
+  .resource-widget.resource-flow-layout.col-10 {
+    width: 600px; }
+  .resource-widget.resource-flow-layout.col-11 {
+    width: 660px; }
+  .resource-widget.resource-flow-layout.col-12 {
+    width: 720px; }
+  .resource-widget.resource-flow-layout.col-13 {
+    width: 780px; }
+  .resource-widget.resource-flow-layout.col-14 {
+    width: 840px; }
+  .resource-widget.resource-flow-layout.col-15 {
+    width: 900px; }
+  .resource-widget.resource-flow-layout.col-16 {
+    width: 960px; }
+  .resource-widget.resource-flow-layout:after {
+    content: ".";
+    display: block;
+    height: 0;
+    clear: both;
+    visibility: hidden; }
+  * html .resource-widget.resource-flow-layout {
+    height: 1px; }
+
+.resource-card {
+  /* stuff that applies to all cards */
+  display: -webkit-flex;
+  -webkit-transform: translateZ(0);
+  float: left;
+  position: relative;
+  margin-right: 20px;
+  margin-bottom: 20px;
+  background-color: #fff;
+  border-radius: 2px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
+  opacity: 0.8;
+  overflow: hidden;
+  transition: 0.4s box-shadow ease, 0.4s opacity ease;
+  /* card templates */
+  /* specific cards and customizations */ }
+  .resource-card .photo {
+    position: relative;
+    background-color: #eee;
+    background-size: cover;
+    background-repeat: no-repeat;
+    background-position: 50% 50%; }
+  .resource-card.nophoto .photo:after {
+    content: '';
+    display: block;
+    position: absolute;
+    left: 20px;
+    top: 20px;
+    right: 20px;
+    bottom: 20px;
+    opacity: 0.2;
+    background-position: center center;
+    background-repeat: no-repeat;
+    background-size: contain; }
+  .resource-card .icon {
+    background-position: center center;
+    background-repeat: no-repeat;
+    background-size: contain;
+    opacity: 0;
+    transition: 0.4s ease; }
+  .resource-card:hover .icon {
+    opacity: 0.2; }
+  .resource-card:hover {
+    opacity: 1;
+    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.5); }
+  .resource-card.resource-card-youtube.nophoto .photo:after, .resource-card.resource-card-youtube .icon {
+    background-image: url(../images/card_video.png); }
+  .resource-card.resource-card-samples.nophoto .photo:after, .resource-card.resource-card-samples .icon {
+    background-image: url(../images/card_sample.png); }
+  .resource-card.resource-card-blog.nophoto .photo:after, .resource-card.resource-card-blog .icon {
+    background-image: url(../images/card_post.png); }
+  .resource-card.resource-card-training.nophoto .photo:after, .resource-card.resource-card-training .icon {
+    background-image: url(../images/card_training.png); }
+  .resource-card .resource-card-text {
+    color: #333333; }
+  .resource-card .title {
+    /*font-weight: 700;*/
+    font-family: 'Roboto Condensed'; }
+  .resource-card .subtitle {
+    font-family: 'Roboto Condensed';
+    text-transform: uppercase;
+    opacity: 0.3; }
+  .resource-card .abstract {
+    font-weight: 300;
+    font-family: 'Roboto'; }
+  .resource-card.resource-card-12x7 {
+    width: 700px;
+    height: 400px;
+    -webkit-flex-direction: column; }
+    .resource-card.resource-card-12x7 .photo {
+      -webkit-flex: 1 1 auto;
+      border-bottom: 1px solid #ddd; }
+    .resource-card.resource-card-12x7 .resource-card-text {
+      margin: 20px;
+      padding-right: 88px; }
+    .resource-card.resource-card-12x7 .icon {
+      position: absolute;
+      right: 20px;
+      bottom: 20px;
+      width: 48px;
+      height: 48px; }
+    .resource-card.resource-card-12x7 .title {
+      font-size: 36px;
+      line-height: 35px;
+      max-height: 70px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical; }
+    .resource-card.resource-card-12x7 .subtitle {
+      font-size: 18px;
+      line-height: 20px;
+      max-height: 20px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 1;
+      -webkit-box-orient: vertical;
+      max-height: 0;
+      margin-top: 0;
+      transition: 0.4s ease; }
+    .resource-card.resource-card-12x7:hover .subtitle {
+      margin-top: 10px;
+      max-height: 20px; }
+    .resource-card.resource-card-12x7 .abstract {
+      font-size: 18px;
+      margin-top: 0;
+      line-height: 25px;
+      max-height: 75px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 3;
+      -webkit-box-orient: vertical;
+      max-height: 0;
+      transition: 0.4s ease; }
+    .resource-card.resource-card-12x7:hover .abstract {
+      margin-top: 20px;
+      max-height: 75px; }
+  .resource-card.resource-card-8x6 {
+    width: 460px;
+    height: 340px;
+    -webkit-flex-direction: column; }
+    .resource-card.resource-card-8x6 .photo {
+      -webkit-flex: 1 1 auto;
+      border-bottom: 1px solid #ddd; }
+    .resource-card.resource-card-8x6 .resource-card-text {
+      margin: 20px;
+      padding-right: 88px; }
+    .resource-card.resource-card-8x6 .icon {
+      position: absolute;
+      right: 20px;
+      bottom: 20px;
+      width: 48px;
+      height: 48px; }
+    .resource-card.resource-card-8x6 .title {
+      font-size: 36px;
+      line-height: 35px;
+      max-height: 70px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical; }
+    .resource-card.resource-card-8x6 .subtitle {
+      font-size: 18px;
+      line-height: 20px;
+      max-height: 20px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 1;
+      -webkit-box-orient: vertical;
+      max-height: 0;
+      margin-top: 0;
+      transition: 0.4s ease; }
+    .resource-card.resource-card-8x6:hover .subtitle {
+      margin-top: 10px;
+      max-height: 20px; }
+    .resource-card.resource-card-8x6 .abstract {
+      font-size: 18px;
+      margin-top: 0;
+      line-height: 25px;
+      max-height: 75px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 3;
+      -webkit-box-orient: vertical;
+      max-height: 0;
+      transition: 0.4s ease; }
+    .resource-card.resource-card-8x6:hover .abstract {
+      margin-top: 20px;
+      max-height: 75px; }
+    .resource-card.resource-card-8x6 .icon {
+      width: 32px;
+      height: 32px; }
+    .resource-card.resource-card-8x6 .title {
+      font-size: 24px;
+      line-height: 25px;
+      max-height: 50px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical; }
+    .resource-card.resource-card-8x6 .abstract {
+      font-size: 16px;
+      margin-top: 10px;
+      line-height: 20px;
+      max-height: 60px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 3;
+      -webkit-box-orient: vertical; }
+    .resource-card.resource-card-8x6 .subtitle {
+      font-size: 16px;
+      line-height: 20px;
+      max-height: 20px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 1;
+      -webkit-box-orient: vertical;
+      max-height: 0;
+      margin-top: 0;
+      transition: 0.4s ease; }
+    .resource-card.resource-card-8x6:hover .subtitle {
+      margin-top: 10px;
+      max-height: 20px; }
+    .resource-card.resource-card-8x6 .abstract {
+      font-size: 16px;
+      margin-top: 0;
+      line-height: 20px;
+      max-height: 60px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 3;
+      -webkit-box-orient: vertical;
+      max-height: 0;
+      transition: 0.4s ease; }
+    .resource-card.resource-card-8x6:hover .abstract {
+      margin-top: 10px;
+      max-height: 60px; }
+  .resource-card.resource-card-6x4 {
+    width: 340px;
+    height: 220px;
+    -webkit-flex-direction: column; }
+    .resource-card.resource-card-6x4 .photo {
+      -webkit-flex: 1 1 auto;
+      border-bottom: 1px solid #ddd; }
+    .resource-card.resource-card-6x4 .resource-card-text {
+      margin: 10px;
+      padding-right: 26px; }
+    .resource-card.resource-card-6x4 .icon {
+      position: absolute;
+      right: 10px;
+      bottom: 10px;
+      width: 16px;
+      height: 16px; }
+    .resource-card.resource-card-6x4 .title {
+      font-size: 16px;
+      line-height: 20px;
+      max-height: 40px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical; }
+    .resource-card.resource-card-6x4 .subtitle {
+      font-size: 13px;
+      line-height: 15px;
+      max-height: 30px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical;
+      max-height: 0;
+      margin-top: 0;
+      transition: 0.4s ease; }
+    .resource-card.resource-card-6x4:hover .subtitle {
+      max-height: 30px; }
+    .resource-card.resource-card-6x4 .abstract {
+      display: none; }
+    .resource-card.resource-card-6x4 .abstract {
+      font-size: 13px;
+      margin-top: 0;
+      line-height: 15px;
+      max-height: 30px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical;
+      max-height: 0;
+      display: block;
+      transition: 0.4s ease; }
+    .resource-card.resource-card-6x4:hover .abstract {
+      margin-top: 10px;
+      max-height: 30px; }
+  .resource-card.resource-card-4x3 {
+    width: 220px;
+    height: 160px;
+    -webkit-flex-direction: column; }
+    .resource-card.resource-card-4x3 .photo {
+      -webkit-flex: 1 1 auto;
+      border-bottom: 1px solid #ddd; }
+    .resource-card.resource-card-4x3 .resource-card-text {
+      margin: 10px;
+      padding-right: 26px; }
+    .resource-card.resource-card-4x3 .icon {
+      position: absolute;
+      right: 10px;
+      bottom: 10px;
+      width: 16px;
+      height: 16px; }
+    .resource-card.resource-card-4x3 .title {
+      font-size: 16px;
+      line-height: 20px;
+      max-height: 40px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical; }
+    .resource-card.resource-card-4x3 .subtitle {
+      font-size: 13px;
+      line-height: 15px;
+      max-height: 30px;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      overflow: hidden;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical;
+      max-height: 0;
+      margin-top: 0;
+      transition: 0.4s ease; }
+    .resource-card.resource-card-4x3:hover .subtitle {
+      max-height: 30px; }
+    .resource-card.resource-card-4x3 .abstract {
+      display: none; }
diff --git a/tools/droiddoc/templates-sdk/assets/images/card_post.png b/tools/droiddoc/templates-sdk/assets/images/card_post.png
new file mode 100644
index 0000000..e061ee9
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/assets/images/card_post.png
Binary files differ
diff --git a/tools/droiddoc/templates-sdk/assets/images/card_sample.png b/tools/droiddoc/templates-sdk/assets/images/card_sample.png
new file mode 100644
index 0000000..3dc36e0
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/assets/images/card_sample.png
Binary files differ
diff --git a/tools/droiddoc/templates-sdk/assets/images/card_training.png b/tools/droiddoc/templates-sdk/assets/images/card_training.png
new file mode 100644
index 0000000..5bb7c81
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/assets/images/card_training.png
Binary files differ
diff --git a/tools/droiddoc/templates-sdk/assets/images/card_video.png b/tools/droiddoc/templates-sdk/assets/images/card_video.png
new file mode 100644
index 0000000..807c0af
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/assets/images/card_video.png
Binary files differ
diff --git a/tools/droiddoc/templates-sdk/assets/js/jd_tag_helpers.js b/tools/droiddoc/templates-sdk/assets/js/jd_tag_helpers.js
new file mode 100644
index 0000000..d179cbc
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/assets/js/jd_tag_helpers.js
@@ -0,0 +1,106 @@
+function mergeArrays() {
+  var arr = arguments[0] || [];
+  for (var i = 1; i < arguments.length; i++) {
+    arr = arr.concat(arguments[i]);
+  }
+  return arr;
+}
+
+var ALL_RESOURCES = mergeArrays(
+  DESIGN_RESOURCES,
+  DISTRIBUTE_RESOURCES,
+  GOOGLE_RESOURCES,
+  GUIDE_RESOURCES,
+  SAMPLES_RESOURCES,
+  TOOLS_RESOURCES,
+  TRAINING_RESOURCES,
+  YOUTUBE_RESOURCES,
+  BLOGGER_RESOURCES
+);
+
+for (var i = 0; i < ALL_RESOURCES.length; i++) {
+  ALL_RESOURCES[i].index = i;
+}
+
+function mergeMaps() {
+  var allRes = {};
+  var offset = 0;
+
+  for (var i = 0; i < arguments.length; i++) {
+    var r = arguments[i];
+    for (var tag in r.map) {
+      allRes[tag] = allRes[tag] || [];
+      allRes[tag] = allRes[tag].concat(r.map[tag].map(function(i){ return ALL_RESOURCES[i + offset]; }));
+    }
+    offset += r.arr.length;
+  }
+
+  return allRes;
+}
+
+function setFromArray(arr) {
+  arr = arr || [];
+  var set = {};
+  for (var i = 0; i < arr.length; i++) {
+    set[arr[i]] = true;
+  }
+  return set;
+}
+
+function buildResourceLookupMap(resourceDict) {
+  var map = {};
+  for (var key in resourceDict) {
+    var dictForKey = {};
+    var srcArr = resourceDict[key];
+    for (var i = 0; i < srcArr.length; i++) {
+      dictForKey[srcArr[i].index] = true;
+    }
+    map[key] = dictForKey;
+  }
+  return map;
+}
+
+// Type lookups
+
+var ALL_RESOURCES_BY_TYPE = {
+  'design': DESIGN_RESOURCES,
+  'distribute': DISTRIBUTE_RESOURCES,
+  'google': GOOGLE_RESOURCES,
+  'guide': GUIDE_RESOURCES,
+  'samples': SAMPLES_RESOURCES,
+  'tools': TOOLS_RESOURCES,
+  'training': TRAINING_RESOURCES,
+  'youtube': YOUTUBE_RESOURCES,
+  'blog': BLOGGER_RESOURCES
+};
+var IS_RESOURCE_OF_TYPE = buildResourceLookupMap(ALL_RESOURCES_BY_TYPE);
+
+// Tag lookups
+
+var ALL_RESOURCES_BY_TAG = mergeMaps(
+  {map:DESIGN_BY_TAG,arr:DESIGN_RESOURCES},
+  {map:DISTRIBUTE_BY_TAG,arr:DISTRIBUTE_RESOURCES},
+  {map:GOOGLE_BY_TAG,arr:GOOGLE_RESOURCES},
+  {map:GUIDE_BY_TAG,arr:GUIDE_RESOURCES},
+  {map:SAMPLES_BY_TAG,arr:SAMPLES_RESOURCES},
+  {map:TOOLS_BY_TAG,arr:TOOLS_RESOURCES},
+  {map:TRAINING_BY_TAG,arr:TRAINING_RESOURCES},
+  {map:YOUTUBE_BY_TAG,arr:YOUTUBE_RESOURCES},
+  {map:BLOGGER_BY_TAG,arr:BLOGGER_RESOURCES}
+);
+var IS_RESOURCE_TAGGED = buildResourceLookupMap(ALL_RESOURCES_BY_TAG);
+
+// Language lookups
+
+var ALL_RESOURCES_BY_LANG = {};
+for (var i = 0; i < ALL_RESOURCES.length; i++) {
+  var res = ALL_RESOURCES[i];
+  var lang = res.lang;
+  if (!lang) {
+    continue;
+  }
+
+  ALL_RESOURCES_BY_LANG[lang] = ALL_RESOURCES_BY_LANG[lang] || [];
+  ALL_RESOURCES_BY_LANG[lang].push(res);
+}
+var IS_RESOURCE_IN_LANG = buildResourceLookupMap(ALL_RESOURCES_BY_LANG);
\ No newline at end of file
diff --git a/tools/droiddoc/templates-sdk/assets/js/resourcecards.js b/tools/droiddoc/templates-sdk/assets/js/resourcecards.js
new file mode 100644
index 0000000..fbba201
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/assets/js/resourcecards.js
@@ -0,0 +1,244 @@
+// Requires jd_tag_helpers.js and the data JS to be loaded.
+
+$(document).ready(function() {
+  $('.resource-widget').each(function() {
+    initResourceWidget(this);
+  });
+});
+
+
+function initResourceWidget(widget) {
+  var $widget = $(widget);
+  var isFlow, isCarousel;
+  isFlow = $widget.hasClass('resource-flow-layout');
+  if (!isFlow) {
+    isCarousel = $widget.hasClass('resource-carousel-layout');
+  }
+
+  // find size of widget by pulling out its class name
+  var sizeCols = 1;
+  var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
+  if (m) {
+    sizeCols = parseInt(m[1], 10);
+  }
+
+  var opts = {
+    source: $widget.data('source'),
+    cardSizes: ($widget.data('cardsizes') || '').split(','),
+    maxResults: parseInt($widget.data('maxresults') || '100'),
+    itemsPerPage: $widget.data('itemsperpage'),
+    sortOrder: $widget.data('sortorder'),
+    query: $widget.data('query'),
+    collectionId: $widget.data('collectionid'),
+    sizeCols: sizeCols
+  };
+
+  // run the search for the set of resources to show
+  var resources = buildResourceList(opts);
+
+  if (isFlow) {
+    drawResourcesFlowWidget($widget, opts, resources);
+  }
+}
+
+
+function drawResourcesFlowWidget($widget, opts, resources) {
+  $widget.empty();
+  var cardSizes = opts.cardSizes || ['4x3'];
+
+  for (var i = 0; i < resources.length; i++) {
+    var resource = resources[i];
+
+    var cardSize = i >= cardSizes.length ? cardSizes[cardSizes.length - 1] : cardSizes[i];
+    cardSize = cardSize.replace(/^\s+|\s+$/,'');
+
+    var $card = $('<a>')
+        .addClass('resource-card resource-card-' + cardSize + ' resource-card-' + resource.type)
+        .attr('href', resource.url);
+
+    $('<img>')
+        .addClass('photo')
+        .attr('src', resource.image || '')
+        .appendTo($card);
+
+    var subtitle = resource.type;
+    if (resource.timestamp) {
+      var d = new Date(resource.timestamp);
+      // TODO: localize, humanize
+      subtitle = (1 + d.getMonth()) + '/' + d.getDate() + '/' + d.getFullYear() + ' on ' + subtitle;
+    }
+
+    $('<div>')
+        .addClass('resource-card-text')
+        .append($('<div>').addClass('icon'))
+        .append($('<div>').addClass('title').text(resource.title))
+        .append($('<div>').addClass('subtitle').text(subtitle))
+        .append($('<div>').addClass('abstract').text(resource.summary))
+        .appendTo($card);
+
+    $card.appendTo($widget);
+  }
+
+  $widget.find('.resource-card .photo').each(function() {
+    var src = $(this).attr('src');
+    if (!src) {
+      $(this).parents('.resource-card').addClass('nophoto');
+      $(this).replaceWith($('<div>')
+          .addClass('photo'));
+    } else {
+      $(this).replaceWith($('<div>')
+          .addClass('photo')
+          .css('background-image', 'url(' + $(this).attr('src') + ')'));
+    }
+  });
+}
+
+
+function buildResourceList(opts) {
+  var maxResults = opts.maxResults || 100;
+
+  switch (opts.source) {
+    case 'query':
+      var query = opts.query || '';
+      var expressions = parseResourceQuery(query);
+      var alreadyAddedResources = {};
+      var allResources = [];
+      for (var i = 0; i < expressions.length; i++) {
+        var clauses = expressions[i];
+
+        // build initial set of resources from first clause
+        var firstClause = clauses[0];
+        var resources = [];
+        switch (firstClause.attr) {
+          case 'type':
+            resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
+            break;
+          case 'lang':
+            resources = ALL_RESOURCES_BY_LANG[firstClause.value];
+            break;
+          case 'tag':
+            resources = ALL_RESOURCES_BY_TAG[firstClause.value];
+            break;
+        }
+        resources = resources || [];
+
+        // use additional clauses to filter corpus
+        if (clauses.length > 1) {
+          var otherClauses = clauses.slice(1);
+          resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
+        }
+
+        // filter out resources already added
+        if (i > 1) {
+          resources = resources.filter(getResourceNotAlreadyAddedFilter(alreadyAddedResources));
+        }
+
+        allResources = allResources.concat(resources);
+        if (allResources.length > maxResults) {
+          break;
+        }
+      }
+      if (opts.sortOrder) {
+        var attr = opts.sortOrder;
+        var desc = attr.charAt(0) == '-';
+        if (desc) {
+          attr = attr.substring(1);
+        }
+        allResources = allResources.sort(function(x,y) {
+          return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
+        });
+      }
+      return allResources.slice(0, maxResults);
+
+    case 'related':
+      // TODO
+      break;
+
+    case 'collection':
+      // TODO
+      break;
+  }
+}
+
+
+function getResourceNotAlreadyAddedFilter(addedResources) {
+  return function(x) {
+    return !!addedResources[x];
+  };
+}
+
+
+function getResourceMatchesClausesFilter(clauses) {
+  return function(x) {
+    return doesResourceMatchClauses(x, clauses);
+  };
+}
+
+
+function doesResourceMatchClauses(resource, clauses) {
+  for (var i = 0; i < clauses.length; i++) {
+    var map;
+    switch (clauses[i].attr) {
+      case 'type':
+        map = IS_RESOURCE_OF_TYPE[clauses[i].value];
+        break;
+      case 'lang':
+        map = IS_RESOURCE_IN_LANG[clauses[i].value];
+        break;
+      case 'tag':
+        map = IS_RESOURCE_TAGGED[clauses[i].value];
+        break;
+    }
+
+    if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+
+function parseResourceQuery(query) {
+  // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
+  var expressions = [];
+  var expressionStrs = query.split(',') || [];
+  for (var i = 0; i < expressionStrs.length; i++) {
+    var expr = expressionStrs[i] || '';
+
+    // Break expression into clauses (clause e.g. 'tag:foo')
+    var clauses = [];
+    var clauseStrs = expr.split(/(?=[\+\-])/);
+    for (var j = 0; j < clauseStrs.length; j++) {
+      var clauseStr = clauseStrs[j] || '';
+
+      // Get attribute and value from clause (e.g. attribute='tag', value='foo')
+      var parts = clauseStr.split(':');
+      var clause = {};
+
+      clause.attr = parts[0].replace(/\s+/g,'');
+      if (clause.attr) {
+        if (clause.attr.charAt(0) == '+') {
+          clause.attr = clause.attr.substring(1);
+        } else if (clause.attr.charAt(0) == '-') {
+          clause.negative = true;
+          clause.attr = clause.attr.substring(1);
+        }
+      }
+
+      if (parts.length > 1) {
+        clause.value = parts[1].replace(/\s+/g,'');
+      }
+
+      clauses.push(clause);
+    }
+
+    if (!clauses.length) {
+      continue;
+    }
+
+    expressions.push(clauses);
+  }
+
+  return expressions;
+}
+
diff --git a/tools/droiddoc/templates-sdk/customizations.cs b/tools/droiddoc/templates-sdk/customizations.cs
index f5f85fe..69a0d0f 100644
--- a/tools/droiddoc/templates-sdk/customizations.cs
+++ b/tools/droiddoc/templates-sdk/customizations.cs
@@ -268,6 +268,7 @@
 <?cs 
             if:subcount(class.package) ?>
             <ul>
+              <?cs call:list("Annotations", class.package.annotations) ?>
               <?cs call:list("Interfaces", class.package.interfaces) ?>
               <?cs call:list("Classes", class.package.classes) ?>
               <?cs call:list("Enums", class.package.enums) ?>
@@ -276,6 +277,7 @@
             </ul><?cs 
             elif:subcount(package) ?>
             <ul>
+              <?cs call:class_link_list("Annotations", package.annotations) ?>
               <?cs call:class_link_list("Interfaces", package.interfaces) ?>
               <?cs call:class_link_list("Classes", package.classes) ?>
               <?cs call:class_link_list("Enums", package.enums) ?>
diff --git a/tools/droiddoc/templates-sdk/head_tag.cs b/tools/droiddoc/templates-sdk/head_tag.cs
index 0916b00..ff07a19 100644
--- a/tools/droiddoc/templates-sdk/head_tag.cs
+++ b/tools/droiddoc/templates-sdk/head_tag.cs
@@ -58,6 +58,13 @@
 </script>
 <script src="<?cs var:toroot ?>assets/js/docs.js" type="text/javascript"></script>
 
+<!-- RESOURCES LIBRARY -->
+<script src="<?cs if:android.whichdoc != 'online' ?>http:<?cs /if ?>//androiddevdocs-staging.appspot.com/ytblogger_lists_unified.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>jd_lists_unified.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>assets/js/jd_tag_helpers.js" type="text/javascript"></script>
+<link href="<?cs var:toroot ?>assets/css/resourcecards.css"  rel="stylesheet" type="text/css" />
+<script src="<?cs var:toroot ?>assets/js/resourcecards.js" type="text/javascript"></script>
+
 <script type="text/javascript">
   var _gaq = _gaq || [];
   _gaq.push(['_setAccount', 'UA-5831155-1']);
diff --git a/tools/droiddoc/templates-sdk/package.cs b/tools/droiddoc/templates-sdk/package.cs
index 99eaff2..abd49f1 100644
--- a/tools/droiddoc/templates-sdk/package.cs
+++ b/tools/droiddoc/templates-sdk/package.cs
@@ -45,6 +45,7 @@
   <?cs /if ?>
 <?cs /def ?>
 
+<?cs call:class_table("Annotations", package.annotations) ?>
 <?cs call:class_table("Interfaces", package.interfaces) ?>
 <?cs call:class_table("Classes", package.classes) ?>
 <?cs call:class_table("Enums", package.enums) ?>
diff --git a/tools/post_process_props.py b/tools/post_process_props.py
index 5d1b350..e43ca59 100755
--- a/tools/post_process_props.py
+++ b/tools/post_process_props.py
@@ -16,6 +16,9 @@
 
 import sys
 
+# Usage: post_process_props.py file.prop [blacklist_key, ...]
+# Blacklisted keys are removed from the property file, if present
+
 # See PROP_VALUE_MAX system_properties.h.
 # PROP_VALUE_MAX in system_properties.h includes the termination NUL,
 # so we decrease it by 1 here.
@@ -82,8 +85,9 @@
     for line in self.lines:
       if not line or line.startswith("#"):
         continue
-      key, value = line.split("=", 1)
-      props[key] = value
+      if "=" in line:
+        key, value = line.split("=", 1)
+        props[key] = value
     return props
 
   def get(self, name):
@@ -101,6 +105,10 @@
         return
     self.lines.append(key + value)
 
+  def delete(self, name):
+    key = name + "="
+    self.lines = [ line for line in self.lines if not line.startswith(key) ]
+
   def write(self, f):
     f.write("\n".join(self.lines))
     f.write("\n")
@@ -124,6 +132,10 @@
   if not validate(properties):
     sys.exit(1)
 
+  # Drop any blacklisted keys
+  for key in argv[2:]:
+    properties.delete(key)
+
   f = open(filename, 'w+')
   properties.write(f)
   f.close()
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index f8f2ada..d3f7144 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -24,6 +24,10 @@
 import os.path
 import subprocess
 import sys
+import commands
+import shutil
+
+import simg_map
 
 def RunCommand(cmd):
   """ Echo and run the given command
@@ -38,6 +42,171 @@
   p.communicate()
   return p.returncode
 
+def GetVerityTreeSize(partition_size):
+  cmd = "build_verity_tree -s %d"
+  cmd %= partition_size
+  status, output = commands.getstatusoutput(cmd)
+  if status:
+    print output
+    return False, 0
+  return True, int(output)
+
+def GetVerityMetadataSize(partition_size):
+  cmd = "system/extras/verity/build_verity_metadata.py -s %d"
+  cmd %= partition_size
+  status, output = commands.getstatusoutput(cmd)
+  if status:
+    print output
+    return False, 0
+  return True, int(output)
+
+def AdjustPartitionSizeForVerity(partition_size):
+  """Modifies the provided partition size to account for the verity metadata.
+
+  This information is used to size the created image appropriately.
+  Args:
+    partition_size: the size of the partition to be verified.
+  Returns:
+    The size of the partition adjusted for verity metadata.
+  """
+  success, verity_tree_size = GetVerityTreeSize(partition_size)
+  if not success:
+    return 0;
+  success, verity_metadata_size = GetVerityMetadataSize(partition_size)
+  if not success:
+    return 0
+  return partition_size - verity_tree_size - verity_metadata_size
+
+def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict):
+  cmd = ("build_verity_tree %s %s" % (sparse_image_path, verity_image_path))
+  print cmd
+  status, output = commands.getstatusoutput(cmd)
+  if status:
+    print "Could not build verity tree! Error: %s" % output
+    return False
+  root, salt = output.split()
+  prop_dict["verity_root_hash"] = root
+  prop_dict["verity_salt"] = salt
+  return True
+
+def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt,
+                        block_device, signer_path, key):
+  cmd = ("system/extras/verity/build_verity_metadata.py %s %s %s %s %s %s %s" %
+              (image_size,
+              verity_metadata_path,
+              root_hash,
+              salt,
+              block_device,
+              signer_path,
+              key))
+  print cmd
+  status, output = commands.getstatusoutput(cmd)
+  if status:
+    print "Could not build verity metadata! Error: %s" % output
+    return False
+  return True
+
+def Append2Simg(sparse_image_path, unsparse_image_path, error_message):
+  """Appends the unsparse image to the given sparse image.
+
+  Args:
+    sparse_image_path: the path to the (sparse) image
+    unsparse_image_path: the path to the (unsparse) image
+  Returns:
+    True on success, False on failure.
+  """
+  cmd = "append2simg %s %s"
+  cmd %= (sparse_image_path, unsparse_image_path)
+  print cmd
+  status, output = commands.getstatusoutput(cmd)
+  if status:
+    print "%s: %s" % (error_message, output)
+    return False
+  return True
+
+def BuildVerifiedImage(data_image_path, verity_image_path, verity_metadata_path):
+  if not Append2Simg(data_image_path, verity_metadata_path, "Could not append verity metadata!"):
+    return False
+  if not Append2Simg(data_image_path, verity_image_path, "Could not append verity tree!"):
+    return False
+  return True
+
+def UnsparseImage(sparse_image_path, replace=True):
+  img_dir = os.path.dirname(sparse_image_path)
+  unsparse_image_path = "unsparse_" + os.path.basename(sparse_image_path)
+  unsparse_image_path = os.path.join(img_dir, unsparse_image_path)
+  if os.path.exists(unsparse_image_path):
+    if replace:
+      os.unlink(unsparse_image_path)
+    else:
+      return True, unsparse_image_path
+  inflate_command = ["simg2img", sparse_image_path, unsparse_image_path]
+  exit_code = RunCommand(inflate_command)
+  if exit_code != 0:
+    os.remove(unsparse_image_path)
+    return False, None
+  return True, unsparse_image_path
+
+def MappedUnsparseImage(sparse_image_path, unsparse_image_path,
+                        map_path, mapped_unsparse_image_path):
+  if simg_map.ComputeMap(sparse_image_path, unsparse_image_path,
+                         map_path, mapped_unsparse_image_path):
+    return False
+  return True
+
+def MakeVerityEnabledImage(out_file, prop_dict):
+  """Creates an image that is verifiable using dm-verity.
+
+  Args:
+    out_file: the location to write the verifiable image at
+    prop_dict: a dictionary of properties required for image creation and verification
+  Returns:
+    True on success, False otherwise.
+  """
+  # get properties
+  image_size = prop_dict["partition_size"]
+  block_dev = prop_dict["verity_block_device"]
+  signer_key = prop_dict["verity_key"]
+  signer_path = prop_dict["verity_signer_cmd"]
+
+  # make a tempdir
+  tempdir_name = os.path.join(os.path.dirname(out_file), "verity_images")
+  if os.path.exists(tempdir_name):
+    shutil.rmtree(tempdir_name)
+  os.mkdir(tempdir_name)
+
+  # get partial image paths
+  verity_image_path = os.path.join(tempdir_name, "verity.img")
+  verity_metadata_path = os.path.join(tempdir_name, "verity_metadata.img")
+
+  # build the verity tree and get the root hash and salt
+  if not BuildVerityTree(out_file, verity_image_path, prop_dict):
+    shutil.rmtree(tempdir_name)
+    return False
+
+  # build the metadata blocks
+  root_hash = prop_dict["verity_root_hash"]
+  salt = prop_dict["verity_salt"]
+  if not BuildVerityMetadata(image_size,
+                              verity_metadata_path,
+                              root_hash,
+                              salt,
+                              block_dev,
+                              signer_path,
+                              signer_key):
+    shutil.rmtree(tempdir_name)
+    return False
+
+  # build the full verified image
+  if not BuildVerifiedImage(out_file,
+                            verity_image_path,
+                            verity_metadata_path):
+    shutil.rmtree(tempdir_name)
+    return False
+
+  shutil.rmtree(tempdir_name)
+  return True
+
 def BuildImage(in_dir, prop_dict, out_file):
   """Build an image to out_file from in_dir with property prop_dict.
 
@@ -52,6 +221,16 @@
   build_command = []
   fs_type = prop_dict.get("fs_type", "")
   run_fsck = False
+
+  # adjust the partition size to make room for the hashes if this is to be verified
+  if prop_dict.get("verity") == "true":
+    partition_size = int(prop_dict.get("partition_size"))
+    adjusted_size = AdjustPartitionSizeForVerity(partition_size)
+    if not adjusted_size:
+      return False
+    prop_dict["partition_size"] = str(adjusted_size)
+    prop_dict["original_partition_size"] = str(partition_size)
+
   if fs_type.startswith("ext"):
     build_command = ["mkuserimg.sh"]
     if "extfs_sparse_flag" in prop_dict:
@@ -59,8 +238,9 @@
       run_fsck = True
     build_command.extend([in_dir, out_file, fs_type,
                           prop_dict["mount_point"]])
-    if "partition_size" in prop_dict:
-      build_command.append(prop_dict["partition_size"])
+    build_command.append(prop_dict["partition_size"])
+    if "timestamp" in prop_dict:
+      build_command.extend(["-T", str(prop_dict["timestamp"])])
     if "selinux_fc" in prop_dict:
       build_command.append(prop_dict["selinux_fc"])
   else:
@@ -77,14 +257,14 @@
   if exit_code != 0:
     return False
 
+  # create the verified image if this is to be verified
+  if prop_dict.get("verity") == "true":
+    if not MakeVerityEnabledImage(out_file, prop_dict):
+      return False
+
   if run_fsck and prop_dict.get("skip_fsck") != "true":
-    # Inflate the sparse image
-    unsparse_image = os.path.join(
-        os.path.dirname(out_file), "unsparse_" + os.path.basename(out_file))
-    inflate_command = ["simg2img", out_file, unsparse_image]
-    exit_code = RunCommand(inflate_command)
-    if exit_code != 0:
-      os.remove(unsparse_image)
+    success, unsparse_image = UnsparseImage(out_file, replace=False)
+    if not success:
       return False
 
     # Run e2fsck on the inflated image file
@@ -104,6 +284,10 @@
     mount_point: such as "system", "data" etc.
   """
   d = {}
+  if "build.prop" in glob_dict:
+    bp = glob_dict["build.prop"]
+    if "ro.build.date.utc" in bp:
+      d["timestamp"] = bp["ro.build.date.utc"]
 
   def copy_prop(src_p, dest_p):
     if src_p in glob_dict:
@@ -114,6 +298,10 @@
       "mkyaffs2_extra_flags",
       "selinux_fc",
       "skip_fsck",
+      "verity",
+      "verity_block_device",
+      "verity_key",
+      "verity_signer_cmd"
       )
   for p in common_props:
     copy_prop(p, p)
@@ -131,6 +319,9 @@
   elif mount_point == "vendor":
     copy_prop("vendor_fs_type", "fs_type")
     copy_prop("vendor_size", "partition_size")
+  elif mount_point == "oem":
+    copy_prop("fs_type", "fs_type")
+    copy_prop("oem_size", "partition_size")
 
   return d
 
@@ -169,6 +360,8 @@
     mount_point = "cache"
   elif image_filename == "vendor.img":
     mount_point = "vendor"
+  elif image_filename == "oem.img":
+    mount_point = "oem"
   else:
     print >> sys.stderr, "error: unknown image file name ", image_filename
     exit(1)
diff --git a/tools/releasetools/check_target_files_signatures b/tools/releasetools/check_target_files_signatures
index 45d30a6..b2f46c1 100755
--- a/tools/releasetools/check_target_files_signatures
+++ b/tools/releasetools/check_target_files_signatures
@@ -41,8 +41,8 @@
 
 import sys
 
-if sys.hexversion < 0x02040000:
-  print >> sys.stderr, "Python 2.4 or newer is required."
+if sys.hexversion < 0x02070000:
+  print >> sys.stderr, "Python 2.7 or newer is required."
   sys.exit(1)
 
 import os
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index f179717..fea335e 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -84,17 +84,24 @@
       pass
 
 
-def LoadInfoDict(zip):
+def LoadInfoDict(input):
   """Read and parse the META/misc_info.txt key/value pairs from the
   input target files and return a dict."""
 
+  def read_helper(fn):
+    if isinstance(input, zipfile.ZipFile):
+      return input.read(fn)
+    else:
+      path = os.path.join(input, *fn.split("/"))
+      try:
+        with open(path) as f:
+          return f.read()
+      except IOError, e:
+        if e.errno == errno.ENOENT:
+          raise KeyError(fn)
   d = {}
   try:
-    for line in zip.read("META/misc_info.txt").split("\n"):
-      line = line.strip()
-      if not line or line.startswith("#"): continue
-      k, v = line.split("=", 1)
-      d[k] = v
+    d = LoadDictionaryFromLines(read_helper("META/misc_info.txt").split("\n"))
   except KeyError:
     # ok if misc_info.txt doesn't exist
     pass
@@ -105,20 +112,20 @@
 
   if "mkyaffs2_extra_flags" not in d:
     try:
-      d["mkyaffs2_extra_flags"] = zip.read("META/mkyaffs2-extra-flags.txt").strip()
+      d["mkyaffs2_extra_flags"] = read_helper("META/mkyaffs2-extra-flags.txt").strip()
     except KeyError:
       # ok if flags don't exist
       pass
 
   if "recovery_api_version" not in d:
     try:
-      d["recovery_api_version"] = zip.read("META/recovery-api-version.txt").strip()
+      d["recovery_api_version"] = read_helper("META/recovery-api-version.txt").strip()
     except KeyError:
       raise ValueError("can't find recovery API version in input target-files")
 
   if "tool_extensions" not in d:
     try:
-      d["tool_extensions"] = zip.read("META/tool-extensions.txt").strip()
+      d["tool_extensions"] = read_helper("META/tool-extensions.txt").strip()
     except KeyError:
       # ok if extensions don't exist
       pass
@@ -127,7 +134,7 @@
     d["fstab_version"] = "1"
 
   try:
-    data = zip.read("META/imagesizes.txt")
+    data = read_helper("META/imagesizes.txt")
     for line in data.split("\n"):
       if not line: continue
       name, value = line.split(" ", 1)
@@ -152,33 +159,36 @@
   makeint("boot_size")
   makeint("fstab_version")
 
-  d["fstab"] = LoadRecoveryFSTab(zip, d["fstab_version"])
-  d["build.prop"] = LoadBuildProp(zip)
+  d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"])
+  d["build.prop"] = LoadBuildProp(read_helper)
   return d
 
-def LoadBuildProp(zip):
+def LoadBuildProp(read_helper):
   try:
-    data = zip.read("SYSTEM/build.prop")
+    data = read_helper("SYSTEM/build.prop")
   except KeyError:
     print "Warning: could not find SYSTEM/build.prop in %s" % zip
     data = ""
+  return LoadDictionaryFromLines(data.split("\n"))
 
+def LoadDictionaryFromLines(lines):
   d = {}
-  for line in data.split("\n"):
+  for line in lines:
     line = line.strip()
     if not line or line.startswith("#"): continue
-    name, value = line.split("=", 1)
-    d[name] = value
+    if "=" in line:
+      name, value = line.split("=", 1)
+      d[name] = value
   return d
 
-def LoadRecoveryFSTab(zip, fstab_version):
+def LoadRecoveryFSTab(read_helper, fstab_version):
   class Partition(object):
     pass
 
   try:
-    data = zip.read("RECOVERY/RAMDISK/etc/recovery.fstab")
+    data = read_helper("RECOVERY/RAMDISK/etc/recovery.fstab")
   except KeyError:
-    print "Warning: could not find RECOVERY/RAMDISK/etc/recovery.fstab in %s." % zip
+    print "Warning: could not find RECOVERY/RAMDISK/etc/recovery.fstab"
     data = ""
 
   if fstab_version == 1:
@@ -342,9 +352,12 @@
   else:
     print "building image from target_files %s..." % (tree_subdir,)
     fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
-    return File(name, BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
-                                         os.path.join(unpack_dir, fs_config),
-                                         info_dict))
+    data = BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
+                              os.path.join(unpack_dir, fs_config),
+                              info_dict)
+    if data:
+      return File(name, data)
+    return None
 
 
 def UnzipTemp(filename, pattern=None):
@@ -725,11 +738,14 @@
     return result
 
 
-def ZipWriteStr(zip, filename, data, perms=0644):
+def ZipWriteStr(zip, filename, data, perms=0644, compression=None):
   # 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
+  if compression is None:
+    zinfo.compress_type = zip.compression
+  else:
+    zinfo.compress_type = compression
   zinfo.external_attr = perms << 16
   zip.writestr(zinfo, data)
 
@@ -756,6 +772,7 @@
           if x == ".py":
             f = b
           info = imp.find_module(f, [d])
+        print "loaded device-specific extensions from", path
         self.module = imp.load_module("device_specific", *info)
       except ImportError:
         print "unable to load device-specific module; assuming none"
@@ -834,8 +851,8 @@
     t.flush()
     return t
 
-  def AddToZip(self, z):
-    ZipWriteStr(z, self.name, self.data)
+  def AddToZip(self, z, compression=None):
+    ZipWriteStr(z, self.name, self.data, compression=compression)
 
 DIFF_PROGRAM_BY_EXT = {
     ".gz" : "imgdiff",
@@ -972,3 +989,95 @@
       save = True
   cert = "".join(cert).decode('base64')
   return cert
+
+def XDelta3(source_path, target_path, output_path):
+  diff_program = ["xdelta3", "-0", "-B", str(64<<20), "-e", "-f", "-s"]
+  diff_program.append(source_path)
+  diff_program.append(target_path)
+  diff_program.append(output_path)
+  p = Run(diff_program, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+  p.communicate()
+  assert p.returncode == 0, "Couldn't produce patch"
+
+def XZ(path):
+  compress_program = ["xz", "-zk", "-9", "--check=crc32"]
+  compress_program.append(path)
+  p = Run(compress_program, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+  p.communicate()
+  assert p.returncode == 0, "Couldn't compress patch"
+
+def MakeSystemPatch(source_file, target_file):
+  with tempfile.NamedTemporaryFile() as output_file:
+    XDelta3(source_file.name, target_file.name, output_file.name)
+    XZ(output_file.name)
+    with open(output_file.name + ".xz") as patch_file:
+      patch_data = patch_file.read()
+      os.unlink(patch_file.name)
+      return File("system.muimg.p", patch_data)
+
+def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
+                      info_dict=None):
+  """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.  info should be the dictionary returned by
+  common.LoadInfoDict() on the input target_files.
+  """
+
+  if info_dict is None:
+    info_dict = OPTIONS.info_dict
+
+  diff_program = ["imgdiff"]
+  path = os.path.join(input_dir, "SYSTEM", "etc", "recovery-resource.dat")
+  if os.path.exists(path):
+    diff_program.append("-b")
+    diff_program.append(path)
+    bonus_args = "-b /system/etc/recovery-resource.dat"
+  else:
+    bonus_args = ""
+
+  d = Difference(recovery_img, boot_img, diff_program=diff_program)
+  _, _, patch = d.ComputePatch()
+  output_sink("recovery-from-boot.p", patch)
+
+  boot_type, boot_device = GetTypeAndDevice("/boot", info_dict)
+  recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict)
+
+  sh = """#!/system/bin/sh
+if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
+  applypatch %(bonus_args)s %(boot_type)s:%(boot_device)s:%(boot_size)d:%(boot_sha1)s %(recovery_type)s:%(recovery_device)s %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p && log -t recovery "Installing new recovery image: succeeded" || log -t recovery "Installing new recovery image: failed"
+else
+  log -t recovery "Recovery image already installed"
+fi
+""" % { 'boot_size': boot_img.size,
+        'boot_sha1': boot_img.sha1,
+        'recovery_size': recovery_img.size,
+        'recovery_sha1': recovery_img.sha1,
+        'boot_type': boot_type,
+        'boot_device': boot_device,
+        'recovery_type': recovery_type,
+        'recovery_device': recovery_device,
+        'bonus_args': bonus_args,
+        }
+
+  # The install script location moved from /system/etc to /system/bin
+  # in the L release.  Parse the init.rc file to find out where the
+  # target-files expects it to be, and put it there.
+  sh_location = "etc/install-recovery.sh"
+  try:
+    with open(os.path.join(input_dir, "BOOT", "RAMDISK", "init.rc")) as f:
+      for line in f:
+        m = re.match("^service flash_recovery /system/(\S+)\s*$", line)
+        if m:
+          sh_location = m.group(1)
+          print "putting script in", sh_location
+          break
+  except (OSError, IOError), e:
+    print "failed to read init.rc: %s" % (e,)
+
+  output_sink(sh_location, sh)
diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py
index 426b713..43e8542 100644
--- a/tools/releasetools/edify_generator.py
+++ b/tools/releasetools/edify_generator.py
@@ -68,19 +68,43 @@
     with temporary=True) to this one."""
     self.script.extend(other.script)
 
+  def AssertOemProperty(self, name, value):
+    """Assert that a property on the OEM paritition matches a value."""
+    if not name:
+      raise ValueError("must specify an OEM property")
+    if not value:
+      raise ValueError("must specify the OEM value")
+    cmd = ('file_getprop("/oem/oem.prop", "%s") == "%s" || '
+           'abort("This package expects the value \\"%s\\"  for '
+           '\\"%s\\" on the OEM partition; '
+           'this has value \\"" + file_getprop("/oem/oem.prop") + "\\".");'
+           ) % (name, value, name, value)
+    self.script.append(cmd)
+
   def AssertSomeFingerprint(self, *fp):
-    """Assert that the current system build fingerprint is one of *fp."""
+    """Assert that the current recovery build fingerprint is one of *fp."""
     if not fp:
       raise ValueError("must specify some fingerprints")
     cmd = (
-           ' ||\n    '.join([('file_getprop("/system/build.prop", '
-                         '"ro.build.fingerprint") == "%s"')
+           ' ||\n    '.join([('getprop("ro.build.fingerprint") == "%s"')
                         % i for i in fp]) +
            ' ||\n    abort("Package expects build fingerprint of %s; this '
            'device has " + getprop("ro.build.fingerprint") + ".");'
            ) % (" or ".join(fp),)
     self.script.append(cmd)
 
+  def AssertSomeThumbprint(self, *fp):
+    """Assert that the current recovery build thumbprint is one of *fp."""
+    if not fp:
+      raise ValueError("must specify some thumbprints")
+    cmd = (
+           ' ||\n    '.join([('getprop("ro.build.thumbprint") == "%s"')
+                        % i for i in fp]) +
+           ' ||\n    abort("Package expects build thumbprint of %s; this '
+           'device has " + getprop("ro.build.thumbprint") + ".");'
+           ) % (" or ".join(fp),)
+    self.script.append(cmd)
+
   def AssertOlderBuild(self, timestamp, timestamp_text):
     """Assert that the build on the device is older (or the same as)
     the given timestamp."""
@@ -178,6 +202,16 @@
                          (p.fs_type, common.PARTITION_TYPES[p.fs_type],
                           p.device, p.length, p.mount_point))
 
+  def WipeBlockDevice(self, partition):
+    if partition != "/system":
+      raise ValueError(("WipeBlockDevice currently only works "
+                        "on /system, not %s\n") % (partition,))
+    fstab = self.info.get("fstab", None)
+    size = self.info.get("system_size", None)
+    device = fstab[partition].device
+
+    self.script.append('wipe_block_device("%s", %s);' % (device, size))
+
   def DeleteFiles(self, file_list):
     """Delete all files in file_list."""
     if not file_list: return
@@ -212,7 +246,7 @@
     cmd = "".join(cmd)
     self.script.append(self._WordWrap(cmd))
 
-  def WriteRawImage(self, mount_point, fn):
+  def WriteRawImage(self, mount_point, fn, mapfn=None):
     """Write the given package file into the partition for the given
     mount point."""
 
@@ -226,8 +260,13 @@
             'write_raw_image(package_extract_file("%(fn)s"), "%(device)s");'
             % args)
       elif partition_type == "EMMC":
-        self.script.append(
-            'package_extract_file("%(fn)s", "%(device)s");' % args)
+        if mapfn:
+          args["map"] = mapfn
+          self.script.append(
+              'package_extract_file("%(fn)s", "%(device)s", "%(map)s");' % args)
+        else:
+          self.script.append(
+              'package_extract_file("%(fn)s", "%(device)s");' % args)
       else:
         raise ValueError("don't know how to write \"%s\" partitions" % (p.fs_type,))
 
@@ -293,6 +332,13 @@
     if input_path is None:
       data = input_zip.read("OTA/bin/updater")
     else:
-      data = open(os.path.join(input_path, "updater")).read()
+      data = open(input_path, "rb").read()
     common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-binary",
                        data, perms=0755)
+
+  def Syspatch(self, filename, target_mapfile, target_sha,
+               source_mapfile, source_sha, patchfile):
+    """Applies a compressed binary patch to a block device."""
+    call = 'syspatch("%s", "%s", "%s", "%s", "%s", "%s");'
+    self.script.append(call % (filename, target_mapfile, target_sha,
+                               source_mapfile, source_sha, patchfile))
diff --git a/tools/releasetools/img_from_target_files b/tools/releasetools/img_from_target_files
new file mode 120000
index 0000000..afaf24b
--- /dev/null
+++ b/tools/releasetools/img_from_target_files
@@ -0,0 +1 @@
+img_from_target_files.py
\ No newline at end of file
diff --git a/tools/releasetools/img_from_target_files b/tools/releasetools/img_from_target_files.py
similarity index 85%
rename from tools/releasetools/img_from_target_files
rename to tools/releasetools/img_from_target_files.py
index e894c42..596a47e 100755
--- a/tools/releasetools/img_from_target_files
+++ b/tools/releasetools/img_from_target_files.py
@@ -31,8 +31,8 @@
 
 import sys
 
-if sys.hexversion < 0x02040000:
-  print >> sys.stderr, "Python 2.4 or newer is required."
+if sys.hexversion < 0x02070000:
+  print >> sys.stderr, "Python 2.7 or newer is required."
   sys.exit(1)
 
 import errno
@@ -53,10 +53,14 @@
 OPTIONS = common.OPTIONS
 
 
-def AddSystem(output_zip):
+def AddSystem(output_zip, sparse=True):
   """Turn the contents of SYSTEM into a system image and store it in
   output_zip."""
+  data = BuildSystem(OPTIONS.input_tmp, OPTIONS.info_dict, sparse=sparse)
+  common.ZipWriteStr(output_zip, "system.img", data)
 
+
+def BuildSystem(input_dir, info_dict, sparse=True, map_file=None):
   print "creating system.img..."
 
   img = tempfile.NamedTemporaryFile()
@@ -65,8 +69,8 @@
   # mkyaffs2image.  It wants "system" but we have a directory named
   # "SYSTEM", so create a symlink.
   try:
-    os.symlink(os.path.join(OPTIONS.input_tmp, "SYSTEM"),
-               os.path.join(OPTIONS.input_tmp, "system"))
+    os.symlink(os.path.join(input_dir, "SYSTEM"),
+               os.path.join(input_dir, "system"))
   except OSError, e:
       # bogus error on my mac version?
       #   File "./build/tools/releasetools/img_from_target_files", line 86, in AddSystem
@@ -75,21 +79,48 @@
     if (e.errno == errno.EEXIST):
       pass
 
-  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict,
-                                                    "system")
-  fstab = OPTIONS.info_dict["fstab"]
+  image_props = build_image.ImagePropFromGlobalDict(info_dict, "system")
+  fstab = info_dict["fstab"]
   if fstab:
     image_props["fs_type" ] = fstab["/system"].fs_type
-  succ = build_image.BuildImage(os.path.join(OPTIONS.input_tmp, "system"),
+  succ = build_image.BuildImage(os.path.join(input_dir, "system"),
                                 image_props, img.name)
   assert succ, "build system.img image failed"
 
-  img.seek(os.SEEK_SET, 0)
-  data = img.read()
-  img.close()
+  mapdata = None
 
-  common.CheckSize(data, "system.img", OPTIONS.info_dict)
-  common.ZipWriteStr(output_zip, "system.img", data)
+  if sparse:
+    img.seek(os.SEEK_SET, 0)
+    data = img.read()
+    img.close()
+  else:
+    success, name = build_image.UnsparseImage(img.name, replace=False)
+    if not success:
+      assert False, "unsparsing system.img failed"
+
+    if map_file:
+      mmap = tempfile.NamedTemporaryFile()
+      mimg = tempfile.NamedTemporaryFile(delete=False)
+      success = build_image.MappedUnsparseImage(
+          img.name, name, mmap.name, mimg.name)
+      if not success:
+        assert False, "creating sparse map failed"
+      os.unlink(name)
+      name = mimg.name
+
+      with open(mmap.name) as f:
+        mapdata = f.read()
+
+    try:
+      with open(name) as f:
+        data = f.read()
+    finally:
+      os.unlink(name)
+
+  if mapdata is None:
+    return data
+  else:
+    return mapdata, data
 
 
 def AddVendor(output_zip):
diff --git a/tools/releasetools/make_recovery_patch b/tools/releasetools/make_recovery_patch
new file mode 100755
index 0000000..08d1450
--- /dev/null
+++ b/tools/releasetools/make_recovery_patch
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2014 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 sys
+
+if sys.hexversion < 0x02070000:
+  print >> sys.stderr, "Python 2.7 or newer is required."
+  sys.exit(1)
+
+import os
+import common
+
+OPTIONS = common.OPTIONS
+
+def main(argv):
+  # def option_handler(o, a):
+  #   return False
+
+  args = common.ParseOptions(argv, __doc__)
+  input_dir, output_dir = args
+
+  OPTIONS.info_dict = common.LoadInfoDict(input_dir)
+
+  recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
+                                         input_dir, "RECOVERY")
+  boot_img = common.GetBootableImage("boot.img", "boot.img",
+                                     input_dir, "BOOT")
+
+  if not recovery_img or not boot_img:
+    sys.exit(0)
+
+  def output_sink(fn, data):
+    with open(os.path.join(output_dir, "SYSTEM", *fn.split("/")), "wb") as f:
+      f.write(data)
+
+  common.MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img)
+
+
+if __name__ == '__main__':
+  main(sys.argv[1:])
diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files
index 8d3f6ce..7019c46 100755
--- a/tools/releasetools/ota_from_target_files
+++ b/tools/releasetools/ota_from_target_files
@@ -21,7 +21,7 @@
 
 Usage:  ota_from_target_files [flags] input_target_files output_ota_package
 
-  -b  (--board_config)  <file>
+  --board_config  <file>
       Deprecated.
 
   -k (--package_key) <key> Key to use to sign the package (default is
@@ -37,6 +37,10 @@
       Generate an incremental OTA using the given target-files zip as
       the starting build.
 
+  -o  (--oem_settings)  <file>
+      Use the file to specify the expected OEM-specific properties
+      on the OEM partition of the intended device.
+
   -w  (--wipe_user_data)
       Generate an OTA package that will wipe the user data partition
       when installed.
@@ -57,12 +61,22 @@
       first, so that any changes made to the system partition are done
       using the new recovery (new kernel, etc.).
 
+  --block
+      Generate a block-based OTA if possible.  Will fall back to a
+      file-based OTA if the target_files is older and doesn't support
+      block-based OTAs.
+
+  -b  (--binary)  <file>
+      Use the given binary as the update-binary in the output package,
+      instead of the binary in the build's target_files.  Use for
+      development only.
+
 """
 
 import sys
 
-if sys.hexversion < 0x02040000:
-  print >> sys.stderr, "Python 2.4 or newer is required."
+if sys.hexversion < 0x02070000:
+  print >> sys.stderr, "Python 2.7 or newer is required."
   sys.exit(1)
 
 import copy
@@ -80,7 +94,9 @@
   from sha import sha as sha1
 
 import common
+import img_from_target_files
 import edify_generator
+import build_image
 
 OPTIONS = common.OPTIONS
 OPTIONS.package_key = None
@@ -95,6 +111,9 @@
 OPTIONS.worker_threads = 3
 OPTIONS.two_step = False
 OPTIONS.no_signing = False
+OPTIONS.block_based = False
+OPTIONS.updater_binary = None
+OPTIONS.oem_source = None
 
 def MostPopularKey(d, default):
   """Given a dict, return the key corresponding to the largest
@@ -347,74 +366,59 @@
                   whole_file=True)
 
 
-def AppendAssertions(script, info_dict):
-  device = GetBuildProp("ro.product.device", info_dict)
-  script.AssertDevice(device)
-
-
-def MakeRecoveryPatch(input_tmp, 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.  info should be the dictionary returned by
-  common.LoadInfoDict() on the input target_files.
-
-  Returns an Item for the shell script, which must be made
-  executable.
-  """
-
-  diff_program = ["imgdiff"]
-  path = os.path.join(input_tmp, "SYSTEM", "etc", "recovery-resource.dat")
-  if os.path.exists(path):
-    diff_program.append("-b")
-    diff_program.append(path)
-    bonus_args = "-b /system/etc/recovery-resource.dat"
+def AppendAssertions(script, info_dict, oem_dict = None):
+  oem_props = info_dict.get("oem_fingerprint_properties")
+  if oem_props is None:
+    device = GetBuildProp("ro.product.device", info_dict)
+    script.AssertDevice(device)
   else:
-    bonus_args = ""
+    if oem_dict is None:
+      raise common.ExternalError("No OEM file provided to answer expected assertions")
+    for prop in oem_props.split():
+      if oem_dict.get(prop) is None:
+        raise common.ExternalError("The OEM file is missing the property %s" % prop)
+      script.AssertOemProperty(prop, oem_dict.get(prop))
 
-  d = common.Difference(recovery_img, boot_img, diff_program=diff_program)
-  _, _, patch = d.ComputePatch()
-  common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
-  Item.Get("system/recovery-from-boot.p", dir=False)
 
-  boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
-  recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
+def HasRecoveryPatch(target_files_zip):
+  try:
+    target_files_zip.getinfo("SYSTEM/recovery-from-boot.p")
+    return True
+  except KeyError:
+    return False
 
-  sh = """#!/system/bin/sh
-if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
-  log -t recovery "Installing new recovery image"
-  applypatch %(bonus_args)s %(boot_type)s:%(boot_device)s:%(boot_size)d:%(boot_sha1)s %(recovery_type)s:%(recovery_device)s %(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,
-        'recovery_size': recovery_img.size,
-        'recovery_sha1': recovery_img.sha1,
-        'boot_type': boot_type,
-        'boot_device': boot_device,
-        'recovery_type': recovery_type,
-        'recovery_device': recovery_device,
-        'bonus_args': bonus_args,
-        }
-  common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
-  return Item.Get("system/etc/install-recovery.sh", dir=False)
+def GetOemProperty(name, oem_props, oem_dict, info_dict):
+  if oem_props is not None and name in oem_props:
+    return oem_dict[name]
+  return GetBuildProp(name, info_dict)
 
 
+def CalculateFingerprint(oem_props, oem_dict, info_dict):
+  if oem_props is None:
+    return GetBuildProp("ro.build.fingerprint", info_dict)
+  return "%s/%s/%s:%s" % (
+    GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
+    GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
+    GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
+    GetBuildProp("ro.build.thumbprint", info_dict))
+
 def WriteFullOTAPackage(input_zip, output_zip):
   # 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(3, OPTIONS.info_dict)
 
-  metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
-                                         OPTIONS.info_dict),
-              "pre-device": GetBuildProp("ro.product.device",
+  oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
+  oem_dict = None
+  if oem_props is not None:
+    if OPTIONS.oem_source is None:
+      raise common.ExternalError("OEM source required for this build")
+    script.Mount("/oem")
+    oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
+
+  metadata = {"post-build": CalculateFingerprint(
+                               oem_props, oem_dict, OPTIONS.info_dict),
+              "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
                                          OPTIONS.info_dict),
               "post-timestamp": GetBuildProp("ro.build.date.utc",
                                              OPTIONS.info_dict),
@@ -429,12 +433,15 @@
       metadata=metadata,
       info_dict=OPTIONS.info_dict)
 
+  has_recovery_patch = HasRecoveryPatch(input_zip)
+  block_based = OPTIONS.block_based and has_recovery_patch
+
   if not OPTIONS.omit_prereq:
     ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
     ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
     script.AssertOlderBuild(ts, ts_text)
 
-  AppendAssertions(script, OPTIONS.info_dict)
+  AppendAssertions(script, OPTIONS.info_dict, oem_dict)
   device_specific.FullOTA_Assertions()
 
   # Two-step package strategy (in chronological order, which is *not*
@@ -478,37 +485,55 @@
 
   device_specific.FullOTA_InstallBegin()
 
-  script.ShowProgress(0.5, 0)
+  system_progress = 0.75
 
   if OPTIONS.wipe_user_data:
-    script.FormatPartition("/data")
+    system_progress -= 0.1
 
   if "selinux_fc" in OPTIONS.info_dict:
     WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
 
-  script.FormatPartition("/system")
-  script.Mount("/system")
-  script.UnpackPackageDir("recovery", "/system")
-  script.UnpackPackageDir("system", "/system")
+  script.ShowProgress(system_progress, 30)
+  if block_based:
+    mapdata, data = img_from_target_files.BuildSystem(
+        OPTIONS.input_tmp, OPTIONS.info_dict,
+        sparse=False, map_file=True)
 
-  symlinks = CopySystemFiles(input_zip, output_zip)
-  script.MakeSymlinks(symlinks)
+    common.ZipWriteStr(output_zip, "system.map", mapdata)
+    common.ZipWriteStr(output_zip, "system.muimg", data)
+    script.WipeBlockDevice("/system")
+    script.WriteRawImage("/system", "system.muimg", mapfn="system.map")
+  else:
+    script.FormatPartition("/system")
+    script.Mount("/system")
+    if not has_recovery_patch:
+      script.UnpackPackageDir("recovery", "/system")
+    script.UnpackPackageDir("system", "/system")
+
+    symlinks = CopySystemFiles(input_zip, output_zip)
+    script.MakeSymlinks(symlinks)
 
   boot_img = common.GetBootableImage("boot.img", "boot.img",
                                      OPTIONS.input_tmp, "BOOT")
-  MakeRecoveryPatch(OPTIONS.input_tmp, output_zip, recovery_img, boot_img)
 
-  Item.GetMetadata(input_zip)
-  Item.Get("system").SetPermissions(script)
+  if not block_based:
+    def output_sink(fn, data):
+      common.ZipWriteStr(output_zip, "recovery/" + fn, data)
+      Item.Get("system/" + fn, dir=False)
+
+    common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
+                             recovery_img, boot_img)
+
+    Item.GetMetadata(input_zip)
+    Item.Get("system").SetPermissions(script)
 
   common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
   common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
-  script.ShowProgress(0.2, 0)
 
-  script.ShowProgress(0.2, 10)
+  script.ShowProgress(0.05, 5)
   script.WriteRawImage("/boot", "boot.img")
 
-  script.ShowProgress(0.1, 0)
+  script.ShowProgress(0.2, 10)
   device_specific.FullOTA_InstallEnd()
 
   if OPTIONS.extra_script is not None:
@@ -516,6 +541,10 @@
 
   script.UnmountAll()
 
+  if OPTIONS.wipe_user_data:
+    script.ShowProgress(0.1, 10)
+    script.FormatPartition("/data")
+    
   if OPTIONS.two_step:
     script.AppendExtra("""
 set_stage("%(bcb_dev)s", "");
@@ -528,7 +557,7 @@
 endif;
 endif;
 """ % bcb_dev)
-  script.AddToZip(input_zip, output_zip)
+  script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
   WriteMetadata(metadata, output_zip)
 
 def WritePolicyConfig(file_context, output_zip):
@@ -560,7 +589,7 @@
   try:
     return info_dict.get("build.prop", {})[prop]
   except KeyError:
-    raise common.ExternalError("couldn't find %s in build.prop" % (property,))
+    raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
 
 def AddToKnownPaths(filename, known_paths):
   if filename[-1] == "/":
@@ -573,7 +602,7 @@
     known_paths.add(path)
     dirs.pop()
 
-def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
+def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
   source_version = OPTIONS.source_info_dict["recovery_api_version"]
   target_version = OPTIONS.target_info_dict["recovery_api_version"]
 
@@ -599,6 +628,303 @@
       metadata=metadata,
       info_dict=OPTIONS.info_dict)
 
+  source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
+  target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
+  metadata["pre-build"] = source_fp
+  metadata["post-build"] = target_fp
+
+  source_boot = common.GetBootableImage(
+      "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
+      OPTIONS.source_info_dict)
+  target_boot = common.GetBootableImage(
+      "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
+  updating_boot = (not OPTIONS.two_step and
+                   (source_boot.data != target_boot.data))
+
+  source_recovery = common.GetBootableImage(
+      "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
+      OPTIONS.source_info_dict)
+  target_recovery = common.GetBootableImage(
+      "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
+  updating_recovery = (source_recovery.data != target_recovery.data)
+
+  with tempfile.NamedTemporaryFile() as src_file:
+    with tempfile.NamedTemporaryFile() as tgt_file:
+      print "building source system image..."
+      src_file = tempfile.NamedTemporaryFile()
+      src_mapdata, src_data = img_from_target_files.BuildSystem(
+          OPTIONS.source_tmp, OPTIONS.source_info_dict,
+          sparse=False, map_file=True)
+
+      src_sys_sha1 = sha1(src_data).hexdigest()
+      print "source system sha1:", src_sys_sha1
+      src_file.write(src_data)
+
+      print "building target system image..."
+      tgt_file = tempfile.NamedTemporaryFile()
+      tgt_mapdata, tgt_data = img_from_target_files.BuildSystem(
+          OPTIONS.target_tmp, OPTIONS.target_info_dict,
+          sparse=False, map_file=True)
+      tgt_sys_sha1 = sha1(tgt_data).hexdigest()
+      print "target system sha1:", tgt_sys_sha1
+      tgt_sys_len = len(tgt_data)
+      tgt_file.write(tgt_data)
+
+      system_type, system_device = common.GetTypeAndDevice("/system", OPTIONS.info_dict)
+      system_patch = common.MakeSystemPatch(src_file, tgt_file)
+
+      TestBlockPatch(src_data, src_mapdata, system_patch.data, tgt_mapdata, tgt_sys_sha1)
+      src_data = None
+      tgt_data = None
+
+      system_patch.AddToZip(output_zip, compression=zipfile.ZIP_STORED)
+      src_mapfilename = system_patch.name + ".src.map"
+      common.ZipWriteStr(output_zip, src_mapfilename, src_mapdata)
+      tgt_mapfilename = system_patch.name + ".tgt.map"
+      common.ZipWriteStr(output_zip, tgt_mapfilename, tgt_mapdata)
+
+  oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
+  oem_dict = None
+  if oem_props is not None:
+    if OPTIONS.oem_source is None:
+      raise common.ExternalError("OEM source required for this build")
+    script.Mount("/oem")
+    oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
+
+  AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
+  device_specific.IncrementalOTA_Assertions()
+
+  # Two-step incremental package strategy (in chronological order,
+  # which is *not* the order in which the generated script has
+  # things):
+  #
+  # if stage is not "2/3" or "3/3":
+  #    do verification on current system
+  #    write recovery image to boot partition
+  #    set stage to "2/3"
+  #    reboot to boot partition and restart recovery
+  # else if stage is "2/3":
+  #    write recovery image to recovery partition
+  #    set stage to "3/3"
+  #    reboot to recovery partition and restart recovery
+  # else:
+  #    (stage must be "3/3")
+  #    perform update:
+  #       patch system files, etc.
+  #       force full install of new boot image
+  #       set up system to update recovery partition on first boot
+  #    complete script normally (allow recovery to mark itself finished and reboot)
+
+  if OPTIONS.two_step:
+    if not OPTIONS.info_dict.get("multistage_support", None):
+      assert False, "two-step packages not supported by this build"
+    fs = OPTIONS.info_dict["fstab"]["/misc"]
+    assert fs.fs_type.upper() == "EMMC", \
+        "two-step packages only supported on devices with EMMC /misc partitions"
+    bcb_dev = {"bcb_dev": fs.device}
+    common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
+    script.AppendExtra("""
+if get_stage("%(bcb_dev)s", "stage") == "2/3" then
+""" % bcb_dev)
+    script.AppendExtra("sleep(20);\n");
+    script.WriteRawImage("/recovery", "recovery.img")
+    script.AppendExtra("""
+set_stage("%(bcb_dev)s", "3/3");
+reboot_now("%(bcb_dev)s", "recovery");
+else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
+""" % bcb_dev)
+
+  script.Print("Verifying current system...")
+
+  device_specific.IncrementalOTA_VerifyBegin()
+
+  if oem_props is None:
+    script.AssertSomeFingerprint(source_fp, target_fp)
+  else:
+    script.AssertSomeThumbprint(
+        GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
+        GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
+
+  if updating_boot:
+    d = common.Difference(target_boot, source_boot)
+    _, _, d = d.ComputePatch()
+    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)
+
+    boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
+
+    script.PatchCheck("%s:%s:%d:%s:%d:%s" %
+                      (boot_type, boot_device,
+                       source_boot.size, source_boot.sha1,
+                       target_boot.size, target_boot.sha1))
+
+  device_specific.IncrementalOTA_VerifyEnd()
+
+  if OPTIONS.two_step:
+    script.WriteRawImage("/boot", "recovery.img")
+    script.AppendExtra("""
+set_stage("%(bcb_dev)s", "2/3");
+reboot_now("%(bcb_dev)s", "");
+else
+""" % bcb_dev)
+
+  script.Comment("---- start making changes here ----")
+
+  device_specific.IncrementalOTA_InstallBegin()
+
+  script.Print("Patching system image...")
+  script.Syspatch(system_device,
+                  tgt_mapfilename, tgt_sys_sha1,
+                  src_mapfilename, src_sys_sha1,
+                  system_patch.name)
+
+  if OPTIONS.two_step:
+    common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
+    script.WriteRawImage("/boot", "boot.img")
+    print "writing full boot image (forced by two-step mode)"
+
+  if not OPTIONS.two_step:
+    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("%s:%s:%d:%s:%d:%s"
+                        % (boot_type, boot_device,
+                           source_boot.size, source_boot.sha1,
+                           target_boot.size, target_boot.sha1),
+                        "-",
+                        target_boot.size, target_boot.sha1,
+                        source_boot.sha1, "patch/boot.img.p")
+      print "boot image changed; including."
+    else:
+      print "boot image unchanged; skipping."
+
+  # Do device-specific installation (eg, write radio image).
+  device_specific.IncrementalOTA_InstallEnd()
+
+  if OPTIONS.extra_script is not None:
+    script.AppendExtra(OPTIONS.extra_script)
+
+  if OPTIONS.wipe_user_data:
+    script.Print("Erasing user data...")
+    script.FormatPartition("/data")
+
+  if OPTIONS.two_step:
+    script.AppendExtra("""
+set_stage("%(bcb_dev)s", "");
+endif;
+endif;
+""" % bcb_dev)
+
+  script.SetProgress(1)
+  script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
+  WriteMetadata(metadata, output_zip)
+
+def ParseMap(map_str):
+  x = map_str.split()
+  assert int(x[0]) == 4096
+  assert int(x[1]) == len(x)-2
+  return int(x[0]), [int(i) for i in x[2:]]
+
+def TestBlockPatch(src_muimg, src_map, patch_data, tgt_map, tgt_sha1):
+  src_blksize, src_regions = ParseMap(src_map)
+  tgt_blksize, tgt_regions = ParseMap(tgt_map)
+
+  with tempfile.NamedTemporaryFile() as src_file,\
+       tempfile.NamedTemporaryFile() as patch_file,\
+       tempfile.NamedTemporaryFile() as tgt_file,\
+       tempfile.NamedTemporaryFile() as src_map_file,\
+       tempfile.NamedTemporaryFile() as tgt_map_file:
+
+    src_total = sum(src_regions) * src_blksize
+    src_file.truncate(src_total)
+    p = 0
+    for i in range(0, len(src_regions), 2):
+      c, dc = src_regions[i:i+2]
+      src_file.write(src_muimg[p:(p+c*src_blksize)])
+      p += c*src_blksize
+      src_file.seek(dc*src_blksize, 1)
+    assert src_file.tell() == src_total
+
+    patch_file.write(patch_data)
+
+    tgt_total = sum(tgt_regions) * tgt_blksize
+    tgt_file.truncate(tgt_total)
+
+    src_map_file.write(src_map)
+    tgt_map_file.write(tgt_map)
+
+    src_file.flush()
+    src_map_file.flush()
+    patch_file.flush()
+    tgt_file.flush()
+    tgt_map_file.flush()
+
+    p = common.Run(["syspatch_host", src_file.name, src_map_file.name,
+                    patch_file.name, tgt_file.name, tgt_map_file.name],
+                   stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    stdoutdata, _ = p.communicate()
+    if p.returncode != 0:
+      print stdoutdata
+      raise ValueError("failed to reconstruct target system image from patch")
+
+    h = sha1()
+    for i in range(0, len(tgt_regions), 2):
+      c, dc = tgt_regions[i:i+2]
+      h.update(tgt_file.read(c*tgt_blksize))
+      tgt_file.seek(dc*tgt_blksize, 1)
+
+    if h.hexdigest() != tgt_sha1:
+      raise ValueError("patch reconstructed incorrect target system image")
+
+  print "test of system image patch succeeded"
+
+
+def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
+  target_has_recovery_patch = HasRecoveryPatch(target_zip)
+  source_has_recovery_patch = HasRecoveryPatch(source_zip)
+
+  if (OPTIONS.block_based and
+      target_has_recovery_patch and
+      source_has_recovery_patch):
+    return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
+
+  source_version = OPTIONS.source_info_dict["recovery_api_version"]
+  target_version = OPTIONS.target_info_dict["recovery_api_version"]
+
+  if source_version == 0:
+    print ("WARNING: generating edify script for a source that "
+           "can't install it.")
+  script = edify_generator.EdifyGenerator(source_version,
+                                          OPTIONS.target_info_dict)
+
+  oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
+  oem_dict = None
+  if oem_props is not None:
+    if OPTIONS.oem_source is None:
+      raise common.ExternalError("OEM source required for this build")
+    script.Mount("/oem")
+    oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
+
+  metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
+                                         OPTIONS.source_info_dict),
+              "post-timestamp": GetBuildProp("ro.build.date.utc",
+                                             OPTIONS.target_info_dict),
+              }
+
+  device_specific = common.DeviceSpecificParams(
+      source_zip=source_zip,
+      source_version=source_version,
+      target_zip=target_zip,
+      target_version=target_version,
+      output_zip=output_zip,
+      script=script,
+      metadata=metadata,
+      info_dict=OPTIONS.info_dict)
+
   print "Loading target..."
   target_data = LoadSystemFiles(target_zip)
   print "Loading source..."
@@ -655,7 +981,7 @@
     if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
         path not in known_paths:
       # patch is almost as big as the file; don't bother patching
-      # or a patch + rename cannot take place due to the target 
+      # or a patch + rename cannot take place due to the target
       # directory not existing
       tf.AddToZip(output_zip)
       verbatim_targets.append((tf.name, tf.size))
@@ -667,14 +993,21 @@
       patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
       largest_source_size = max(largest_source_size, sf.size)
 
-  source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
-  target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
+  script.Mount("/system")
+
+  target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
+  source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
+
+  if oem_props is None:
+    script.AssertSomeFingerprint(source_fp, target_fp)
+  else:
+    script.AssertSomeThumbprint(
+        GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
+        GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
+
   metadata["pre-build"] = source_fp
   metadata["post-build"] = target_fp
 
-  script.Mount("/system")
-  script.AssertSomeFingerprint(source_fp, target_fp)
-
   source_boot = common.GetBootableImage(
       "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
       OPTIONS.source_info_dict)
@@ -696,7 +1029,7 @@
   #  0.1 for unpacking verbatim files, symlinking, and doing the
   #      device-specific commands.
 
-  AppendAssertions(script, OPTIONS.target_info_dict)
+  AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
   device_specific.IncrementalOTA_Assertions()
 
   # Two-step incremental package strategy (in chronological order,
@@ -744,9 +1077,6 @@
   device_specific.IncrementalOTA_VerifyBegin()
 
   script.ShowProgress(0.1, 0)
-  total_verify_size = float(sum([i[1].size for i in patch_list]) + 1)
-  if updating_boot:
-    total_verify_size += source_boot.size
   so_far = 0
 
   for tf, sf, size, patch_sha in patch_list:
@@ -754,7 +1084,6 @@
       script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
     script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
     so_far += sf.size
-    script.SetProgress(so_far / total_verify_size)
 
   if updating_boot:
     d = common.Difference(target_boot, source_boot)
@@ -771,7 +1100,6 @@
                        source_boot.size, source_boot.sha1,
                        target_boot.size, target_boot.sha1))
     so_far += source_boot.size
-    script.SetProgress(so_far / total_verify_size)
 
   if patch_list or updating_recovery or updating_boot:
     script.CacheFreeSpaceCheck(largest_source_size)
@@ -795,10 +1123,6 @@
     script.WriteRawImage("/boot", "boot.img")
     print "writing full boot image (forced by two-step mode)"
 
-  if OPTIONS.wipe_user_data:
-    script.Print("Erasing user data...")
-    script.FormatPartition("/data")
-
   script.Print("Removing unneeded files...")
   script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
                      ["/"+i for i in sorted(source_data)
@@ -854,10 +1178,15 @@
     # For older builds where recovery-resource.dat is not present, we
     # use only the boot image as the source.
 
-    MakeRecoveryPatch(OPTIONS.target_tmp, output_zip,
-                      target_recovery, target_boot)
-    script.DeleteFiles(["/system/recovery-from-boot.p",
-                        "/system/etc/install-recovery.sh"])
+    if not target_has_recovery_patch:
+      def output_sink(fn, data):
+        common.ZipWriteStr(output_zip, "recovery/" + fn, data)
+        Item.Get("system/" + fn, dir=False)
+
+      common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
+                               target_recovery, target_boot)
+      script.DeleteFiles(["/system/recovery-from-boot.p",
+                          "/system/etc/install-recovery.sh"])
     print "recovery image changed; including as patch from boot."
   else:
     print "recovery image unchanged; skipping."
@@ -889,7 +1218,7 @@
     script.Print("Unpacking new files...")
     script.UnpackPackageDir("system", "/system")
 
-  if updating_recovery:
+  if updating_recovery and not target_has_recovery_patch:
     script.Print("Unpacking new recovery...")
     script.UnpackPackageDir("recovery", "/system")
 
@@ -934,6 +1263,10 @@
     script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
   script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
 
+  if OPTIONS.wipe_user_data:
+    script.Print("Erasing user data...")
+    script.FormatPartition("/data")
+
   if OPTIONS.two_step:
     script.AppendExtra("""
 set_stage("%(bcb_dev)s", "");
@@ -941,14 +1274,14 @@
 endif;
 """ % bcb_dev)
 
-  script.AddToZip(target_zip, output_zip)
+  script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
   WriteMetadata(metadata, output_zip)
 
 
 def main(argv):
 
   def option_handler(o, a):
-    if o in ("-b", "--board_config"):
+    if o == "--board_config":
       pass   # deprecated
     elif o in ("-k", "--package_key"):
       OPTIONS.package_key = a
@@ -958,6 +1291,8 @@
       OPTIONS.wipe_user_data = True
     elif o in ("-n", "--no_prereq"):
       OPTIONS.omit_prereq = True
+    elif o in ("-o", "--oem_settings"):
+      OPTIONS.oem_source = a
     elif o in ("-e", "--extra_script"):
       OPTIONS.extra_script = a
     elif o in ("-a", "--aslr_mode"):
@@ -969,14 +1304,18 @@
       OPTIONS.worker_threads = int(a)
     elif o in ("-2", "--two_step"):
       OPTIONS.two_step = True
-    elif o in ("--no_signing"):
+    elif o == "--no_signing":
       OPTIONS.no_signing = True
+    elif o == "--block":
+      OPTIONS.block_based = True
+    elif o in ("-b", "--binary"):
+      OPTIONS.updater_binary = a
     else:
       return False
     return True
 
   args = common.ParseOptions(argv, __doc__,
-                             extra_opts="b:k:i:d:wne:a:2",
+                             extra_opts="b:k:i:d:wne:a:2o:",
                              extra_long_opts=["board_config=",
                                               "package_key=",
                                               "incremental_from=",
@@ -987,6 +1326,9 @@
                                               "aslr_mode=",
                                               "two_step",
                                               "no_signing",
+                                              "block",
+                                              "binary=",
+                                              "oem_settings=",
                                               ],
                              extra_option_handler=option_handler)
 
@@ -1015,11 +1357,23 @@
     print "--- target info ---"
     common.DumpInfoDict(OPTIONS.info_dict)
 
+  # If the caller explicitly specified the device-specific extensions
+  # path via -s/--device_specific, use that.  Otherwise, use
+  # META/releasetools.py if it is present in the target target_files.
+  # Otherwise, take the path of the file from 'tool_extensions' in the
+  # info dict and look for that in the local filesystem, relative to
+  # the current directory.
+
   if OPTIONS.device_specific is None:
-    OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
+    from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
+    if os.path.exists(from_input):
+      print "(using device-specific extensions from target_files)"
+      OPTIONS.device_specific = from_input
+    else:
+      OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
+
   if OPTIONS.device_specific is not None:
-    OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
-    print "using device-specific extensions in", OPTIONS.device_specific
+    OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
 
   if OPTIONS.no_signing:
     output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
@@ -1039,6 +1393,9 @@
     OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
     OPTIONS.target_info_dict = OPTIONS.info_dict
     OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
+    if "selinux_fc" in OPTIONS.source_info_dict:
+      OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
+                                                            "file_contexts")
     if OPTIONS.package_key is None:
       OPTIONS.package_key = OPTIONS.source_info_dict.get(
           "default_system_dev_certificate",
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index ab24706..5398cec 100755
--- a/tools/releasetools/sign_target_files_apks
+++ b/tools/releasetools/sign_target_files_apks
@@ -67,8 +67,8 @@
 
 import sys
 
-if sys.hexversion < 0x02040000:
-  print >> sys.stderr, "Python 2.4 or newer is required."
+if sys.hexversion < 0x02070000:
+  print >> sys.stderr, "Python 2.7 or newer is required."
   sys.exit(1)
 
 import base64
@@ -77,6 +77,7 @@
 import errno
 import os
 import re
+import shutil
 import subprocess
 import tempfile
 import zipfile
@@ -139,14 +140,40 @@
   return data
 
 
-def SignApks(input_tf_zip, output_tf_zip, apk_key_map, key_passwords):
+def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
+                       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')])
+  rebuild_recovery = False
+
+  tmpdir = tempfile.mkdtemp()
+  def write_to_temp(fn, attr, data):
+    fn = os.path.join(tmpdir, fn)
+    if fn.endswith("/"):
+      fn = os.path.join(tmpdir, fn)
+      os.mkdir(fn)
+    else:
+      d = os.path.dirname(fn)
+      if d and not os.path.exists(d):
+        os.makedirs(d)
+
+      if attr >> 16 == 0xa1ff:
+        os.symlink(data, fn)
+      else:
+        with open(fn, "wb") as f:
+          f.write(data)
 
   for info in input_tf_zip.infolist():
     data = input_tf_zip.read(info.filename)
     out_info = copy.copy(info)
+
+    if (info.filename.startswith("BOOT/") or
+        info.filename.startswith("RECOVERY/") or
+        info.filename.startswith("META/") or
+        info.filename == "SYSTEM/etc/recovery-resource.dat"):
+      write_to_temp(info.filename, info.external_attr, data)
+
     if info.filename.endswith(".apk"):
       name = os.path.basename(info.filename)
       key = apk_key_map[name]
@@ -163,14 +190,43 @@
       print "rewriting %s:" % (info.filename,)
       new_data = RewriteProps(data)
       output_tf_zip.writestr(out_info, new_data)
+      if info.filename == "RECOVERY/RAMDISK/default.prop":
+        write_to_temp(info.filename, info.external_attr, new_data)
     elif info.filename.endswith("mac_permissions.xml"):
       print "rewriting %s with new keys." % (info.filename,)
       new_data = ReplaceCerts(data)
       output_tf_zip.writestr(out_info, new_data)
+    elif info.filename in ("SYSTEM/recovery-from-boot.p",
+                           "SYSTEM/bin/install-recovery.sh"):
+      rebuild_recovery = True
+    elif (OPTIONS.replace_ota_keys and
+          info.filename in ("RECOVERY/RAMDISK/res/keys",
+                            "SYSTEM/etc/security/otacerts.zip")):
+      # don't copy these files if we're regenerating them below
+      pass
     else:
       # a non-APK file; copy it verbatim
       output_tf_zip.writestr(out_info, data)
 
+  if OPTIONS.replace_ota_keys:
+    new_recovery_keys = ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
+    if new_recovery_keys:
+      write_to_temp("RECOVERY/RAMDISK/res/keys", 0755 << 16, new_recovery_keys)
+
+  if rebuild_recovery:
+    recovery_img = common.GetBootableImage(
+        "recovery.img", "recovery.img", tmpdir, "RECOVERY", info_dict=misc_info)
+    boot_img = common.GetBootableImage(
+        "boot.img", "boot.img", tmpdir, "BOOT", info_dict=misc_info)
+
+    def output_sink(fn, data):
+      output_tf_zip.writestr("SYSTEM/"+fn, data)
+
+    common.MakeRecoveryPatch(tmpdir, output_sink, recovery_img, boot_img,
+                             info_dict=misc_info)
+
+  shutil.rmtree(tmpdir)
+
 
 def ReplaceCerts(data):
   """Given a string of data, replace all occurences of a set
@@ -265,7 +321,8 @@
   for k in keylist:
     m = re.match(r"^(.*)\.x509\.pem$", k)
     if not m:
-      raise common.ExternalError("can't parse \"%s\" from META/otakeys.txt" % (k,))
+      raise common.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")
 
@@ -287,10 +344,11 @@
                   os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar")]
                  + mapped_keys + extra_recovery_keys,
                  stdout=subprocess.PIPE)
-  data, _ = p.communicate()
+  new_recovery_keys, _ = p.communicate()
   if p.returncode != 0:
     raise common.ExternalError("failed to run dumpkeys")
-  common.ZipWriteStr(output_tf_zip, "RECOVERY/RAMDISK/res/keys", data)
+  common.ZipWriteStr(output_tf_zip, "RECOVERY/RAMDISK/res/keys",
+                     new_recovery_keys)
 
   # SystemUpdateActivity uses the x509.pem version of the keys, but
   # put into a zipfile system/etc/security/otacerts.zip.
@@ -304,6 +362,8 @@
   common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip",
                      tempfile.getvalue())
 
+  return new_recovery_keys
+
 
 def BuildKeyMap(misc_info, key_mapping_options):
   for s, d in key_mapping_options:
@@ -375,10 +435,8 @@
   CheckAllApksSigned(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, misc_info)
+  ProcessTargetFiles(input_zip, output_zip, misc_info,
+                     apk_key_map, key_passwords)
 
   input_zip.close()
   output_zip.close()
diff --git a/tools/releasetools/simg_map.py b/tools/releasetools/simg_map.py
new file mode 100644
index 0000000..22dc863
--- /dev/null
+++ b/tools/releasetools/simg_map.py
@@ -0,0 +1,148 @@
+#! /usr/bin/env python
+
+# Copyright (C) 2012 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.
+
+from __future__ import print_function
+import getopt, posixpath, signal, struct, sys
+
+def main():
+  if len(sys.argv) == 4:
+    print("No sparse_image_file specified")
+    usage(me)
+
+  sparse_fn = sys.argv[1]
+  unsparse_fn = sys.argv[2]
+  map_file = sys.argv[3]
+  mapped_unsparse_fn = sys.argv[4]
+
+  return ComputeMap(sparse_fn, unsparse_fn, map_file, mapped_unsparse_fn)
+
+
+def ComputeMap(sparse_fn, unsparse_fn, map_file, mapped_unsparse_fn):
+  care_map = []
+
+  with open(sparse_fn, "rb") as FH:
+    header_bin = FH.read(28)
+    header = struct.unpack("<I4H4I", header_bin)
+
+    magic = header[0]
+    major_version = header[1]
+    minor_version = header[2]
+    file_hdr_sz = header[3]
+    chunk_hdr_sz = header[4]
+    blk_sz = header[5]
+    total_blks = header[6]
+    total_chunks = header[7]
+    image_checksum = header[8]
+
+    if magic != 0xED26FF3A:
+      print("%s: %s: Magic should be 0xED26FF3A but is 0x%08X"
+            % (me, path, magic))
+      return 1
+    if major_version != 1 or minor_version != 0:
+      print("%s: %s: I only know about version 1.0, but this is version %u.%u"
+            % (me, path, major_version, minor_version))
+      return 1
+    if file_hdr_sz != 28:
+      print("%s: %s: The file header size was expected to be 28, but is %u."
+            % (me, path, file_hdr_sz))
+      return 1
+    if chunk_hdr_sz != 12:
+      print("%s: %s: The chunk header size was expected to be 12, but is %u."
+            % (me, path, chunk_hdr_sz))
+      return 1
+
+    print("%s: Total of %u %u-byte output blocks in %u input chunks."
+          % (sparse_fn, total_blks, blk_sz, total_chunks))
+
+    offset = 0
+    for i in range(total_chunks):
+      header_bin = FH.read(12)
+      header = struct.unpack("<2H2I", header_bin)
+      chunk_type = header[0]
+      reserved1 = header[1]
+      chunk_sz = header[2]
+      total_sz = header[3]
+      data_sz = total_sz - 12
+
+      if chunk_type == 0xCAC1:
+        if data_sz != (chunk_sz * blk_sz):
+          print("Raw chunk input size (%u) does not match output size (%u)"
+                % (data_sz, chunk_sz * blk_sz))
+          return 1
+        else:
+          care_map.append((1, chunk_sz))
+          FH.seek(data_sz, 1)
+
+      elif chunk_type == 0xCAC2:
+        print("Fill chunks are not supported")
+        return 1
+
+      elif chunk_type == 0xCAC3:
+        if data_sz != 0:
+          print("Don't care chunk input size is non-zero (%u)" % (data_sz))
+          return 1
+        else:
+          care_map.append((0, chunk_sz))
+
+      elif chunk_type == 0xCAC4:
+        print("CRC32 chunks are not supported")
+
+      else:
+        print("Unknown chunk type 0x%04X not supported" % (chunk_type,))
+        return 1
+
+      offset += chunk_sz
+
+    if total_blks != offset:
+      print("The header said we should have %u output blocks, but we saw %u"
+            % (total_blks, offset))
+
+    junk_len = len(FH.read())
+    if junk_len:
+      print("There were %u bytes of extra data at the end of the file."
+            % (junk_len))
+      return 1
+
+  last_kind = None
+  new_care_map = []
+  for kind, size in care_map:
+    if kind != last_kind:
+      new_care_map.append((kind, size))
+      last_kind = kind
+    else:
+      new_care_map[-1] = (kind, new_care_map[-1][1] + size)
+
+  if new_care_map[0][0] == 0:
+    new_care_map.insert(0, (1, 0))
+  if len(new_care_map) % 2:
+    new_care_map.append((0, 0))
+
+  with open(map_file, "w") as fmap:
+    fmap.write("%d\n%d\n" % (blk_sz, len(new_care_map)))
+    for _, sz in new_care_map:
+      fmap.write("%d\n" % sz)
+
+  with open(unsparse_fn, "rb") as fin:
+    with open(mapped_unsparse_fn, "wb") as fout:
+      for k, sz in care_map:
+        data = fin.read(sz * blk_sz)
+        if k:
+          fout.write(data)
+        else:
+          assert data == "\x00" * len(data)
+
+if __name__ == "__main__":
+  sys.exit(main())