Merge "Build "secdiscard" for securely discarding data from flash devices." into mnc-dr-dev
diff --git a/CleanSpec.mk b/CleanSpec.mk
index bfb27d9..c95ae05 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -359,6 +359,11 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/*)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/*)
 
+# 23 is becoming alive!!!
+$(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/*)
+
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/core/Makefile b/core/Makefile
index c96f04f..c98df0c 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -460,12 +460,12 @@
 INSTALLED_RAMDISK_TARGET := $(BUILT_RAMDISK_TARGET)
 $(INSTALLED_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_RAMDISK_FILES) | $(MINIGZIP)
 	$(call pretty,"Target ram disk: $@")
-	$(hide) $(MKBOOTFS) $(TARGET_ROOT_OUT) | $(MINIGZIP) > $@
+	$(hide) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_ROOT_OUT) | $(MINIGZIP) > $@
 
 .PHONY: ramdisk-nodeps
 ramdisk-nodeps: $(MKBOOTFS) | $(MINIGZIP)
 	@echo "make $@: ignoring dependencies"
-	$(hide) $(MKBOOTFS) $(TARGET_ROOT_OUT) | $(MINIGZIP) > $(INSTALLED_RAMDISK_TARGET)
+	$(hide) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_ROOT_OUT) | $(MINIGZIP) > $(INSTALLED_RAMDISK_TARGET)
 
 ifneq ($(strip $(TARGET_NO_KERNEL)),true)
 
@@ -710,7 +710,11 @@
 endif
 
 ifeq ($(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE),squashfs)
-INTERNAL_USERIMAGES_DEPS += $(MAKE_SQUASHFS) $(MKSQUASHFSUSERIMG)
+INTERNAL_USERIMAGES_DEPS += $(MAKE_SQUASHFS) $(MKSQUASHFSUSERIMG) $(IMG2SIMG)
+endif
+
+ifeq ($(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE),squashfs)
+INTERNAL_USERIMAGES_DEPS += $(MAKE_SQUASHFS) $(MKSQUASHFSUSERIMG) $(IMG2SIMG)
 endif
 
 INTERNAL_USERIMAGES_BINARY_PATHS := $(sort $(dir $(INTERNAL_USERIMAGES_DEPS)))
@@ -729,6 +733,9 @@
 $(if $(BOARD_SYSTEMIMAGE_PARTITION_SIZE),$(hide) echo "system_size=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)" >> $(1))
 $(if $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "system_fs_type=$(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
 $(if $(BOARD_SYSTEMIMAGE_JOURNAL_SIZE),$(hide) echo "system_journal_size=$(BOARD_SYSTEMIMAGE_JOURNAL_SIZE)" >> $(1))
+$(if $(BOARD_HAS_EXT4_RESERVED_BLOCKS),$(hide) echo "has_ext4_reserved_blocks=$(BOARD_HAS_EXT4_RESERVED_BLOCKS)" >> $(1))
+$(if $(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "system_squashfs_compressor=$(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
+$(if $(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "system_squashfs_compressor_opt=$(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
 $(if $(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "userdata_fs_type=$(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
 $(if $(BOARD_USERDATAIMAGE_PARTITION_SIZE),$(hide) echo "userdata_size=$(BOARD_USERDATAIMAGE_PARTITION_SIZE)" >> $(1))
 $(if $(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "cache_fs_type=$(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
@@ -877,7 +884,7 @@
   $(hide) cp $(RECOVERY_INSTALL_OTA_KEYS) $(TARGET_RECOVERY_ROOT_OUT)/res/keys
   $(hide) cat $(INSTALLED_DEFAULT_PROP_TARGET) $(recovery_build_prop) \
           > $(TARGET_RECOVERY_ROOT_OUT)/default.prop
-  $(hide) $(MKBOOTFS) $(TARGET_RECOVERY_ROOT_OUT) | $(MINIGZIP) > $(recovery_ramdisk)
+  $(hide) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RECOVERY_ROOT_OUT) | $(MINIGZIP) > $(recovery_ramdisk)
   $(if $(filter true,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT)), \
     $(hide) $(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1).unsigned, \
     $(hide) $(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1) --id > $(RECOVERYIMAGE_ID_FILE))
@@ -988,10 +995,18 @@
       skip_fsck=true)
   $(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) \
+      $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \
       || ( echo "Out of space? the tree size of $(TARGET_OUT) is (MB): " 1>&2 ;\
            du -sm $(TARGET_OUT) 1>&2;\
-           echo "The max is $$(( $(BOARD_SYSTEMIMAGE_PARTITION_SIZE) / 1048576 )) MB." 1>&2 ;\
+           if [ "$(INTERNAL_USERIMAGES_EXT_VARIANT)" == "ext4" ]; then \
+               maxsize=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE); \
+               if [ "$(BOARD_HAS_EXT4_RESERVED_BLOCKS)" == "true" ]; then \
+                   maxsize=$$((maxsize - 4096 * 4096)); \
+               fi; \
+               echo "The max is $$(( maxsize / 1048576 )) MB." 1>&2 ;\
+           else \
+               echo "The max is $$(( $(BOARD_SYSTEMIMAGE_PARTITION_SIZE) / 1048576 )) MB." 1>&2 ;\
+           fi; \
            mkdir -p $(DIST_DIR); cp $(INSTALLED_FILES_FILE) $(DIST_DIR)/installed-files-rescued.txt; \
            exit 1 )
 endef
@@ -1047,7 +1062,7 @@
   $(call create-system-vendor-symlink)
   $(MKTARBALL) $(FS_GET_STATS) \
     $(PRODUCT_OUT) system $(PRIVATE_SYSTEM_TAR) \
-    $(INSTALLED_SYSTEMTARBALL_TARGET)
+    $(INSTALLED_SYSTEMTARBALL_TARGET) $(TARGET_OUT)
 endef
 
 ifndef SYSTEM_TARBALL_FORMAT
@@ -1115,7 +1130,7 @@
     $(hide) echo $(BOARD_KERNEL_CMDLINE) > $(PRODUCT_OUT)/boot/cmdline
     $(hide) $(MKTARBALL) $(FS_GET_STATS) \
                  $(PRODUCT_OUT) boot $(PRIVATE_BOOT_TAR) \
-                 $(INSTALLED_BOOTTARBALL_TARGET)
+                 $(INSTALLED_BOOTTARBALL_TARGET) $(TARGET_OUT)
 endef
 
 ifndef BOOT_TARBALL_FORMAT
@@ -1159,7 +1174,7 @@
   $(call generate-userimage-prop-dictionary, $(userdataimage_intermediates)/userdata_image_info.txt, skip_fsck=true)
   $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
       ./build/tools/releasetools/build_image.py \
-      $(TARGET_OUT_DATA) $(userdataimage_intermediates)/userdata_image_info.txt $(INSTALLED_USERDATAIMAGE_TARGET)
+      $(TARGET_OUT_DATA) $(userdataimage_intermediates)/userdata_image_info.txt $(INSTALLED_USERDATAIMAGE_TARGET) $(TARGET_OUT)
   $(hide) $(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE))
 endef
 
@@ -1183,7 +1198,7 @@
                   "$(INSTALLED_USERDATATARBALL_TARGET)")
     $(MKTARBALL) $(FS_GET_STATS) \
 		$(PRODUCT_OUT) data $(PRIVATE_USERDATA_TAR) \
-		$(INSTALLED_USERDATATARBALL_TARGET)
+		$(INSTALLED_USERDATATARBALL_TARGET) $(TARGET_OUT)
 endef
 
 userdata_tar := $(PRODUCT_OUT)/userdata.tar
@@ -1214,7 +1229,7 @@
   $(call generate-userimage-prop-dictionary, $(cacheimage_intermediates)/cache_image_info.txt, skip_fsck=true)
   $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
       ./build/tools/releasetools/build_image.py \
-      $(TARGET_OUT_CACHE) $(cacheimage_intermediates)/cache_image_info.txt $(INSTALLED_CACHEIMAGE_TARGET)
+      $(TARGET_OUT_CACHE) $(cacheimage_intermediates)/cache_image_info.txt $(INSTALLED_CACHEIMAGE_TARGET) $(TARGET_OUT)
   $(hide) $(call assert-max-image-size,$(INSTALLED_CACHEIMAGE_TARGET),$(BOARD_CACHEIMAGE_PARTITION_SIZE))
 endef
 
@@ -1252,7 +1267,7 @@
   $(call generate-userimage-prop-dictionary, $(vendorimage_intermediates)/vendor_image_info.txt, skip_fsck=true)
   $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
       ./build/tools/releasetools/build_image.py \
-      $(TARGET_OUT_VENDOR) $(vendorimage_intermediates)/vendor_image_info.txt $(INSTALLED_VENDORIMAGE_TARGET)
+      $(TARGET_OUT_VENDOR) $(vendorimage_intermediates)/vendor_image_info.txt $(INSTALLED_VENDORIMAGE_TARGET) $(TARGET_OUT)
   $(hide) $(call assert-max-image-size,$(INSTALLED_VENDORIMAGE_TARGET),$(BOARD_VENDORIMAGE_PARTITION_SIZE))
 endef
 
@@ -1293,6 +1308,7 @@
   $(HOST_OUT_EXECUTABLES)/build_verity_tree \
   $(HOST_OUT_EXECUTABLES)/verity_signer \
   $(HOST_OUT_EXECUTABLES)/append2simg \
+  $(HOST_OUT_EXECUTABLES)/img2simg \
   $(HOST_OUT_EXECUTABLES)/boot_signer
 
 # Shared libraries.
@@ -1473,6 +1489,9 @@
 ifdef BOARD_RECOVERYIMAGE_PARTITION_SIZE
 	$(hide) echo "recovery_size=$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)" >> $(zip_root)/META/misc_info.txt
 endif
+ifdef BOARD_HAS_EXT4_RESERVED_BLOCKS
+	$(hide) echo "has_ext4_reserved_blocks=$(BOARD_HAS_EXT4_RESERVED_BLOCKS)" >> $(zip_root)/META/misc_info.txt
+endif
 ifdef TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS
 	@# TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS can be empty to indicate that nothing but defaults should be used.
 	$(hide) echo "recovery_mount_options=$(TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS)" >> $(zip_root)/META/misc_info.txt
@@ -1488,7 +1507,7 @@
 	$(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
-	$(hide) echo "blockimgdiff_versions=1,2,3" >> $(zip_root)/META/misc_info.txt
+	$(hide) echo "blockimgdiff_versions=1,2" >> $(zip_root)/META/misc_info.txt
 ifneq ($(OEM_THUMBPRINT_PROPERTIES),)
 	# OTA scripts are only interested in fingerprint related properties
 	$(hide) echo "oem_fingerprint_properties=$(OEM_THUMBPRINT_PROPERTIES)" >> $(zip_root)/META/misc_info.txt
@@ -1500,10 +1519,10 @@
 	$(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
 	@# Run fs_config on all the system, vendor, boot ramdisk,
 	@# and recovery ramdisk files in the zip, and save the output
-	$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/filesystem_config.txt
-	$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="VENDOR/" } /^VENDOR\// {print "vendor/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/vendor_filesystem_config.txt
-	$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="BOOT/RAMDISK/" } /^BOOT\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/boot_filesystem_config.txt
-	$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/recovery_filesystem_config.txt
+	$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/filesystem_config.txt
+	$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="VENDOR/" } /^VENDOR\// {print "vendor/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/vendor_filesystem_config.txt
+	$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="BOOT/RAMDISK/" } /^BOOT\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/boot_filesystem_config.txt
+	$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/recovery_filesystem_config.txt
 	$(hide) (cd $(zip_root) && zip -q ../$(notdir $@) META/*filesystem_config.txt)
 	$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH MKBOOTIMG=$(MKBOOTIMG) \
 	    ./build/tools/releasetools/add_img_to_target_files -p $(HOST_OUT) $@
diff --git a/core/config.mk b/core/config.mk
index 87cd1cf..ba8f310 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -419,6 +419,7 @@
 MAKE_F2FS := $(HOST_OUT_EXECUTABLES)/make_f2fs$(HOST_EXECUTABLE_SUFFIX)
 MKF2FSUSERIMG := $(HOST_OUT_EXECUTABLES)/mkf2fsuserimg.sh
 SIMG2IMG := $(HOST_OUT_EXECUTABLES)/simg2img$(HOST_EXECUTABLE_SUFFIX)
+IMG2SIMG := $(HOST_OUT_EXECUTABLES)/img2simg$(HOST_EXECUTABLE_SUFFIX)
 E2FSCK := $(HOST_OUT_EXECUTABLES)/e2fsck$(HOST_EXECUTABLE_SUFFIX)
 MKTARBALL := build/tools/mktarball.sh
 TUNE2FS := $(HOST_OUT_EXECUTABLES)/tune2fs$(HOST_EXECUTABLE_SUFFIX)
diff --git a/core/java.mk b/core/java.mk
index ae4a602..b371289 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -441,9 +441,10 @@
 my_support_library_sdk_raise :=
 ifneq (,$(filter android-support-%,$(LOCAL_STATIC_JAVA_LIBRARIES)))
 ifdef LOCAL_SDK_VERSION
-ifeq (,$(filter current system_current, $(LOCAL_SDK_VERSION)))
 ifdef TARGET_BUILD_APPS
+ifeq (,$(filter current system_current, $(LOCAL_SDK_VERSION)))
   my_support_library_sdk_raise := $(call java-lib-files, sdk_vcurrent)
+endif
 else
   # For platform build, we can't just raise to the "current" SDK,
   # that would break apps that use APIs removed from the current SDK.
@@ -451,7 +452,6 @@
 endif
 endif
 endif
-endif
 
 # jack already has the libraries in its classpath and doesn't support jars
 legacy_proguard_flags := $(addprefix -libraryjars ,$(my_support_library_sdk_raise) $(full_shared_java_libs))
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index 6cbe81c..e384526 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -53,7 +53,7 @@
   # intermediate builds).  During development, this number remains at the
   # SDK version the branch is based on and PLATFORM_VERSION_CODENAME holds
   # the code-name of the new development work.
-  PLATFORM_SDK_VERSION := 22
+  PLATFORM_SDK_VERSION := 23
 endif
 
 ifeq "" "$(PLATFORM_VERSION_CODENAME)"
@@ -80,7 +80,7 @@
     # assuming the device can only support APIs as of the previous official
     # public release.
     # This value will always be 0 for release builds.
-    PLATFORM_PREVIEW_SDK_VERSION := 1
+    PLATFORM_PREVIEW_SDK_VERSION := 2
   endif
 endif
 
diff --git a/target/board/generic/sepolicy/init.te b/target/board/generic/sepolicy/init.te
new file mode 100644
index 0000000..3aa81d1
--- /dev/null
+++ b/target/board/generic/sepolicy/init.te
@@ -0,0 +1 @@
+allow init tmpfs:lnk_file create_file_perms;
diff --git a/target/board/generic_x86/sepolicy/init.te b/target/board/generic_x86/sepolicy/init.te
new file mode 100644
index 0000000..3aa81d1
--- /dev/null
+++ b/target/board/generic_x86/sepolicy/init.te
@@ -0,0 +1 @@
+allow init tmpfs:lnk_file create_file_perms;
diff --git a/target/product/base.mk b/target/product/base.mk
index 6a74c00..4c49e86 100644
--- a/target/product/base.mk
+++ b/target/product/base.mk
@@ -36,6 +36,7 @@
     dpm \
     framework \
     fsck_msdos \
+    hid \
     ime \
     input \
     javax.obex \
diff --git a/tools/droiddoc/templates-sdk/assets/js/docs.js b/tools/droiddoc/templates-sdk/assets/js/docs.js
index feaf06b..d88c0e6 100644
--- a/tools/droiddoc/templates-sdk/assets/js/docs.js
+++ b/tools/droiddoc/templates-sdk/assets/js/docs.js
@@ -2101,9 +2101,8 @@
 
         // Search for matching JD docs
         if (text.length >= 2) {
-          // Regex to match only the beginning of a word
-          var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
-
+          // match only the beginning of a word
+          var queryStr = text.toLowerCase();
 
           // Search for Training classes
           for (var i=0; i<TRAINING_RESOURCES.length; i++) {
@@ -2117,7 +2116,7 @@
             // Check if query matches any tags; work backwards toward 1 to assist ranking
             for (var j = s.keywords.length - 1; j >= 0; j--) {
               // it matches a tag
-              if (s.keywords[j].toLowerCase().match(textRegex)) {
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_tag = j + 1; // add 1 to index position
               }
@@ -2127,7 +2126,7 @@
             if ((s.lang == currentLang) &&
                   (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
               // it matches the doc title
-              if (s.title.toLowerCase().match(textRegex)) {
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_title = 1;
               }
@@ -2151,7 +2150,8 @@
             // Check if query matches any tags; work backwards toward 1 to assist ranking
             for (var j = s.keywords.length - 1; j >= 0; j--) {
               // it matches a tag
-              if (s.keywords[j].toLowerCase().match(textRegex)) {
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
+
                 matched = true;
                 s.matched_tag = j + 1; // add 1 to index position
               }
@@ -2159,7 +2159,7 @@
             // Check if query matches the doc title, but only for current language
             if (s.lang == currentLang) {
               // if query matches the doc title
-              if (s.title.toLowerCase().match(textRegex)) {
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_title = 1;
               }
@@ -2183,7 +2183,7 @@
             // Check if query matches any tags; work backwards toward 1 to assist ranking
             for (var j = s.keywords.length - 1; j >= 0; j--) {
               // it matches a tag
-              if (s.keywords[j].toLowerCase().match(textRegex)) {
+                if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_tag = j + 1; // add 1 to index position
               }
@@ -2191,7 +2191,7 @@
             // Check if query matches the doc title, but only for current language
             if (s.lang == currentLang) {
               // if query matches the doc title
-              if (s.title.toLowerCase().match(textRegex)) {
+                if (s.title.toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_title = 1;
               }
@@ -2215,7 +2215,7 @@
             // Check if query matches any tags; work backwards toward 1 to assist ranking
             for (var j = s.keywords.length - 1; j >= 0; j--) {
               // it matches a tag
-              if (s.keywords[j].toLowerCase().match(textRegex)) {
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_tag = j + 1; // add 1 to index position
               }
@@ -2223,7 +2223,7 @@
             // Check if query matches the doc title, but only for current language
             if (s.lang == currentLang) {
               // if query matches the doc title
-              if (s.title.toLowerCase().match(textRegex)) {
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_title = 1;
               }
@@ -2247,7 +2247,7 @@
             // Check if query matches any tags; work backwards toward 1 to assist ranking
             for (var j = s.keywords.length - 1; j >= 0; j--) {
               // it matches a tag
-              if (s.keywords[j].toLowerCase().match(textRegex)) {
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_tag = j + 1; // add 1 to index position
               }
@@ -2255,7 +2255,7 @@
             // Check if query matches the doc title, but only for current language
             if (s.lang == currentLang) {
               // if query matches the doc title
-              if (s.title.toLowerCase().match(textRegex)) {
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_title = 1;
               }
@@ -2279,7 +2279,7 @@
             // Check if query matches any tags; work backwards toward 1 to assist ranking
             for (var j = s.keywords.length - 1; j >= 0; j--) {
               // it matches a tag
-              if (s.keywords[j].toLowerCase().match(textRegex)) {
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_tag = j + 1; // add 1 to index position
               }
@@ -2287,7 +2287,7 @@
             // Check if query matches the doc title, but only for current language
             if (s.lang == currentLang) {
               // if query matches the doc title
-              if (s.title.toLowerCase().match(textRegex)) {
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_title = 1;
               }
@@ -2311,7 +2311,7 @@
             // Check if query matches any tags; work backwards toward 1 to assist ranking
             for (var j = s.keywords.length - 1; j >= 0; j--) {
               // it matches a tag
-              if (s.keywords[j].toLowerCase().match(textRegex)) {
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_tag = j + 1; // add 1 to index position
               }
@@ -2319,7 +2319,7 @@
             // Check if query matches the doc title, but only for current language
             if (s.lang == currentLang) {
               // if query matches the doc title
-              if (s.title.toLowerCase().match(textRegex)) {
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_title = 1;
               }
@@ -2342,7 +2342,7 @@
             // Check if query matches any tags; work backwards toward 1 to assist ranking
             for (var j = s.keywords.length - 1; j >= 0; j--) {
               // it matches a tag
-              if (s.keywords[j].toLowerCase().match(textRegex)) {
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_tag = j + 1; // add 1 to index position
               }
@@ -2350,7 +2350,7 @@
             // Check if query matches the doc title, but only for current language
             if (s.lang == currentLang) {
               // if query matches the doc title.t
-              if (s.title.toLowerCase().match(textRegex)) {
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_title = 1;
               }
@@ -2373,7 +2373,7 @@
             // Check if query matches any tags; work backwards toward 1 to assist ranking
             for (var j = s.keywords.length - 1; j >= 0; j--) {
               // it matches a tag
-              if (s.keywords[j].toLowerCase().match(textRegex)) {
+              if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_tag = j + 1; // add 1 to index position
               }
@@ -2381,7 +2381,7 @@
             // Check if query matches the doc title, but only for current language
             if (s.lang == currentLang) {
               // if query matches the doc title
-              if (s.title.toLowerCase().match(textRegex)) {
+              if (s.title.toLowerCase().indexOf(queryStr) == 0) {
                 matched = true;
                 s.matched_title = 1;
               }
diff --git a/tools/droiddoc/templates-sdk/docpage.cs b/tools/droiddoc/templates-sdk/docpage.cs
index 204e06c..668105b 100644
--- a/tools/droiddoc/templates-sdk/docpage.cs
+++ b/tools/droiddoc/templates-sdk/docpage.cs
@@ -194,10 +194,10 @@
 
 <?cs include:"trailer.cs" ?>
   <script src="https://developer.android.com/ytblogger_lists_unified.js" type="text/javascript"></script>
-  <script src="<?cs var:toroot ?>jd_lists_unified.js?v=14" type="text/javascript"></script>
-  <script src="<?cs var:toroot ?>jd_extras.js?v=14" type="text/javascript"></script>
-  <script src="<?cs var:toroot ?>jd_collections.js?v=14" type="text/javascript"></script>
-  <script src="<?cs var:toroot ?>jd_tag_helpers.js?v=14" type="text/javascript"></script>
+  <script src="<?cs var:toroot ?>jd_lists_unified.js?v=15" type="text/javascript"></script>
+  <script src="<?cs var:toroot ?>jd_extras.js?v=15" type="text/javascript"></script>
+  <script src="<?cs var:toroot ?>jd_collections.js?v=15" type="text/javascript"></script>
+  <script src="<?cs var:toroot ?>jd_tag_helpers.js?v=15" type="text/javascript"></script>
 
 </body>
 </html>
diff --git a/tools/droiddoc/templates-sdk/footer.cs b/tools/droiddoc/templates-sdk/footer.cs
index c960953..1ffee63 100644
--- a/tools/droiddoc/templates-sdk/footer.cs
+++ b/tools/droiddoc/templates-sdk/footer.cs
@@ -46,6 +46,7 @@
           <option value="es">Español</option>
           <option value="ja">日本語</option>
           <option value="ko">한국어</option>
+          <option value="pt-br">Português Brasileiro</option>
           <option value="ru">Русский</option>
           <option value="zh-cn">中文(简体)</option>
           <option value="zh-tw">中文(繁體)</option>
diff --git a/tools/droiddoc/templates-sdk/head_tag.cs b/tools/droiddoc/templates-sdk/head_tag.cs
index dfbff5a..babb3c7 100644
--- a/tools/droiddoc/templates-sdk/head_tag.cs
+++ b/tools/droiddoc/templates-sdk/head_tag.cs
@@ -65,7 +65,7 @@
   var metaTags = [<?cs var:meta.tags ?>];
   var devsite = <?cs if:devsite ?>true<?cs else ?>false<?cs /if ?>;
 </script>
-<script src="<?cs var:toroot ?>assets/js/docs.js?v=5" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>assets/js/docs.js?v=6" type="text/javascript"></script>
 
 <?cs if:helpoutsWidget ?>
 <script type="text/javascript" src="https://helpouts.google.com/ps/res/embed.js" defer async
diff --git a/tools/fs_config/fs_config.c b/tools/fs_config/fs_config.c
index f594c1e..b9a14e1 100644
--- a/tools/fs_config/fs_config.c
+++ b/tools/fs_config/fs_config.c
@@ -68,16 +68,17 @@
 }
 
 static void usage() {
-  fprintf(stderr, "Usage: fs_config [-S context_file] [-C]\n");
+  fprintf(stderr, "Usage: fs_config [-D product_out_path] [-S context_file] [-C]\n");
 }
 
 int main(int argc, char** argv) {
   char buffer[1024];
   const char* context_file = NULL;
+  const char* product_out_path = NULL;
   struct selabel_handle* sehnd = NULL;
   int print_capabilities = 0;
   int opt;
-  while((opt = getopt(argc, argv, "CS:")) != -1) {
+  while((opt = getopt(argc, argv, "CS:D:")) != -1) {
     switch(opt) {
     case 'C':
       print_capabilities = 1;
@@ -85,6 +86,9 @@
     case 'S':
       context_file = optarg;
       break;
+    case 'D':
+      product_out_path = optarg;
+      break;
     default:
       usage();
       exit(EXIT_FAILURE);
@@ -115,7 +119,7 @@
 
     unsigned uid = 0, gid = 0, mode = 0;
     uint64_t capabilities;
-    fs_config(buffer, is_dir, &uid, &gid, &mode, &capabilities);
+    fs_config(buffer, is_dir, product_out_path, &uid, &gid, &mode, &capabilities);
     printf("%s %d %d %o", buffer, uid, gid, mode);
 
     if (sehnd != NULL) {
diff --git a/tools/fs_get_stats/fs_get_stats.c b/tools/fs_get_stats/fs_get_stats.c
index a9814b9..159e2aa 100644
--- a/tools/fs_get_stats/fs_get_stats.c
+++ b/tools/fs_get_stats/fs_get_stats.c
@@ -25,11 +25,12 @@
 {
 	fprintf(stderr, "fs_get_stats: retrieve the target file stats "
 	        "for the specified file\n");
-	fprintf(stderr, "usage: fs_get_stats cur_perms is_dir filename\n");
+	fprintf(stderr, "usage: fs_get_stats cur_perms is_dir filename targetout\n");
 	fprintf(stderr, "\tcur_perms - The current permissions of "
 	        "the file\n");
 	fprintf(stderr, "\tis_dir    - Is filename is a dir, 1. Otherwise, 0.\n");
 	fprintf(stderr, "\tfilename  - The filename to lookup\n");
+	fprintf(stderr, "\ttargetout - The target out path to query device specific FS configs\n");
 	fprintf(stderr, "\n");
 }
 
@@ -42,7 +43,7 @@
 	unsigned uid = (unsigned)-1;
 	unsigned gid = (unsigned)-1;
 
-	if (argc < 4) {
+	if (argc < 5) {
 		ERROR("Invalid arguments\n");
 		print_help();
 		exit(-1);
@@ -58,7 +59,7 @@
 		is_dir = 1;
 
 	uint64_t capabilities;
-	fs_config(argv[3], is_dir, &uid, &gid, &perms, &capabilities);
+	fs_config(argv[3], is_dir, argv[4], &uid, &gid, &perms, &capabilities);
 	fprintf(stdout, "%d %d 0%o\n", uid, gid, perms);
 
 	return 0;
diff --git a/tools/mktarball.sh b/tools/mktarball.sh
index 3e32006..ef0fe86 100755
--- a/tools/mktarball.sh
+++ b/tools/mktarball.sh
@@ -5,8 +5,9 @@
 # $3: subdir to tar up (from $2)
 # $4: target tar name
 # $5: target tarball name (usually $(3).bz2)
+# $6: TARGET_OUT path to query device specific FS configs
 
-if [ $# -ne 5 ]; then
+if [ $# -ne 6 ]; then
     echo "Error: wrong number of arguments in cmd: $0 $* "
     exit 1
 fi
@@ -16,6 +17,7 @@
 dir_to_tar=$3
 target_tar=`readlink -f $4`
 target_tarball=`readlink -f $5`
+target_out=`readlink -f $6`
 
 cd $2
 
@@ -28,7 +30,7 @@
 for f in ${subdirs} ${files} ; do
     curr_perms=`stat -c 0%a $f`
     [ -d "$f" ] && is_dir=1 || is_dir=0
-    new_info=`${fs_get_stats} ${curr_perms} ${is_dir} ${f}`
+    new_info=`${fs_get_stats} ${curr_perms} ${is_dir} ${f} ${target_out}`
     new_uid=`echo ${new_info} | awk '{print $1;}'`
     new_gid=`echo ${new_info} | awk '{print $2;}'`
     new_perms=`echo ${new_info} | awk '{print $3;}'`
diff --git a/tools/releasetools/blockimgdiff.py b/tools/releasetools/blockimgdiff.py
index 3402572..6ed9ca2 100644
--- a/tools/releasetools/blockimgdiff.py
+++ b/tools/releasetools/blockimgdiff.py
@@ -83,6 +83,7 @@
   blocksize = 4096
   care_map = RangeSet()
   clobbered_blocks = RangeSet()
+  extended = RangeSet()
   total_blocks = 0
   file_map = {}
   def ReadRangeSet(self, ranges):
@@ -119,6 +120,7 @@
     self.total_blocks = len(self.data) / self.blocksize
     self.care_map = RangeSet(data=(0, self.total_blocks))
     self.clobbered_blocks = RangeSet()
+    self.extended = RangeSet()
 
     zero_blocks = []
     nonzero_blocks = []
@@ -411,7 +413,7 @@
           elif self.version >= 3:
             # take into account automatic stashing of overlapping blocks
             if xf.src_ranges.overlaps(xf.tgt_ranges):
-              temp_stash_usage = stashed_blocks + xf.src_ranges.size();
+              temp_stash_usage = stashed_blocks + xf.src_ranges.size()
               if temp_stash_usage > max_stashed_blocks:
                 max_stashed_blocks = temp_stash_usage
 
@@ -435,7 +437,7 @@
         elif self.version >= 3:
           # take into account automatic stashing of overlapping blocks
           if xf.src_ranges.overlaps(xf.tgt_ranges):
-            temp_stash_usage = stashed_blocks + xf.src_ranges.size();
+            temp_stash_usage = stashed_blocks + xf.src_ranges.size()
             if temp_stash_usage > max_stashed_blocks:
               max_stashed_blocks = temp_stash_usage
 
@@ -462,18 +464,17 @@
       # stash space
       assert max_stashed_blocks * self.tgt.blocksize < (512 << 20)
 
+    # Zero out extended blocks as a workaround for bug 20881595.
+    if self.tgt.extended:
+      out.append("zero %s\n" % (self.tgt.extended.to_string_raw(),))
+
+    # We erase all the blocks on the partition that a) don't contain useful
+    # data in the new image and b) will not be touched by dm-verity.
     all_tgt = RangeSet(data=(0, self.tgt.total_blocks))
-    if performs_read:
-      # if some of the original data is used, then at the end we'll
-      # erase all the blocks on the partition that don't contain data
-      # in the new image.
-      new_dontcare = all_tgt.subtract(self.tgt.care_map)
-      if new_dontcare:
-        out.append("erase %s\n" % (new_dontcare.to_string_raw(),))
-    else:
-      # if nothing is read (ie, this is a full OTA), then we can start
-      # by erasing the entire partition.
-      out.insert(0, "erase %s\n" % (all_tgt.to_string_raw(),))
+    all_tgt_minus_extended = all_tgt.subtract(self.tgt.extended)
+    new_dontcare = all_tgt_minus_extended.subtract(self.tgt.care_map)
+    if new_dontcare:
+      out.append("erase %s\n" % (new_dontcare.to_string_raw(),))
 
     out.insert(0, "%d\n" % (self.version,))   # format version number
     out.insert(1, str(total) + "\n")
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 6c526c8..4b43c0c 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -22,6 +22,7 @@
 """
 import os
 import os.path
+import re
 import subprocess
 import sys
 import commands
@@ -34,17 +35,18 @@
 FIXED_SALT = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7"
 
 def RunCommand(cmd):
-  """ Echo and run the given command
+  """Echo and run the given command.
 
   Args:
     cmd: the command represented as a list of strings.
   Returns:
-    The exit code.
+    A tuple of the output and the exit code.
   """
   print "Running: ", " ".join(cmd)
-  p = subprocess.Popen(cmd)
-  p.communicate()
-  return p.returncode
+  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+  output, _ = p.communicate()
+  print "%s" % (output.rstrip(),)
+  return (output, p.returncode)
 
 def GetVerityTreeSize(partition_size):
   cmd = "build_verity_tree -s %d"
@@ -146,7 +148,7 @@
     else:
       return True, unsparse_image_path
   inflate_command = ["simg2img", sparse_image_path, unsparse_image_path]
-  exit_code = RunCommand(inflate_command)
+  (_, exit_code) = RunCommand(inflate_command)
   if exit_code != 0:
     os.remove(unsparse_image_path)
     return False, None
@@ -202,13 +204,14 @@
   shutil.rmtree(tempdir_name, ignore_errors=True)
   return True
 
-def BuildImage(in_dir, prop_dict, out_file):
+def BuildImage(in_dir, prop_dict, out_file, target_out=None):
   """Build an image to out_file from in_dir with property prop_dict.
 
   Args:
     in_dir: path of input directory.
     prop_dict: property dictionary.
     out_file: path of the output image file.
+    target_out: path of the product out directory to read device specific FS config files.
 
   Returns:
     True iff the image is built successfully.
@@ -241,11 +244,12 @@
 
   fs_spans_partition = True
   if fs_type.startswith("squash"):
-      fs_spans_partition = False
+    fs_spans_partition = False
 
   is_verity_partition = "verity_block_device" in prop_dict
   verity_supported = prop_dict.get("verity") == "true"
-  # adjust the partition size to make room for the hashes if this is to be verified
+  # Adjust the partition size to make room for the hashes if this is to be
+  # verified.
   if verity_supported and is_verity_partition and fs_spans_partition:
     partition_size = int(prop_dict.get("partition_size"))
 
@@ -269,6 +273,8 @@
       build_command.extend(["-T", str(prop_dict["timestamp"])])
     if fs_config:
       build_command.extend(["-C", fs_config])
+    if target_out:
+      build_command.extend(["-D", target_out])
     if "block_list" in prop_dict:
       build_command.extend(["-B", prop_dict["block_list"]])
     build_command.extend(["-L", prop_dict["mount_point"]])
@@ -277,9 +283,16 @@
   elif fs_type.startswith("squash"):
     build_command = ["mksquashfsimage.sh"]
     build_command.extend([in_dir, out_file])
+    build_command.extend(["-s"])
     build_command.extend(["-m", prop_dict["mount_point"]])
+    if target_out:
+      build_command.extend(["-d", target_out])
     if "selinux_fc" in prop_dict:
       build_command.extend(["-c", prop_dict["selinux_fc"]])
+    if "squashfs_compressor" in prop_dict:
+      build_command.extend(["-z", prop_dict["squashfs_compressor"]])
+    if "squashfs_compressor_opt" in prop_dict:
+      build_command.extend(["-zo", prop_dict["squashfs_compressor_opt"]])
   elif fs_type.startswith("f2fs"):
     build_command = ["mkf2fsuserimg.sh"]
     build_command.extend([out_file, prop_dict["partition_size"]])
@@ -302,8 +315,15 @@
     staging_system = os.path.join(in_dir, "system")
     shutil.rmtree(staging_system, ignore_errors=True)
     shutil.copytree(origin_in, staging_system, symlinks=True)
+
+  reserved_blocks = prop_dict.get("has_ext4_reserved_blocks") == "true"
+  ext4fs_output = None
+
   try:
-    exit_code = RunCommand(build_command)
+    if reserved_blocks and fs_type.startswith("ext4"):
+      (ext4fs_output, exit_code) = RunCommand(build_command)
+    else:
+      (_, exit_code) = RunCommand(build_command)
   finally:
     if in_dir != origin_in:
       # Clean up temporary directories and files.
@@ -313,17 +333,42 @@
   if exit_code != 0:
     return False
 
+  # Bug: 21522719, 22023465
+  # There are some reserved blocks on ext4 FS (lesser of 4096 blocks and 2%).
+  # We need to deduct those blocks from the available space, since they are
+  # not writable even with root privilege. It only affects devices using
+  # file-based OTA and a kernel version of 3.10 or greater (currently just
+  # sprout).
+  if reserved_blocks and fs_type.startswith("ext4"):
+    assert ext4fs_output is not None
+    ext4fs_stats = re.compile(
+        r'Created filesystem with .* (?P<used_blocks>[0-9]+)/'
+        r'(?P<total_blocks>[0-9]+) blocks')
+    m = ext4fs_stats.match(ext4fs_output.strip().split('\n')[-1])
+    used_blocks = int(m.groupdict().get('used_blocks'))
+    total_blocks = int(m.groupdict().get('total_blocks'))
+    reserved_blocks = min(4096, int(total_blocks * 0.02))
+    adjusted_blocks = total_blocks - reserved_blocks
+    if used_blocks > adjusted_blocks:
+      mount_point = prop_dict.get("mount_point")
+      print("Error: Not enough room on %s (total: %d blocks, used: %d blocks, "
+            "reserved: %d blocks, available: %d blocks)" % (
+                mount_point, total_blocks, used_blocks, reserved_blocks,
+                adjusted_blocks))
+      return False
+
   if not fs_spans_partition:
     mount_point = prop_dict.get("mount_point")
     partition_size = int(prop_dict.get("partition_size"))
     image_size = os.stat(out_file).st_size
     if image_size > partition_size:
-        print "Error: %s image size of %d is larger than partition size of %d" % (mount_point, image_size, partition_size)
-        return False
+      print("Error: %s image size of %d is larger than partition size of "
+            "%d" % (mount_point, image_size, partition_size))
+      return False
     if verity_supported and is_verity_partition:
-        if 2 * image_size - AdjustPartitionSizeForVerity(image_size) > partition_size:
-            print "Error: No more room on %s to fit verity data" % mount_point
-            return False
+      if 2 * image_size - AdjustPartitionSizeForVerity(image_size) > partition_size:
+        print "Error: No more room on %s to fit verity data" % mount_point
+        return False
     prop_dict["original_partition_size"] = prop_dict["partition_size"]
     prop_dict["partition_size"] = str(image_size)
 
@@ -339,7 +384,7 @@
 
     # Run e2fsck on the inflated image file
     e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]
-    exit_code = RunCommand(e2fsck_command)
+    (_, exit_code) = RunCommand(e2fsck_command)
 
     os.remove(unsparse_image)
 
@@ -386,6 +431,9 @@
     copy_prop("system_verity_block_device", "verity_block_device")
     copy_prop("system_root_image", "system_root_image")
     copy_prop("ramdisk_dir", "ramdisk_dir")
+    copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_blocks")
+    copy_prop("system_squashfs_compressor", "squashfs_compressor")
+    copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
   elif mount_point == "data":
     # Copy the generic fs type first, override with specific one if available.
     copy_prop("fs_type", "fs_type")
@@ -399,10 +447,12 @@
     copy_prop("vendor_size", "partition_size")
     copy_prop("vendor_journal_size", "journal_size")
     copy_prop("vendor_verity_block_device", "verity_block_device")
+    copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_blocks")
   elif mount_point == "oem":
     copy_prop("fs_type", "fs_type")
     copy_prop("oem_size", "partition_size")
     copy_prop("oem_journal_size", "journal_size")
+    copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_blocks")
 
   return d
 
@@ -422,17 +472,19 @@
 
 
 def main(argv):
-  if len(argv) != 3:
+  if len(argv) != 4:
     print __doc__
     sys.exit(1)
 
   in_dir = argv[0]
   glob_dict_file = argv[1]
   out_file = argv[2]
+  target_out = argv[3]
 
   glob_dict = LoadGlobalDict(glob_dict_file)
   if "mount_point" in glob_dict:
-    # The caller knows the mount point and provides a dictionay needed by BuildImage().
+    # The caller knows the mount point and provides a dictionay needed by
+    # BuildImage().
     image_properties = glob_dict
   else:
     image_filename = os.path.basename(out_file)
@@ -453,7 +505,7 @@
 
     image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)
 
-  if not BuildImage(in_dir, image_properties, out_file):
+  if not BuildImage(in_dir, image_properties, out_file, target_out):
     print >> sys.stderr, "error: failed to build %s from %s" % (out_file,
                                                                 in_dir)
     exit(1)
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 6f921e0..99b319d 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -1253,7 +1253,23 @@
     script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
                        self.device, ranges_str,
                        self.tgt.TotalSha1(include_clobbered_blocks=True)))
-    script.Print('Verified the updated %s image.' % (partition,))
+
+    # Bug: 20881595
+    # Verify that extended blocks are really zeroed out.
+    if self.tgt.extended:
+      ranges_str = self.tgt.extended.to_string_raw()
+      script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
+                         self.device, ranges_str,
+                         self._HashZeroBlocks(self.tgt.extended.size())))
+      script.Print('Verified the updated %s image.' % (partition,))
+      script.AppendExtra(
+          'else\n'
+          '  abort("%s partition has unexpected non-zero contents after OTA '
+          'update");\n'
+          'endif;' % (partition,))
+    else:
+      script.Print('Verified the updated %s image.' % (partition,))
+
     script.AppendExtra(
         'else\n'
         '  abort("%s partition has unexpected contents after OTA update");\n'
@@ -1286,6 +1302,15 @@
 
     return ctx.hexdigest()
 
+  def _HashZeroBlocks(self, num_blocks): # pylint: disable=no-self-use
+    """Return the hash value for all zero blocks."""
+    zero_block = '\x00' * 4096
+    ctx = sha1()
+    for _ in range(num_blocks):
+      ctx.update(zero_block)
+
+    return ctx.hexdigest()
+
   # TODO(tbao): Due to http://b/20939131, block 0 may be changed without
   # remounting R/W. Will change the checking to a finer-grained way to
   # mask off those bits.
diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py
index 281ed59..566e687 100644
--- a/tools/releasetools/edify_generator.py
+++ b/tools/releasetools/edify_generator.py
@@ -20,11 +20,15 @@
   """Class to generate scripts in the 'edify' recovery script language
   used from donut onwards."""
 
-  def __init__(self, version, info):
+  def __init__(self, version, info, fstab=None):
     self.script = []
     self.mounts = set()
     self.version = version
     self.info = info
+    if fstab is None:
+      self.fstab = self.info.get("fstab", None)
+    else:
+      self.fstab = fstab
 
   def MakeTemporary(self):
     """Make a temporary script object whose commands can latter be
@@ -168,7 +172,7 @@
       where option is optname[=optvalue]
       E.g. ext4=barrier=1,nodelalloc,errors=panic|f2fs=errors=recover
     """
-    fstab = self.info.get("fstab", None)
+    fstab = self.fstab
     if fstab:
       p = fstab[mount_point]
       mount_dict = {}
@@ -202,7 +206,7 @@
     self.script.append('ui_print("%s");' % (message,))
 
   def TunePartition(self, partition, *options):
-    fstab = self.info.get("fstab", None)
+    fstab = self.fstab
     if fstab:
       p = fstab[partition]
       if p.fs_type not in ("ext2", "ext3", "ext4"):
@@ -216,7 +220,7 @@
     """Format the given partition, specified by its mount point (eg,
     "/system")."""
 
-    fstab = self.info.get("fstab", None)
+    fstab = self.fstab
     if fstab:
       p = fstab[partition]
       self.script.append('format("%s", "%s", "%s", "%s", "%s");' %
@@ -226,7 +230,7 @@
   def WipeBlockDevice(self, partition):
     if partition not in ("/system", "/vendor"):
       raise ValueError(("WipeBlockDevice doesn't work on %s\n") % (partition,))
-    fstab = self.info.get("fstab", None)
+    fstab = self.fstab
     size = self.info.get(partition.lstrip("/") + "_size", None)
     device = fstab[partition].device
 
@@ -271,7 +275,7 @@
     """Write the given package file into the partition for the given
     mount point."""
 
-    fstab = self.info["fstab"]
+    fstab = self.fstab
     if fstab:
       p = fstab[mount_point]
       partition_type = common.PARTITION_TYPES[p.fs_type]
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index c4d0c1b..82d6313 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -486,8 +486,9 @@
 
 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.
+  # be installed on top of. For now, we expect the API just won't
+  # change very often. Similarly for fstab, it might have changed
+  # in the target build.
   script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
 
   oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
@@ -727,8 +728,9 @@
   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)
+  script = edify_generator.EdifyGenerator(
+      source_version, OPTIONS.target_info_dict,
+      fstab=OPTIONS.source_info_dict["fstab"])
 
   metadata = {
       "pre-device": GetBuildProp("ro.product.device",
@@ -794,7 +796,7 @@
     vendor_diff = None
 
   oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
-  recovery_mount_options = OPTIONS.target_info_dict.get(
+  recovery_mount_options = OPTIONS.source_info_dict.get(
       "recovery_mount_options")
   oem_dict = None
   if oem_props is not None and len(oem_props) > 0:
@@ -1114,11 +1116,13 @@
   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)
+  script = edify_generator.EdifyGenerator(
+      source_version, OPTIONS.target_info_dict,
+      fstab=OPTIONS.source_info_dict["fstab"])
 
   oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
-  recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
+  recovery_mount_options = OPTIONS.source_info_dict.get(
+      "recovery_mount_options")
   oem_dict = None
   if oem_props is not None and len(oem_props) > 0:
     if OPTIONS.oem_source is None:
diff --git a/tools/releasetools/rangelib.py b/tools/releasetools/rangelib.py
index 8b327fe..1506658 100644
--- a/tools/releasetools/rangelib.py
+++ b/tools/releasetools/rangelib.py
@@ -238,6 +238,28 @@
         out.append(offset + p - start)
     return RangeSet(data=out)
 
+  def extend(self, n):
+    """Extend the RangeSet by 'n' blocks.
+
+    The lower bound is guaranteed to be non-negative.
+
+    >>> RangeSet("0-9").extend(1)
+    <RangeSet("0-10")>
+    >>> RangeSet("10-19").extend(15)
+    <RangeSet("0-34")>
+    >>> RangeSet("10-19 30-39").extend(4)
+    <RangeSet("6-23 26-43")>
+    >>> RangeSet("10-19 30-39").extend(10)
+    <RangeSet("0-49")>
+    """
+    out = self
+    for i in range(0, len(self.data), 2):
+      s, e = self.data[i:i+2]
+      s1 = max(0, s - n)
+      e1 = e + n
+      out = out.union(RangeSet(str(s1) + "-" + str(e1-1)))
+    return out
+
 
 if __name__ == "__main__":
   import doctest
diff --git a/tools/releasetools/sparse_img.py b/tools/releasetools/sparse_img.py
index 51a1643..07f3c1c 100644
--- a/tools/releasetools/sparse_img.py
+++ b/tools/releasetools/sparse_img.py
@@ -110,6 +110,17 @@
     self.care_map = rangelib.RangeSet(care_data)
     self.offset_index = [i[0] for i in offset_map]
 
+    # Bug: 20881595
+    # Introduce extended blocks as a workaround for the bug. dm-verity may
+    # touch blocks that are not in the care_map due to block device
+    # read-ahead. It will fail if such blocks contain non-zeroes. We zero out
+    # the extended blocks explicitly to avoid dm-verity failures. 512 blocks
+    # are the maximum read-ahead we configure for dm-verity block devices.
+    extended = self.care_map.extend(512)
+    all_blocks = rangelib.RangeSet(data=(0, self.total_blocks))
+    extended = extended.intersect(all_blocks).subtract(self.care_map)
+    self.extended = extended
+
     if file_map_fn:
       self.LoadFileBlockMap(file_map_fn, self.clobbered_blocks)
     else:
@@ -125,7 +136,7 @@
     clobbered_blocks."""
     ranges = self.care_map
     if not include_clobbered_blocks:
-      ranges.subtract(self.clobbered_blocks)
+      ranges = ranges.subtract(self.clobbered_blocks)
     h = sha1()
     for d in self._GetRangeData(ranges):
       h.update(d)
@@ -221,9 +232,14 @@
           nonzero_blocks.append(b)
           nonzero_blocks.append(b+1)
 
-    out["__ZERO"] = rangelib.RangeSet(data=zero_blocks)
-    out["__NONZERO"] = rangelib.RangeSet(data=nonzero_blocks)
-    out["__COPY"] = clobbered_blocks
+    assert zero_blocks or nonzero_blocks or clobbered_blocks
+
+    if zero_blocks:
+      out["__ZERO"] = rangelib.RangeSet(data=zero_blocks)
+    if nonzero_blocks:
+      out["__NONZERO"] = rangelib.RangeSet(data=nonzero_blocks)
+    if clobbered_blocks:
+      out["__COPY"] = clobbered_blocks
 
   def ResetFileMap(self):
     """Throw away the file map and treat the entire image as