Merge "Mark BUILD_NUMBER as obsolete, more vars as readonly"
diff --git a/core/Makefile b/core/Makefile
index fe728d6..9dbf6e4 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1206,7 +1206,7 @@
 
 ifeq ($(INTERNAL_USERIMAGES_USE_EXT),true)
 INTERNAL_USERIMAGES_DEPS := $(SIMG2IMG)
-INTERNAL_USERIMAGES_DEPS += $(MKEXTUSERIMG) $(MAKE_EXT4FS) $(E2FSCK)
+INTERNAL_USERIMAGES_DEPS += $(MKEXTUSERIMG) $(MAKE_EXT4FS) $(E2FSCK) $(TUNE2FS)
 ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
 INTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG) $(MAKE_F2FS)
 endif
@@ -1638,15 +1638,13 @@
 # Generate a file containing the keys that will be read by the
 # recovery binary.
 RECOVERY_INSTALL_OTA_KEYS := \
-	$(call intermediates-dir-for,PACKAGING,ota_keys)/keys
-DUMPKEY_JAR := $(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar
+	$(call intermediates-dir-for,PACKAGING,ota_keys)/otacerts.zip
 $(RECOVERY_INSTALL_OTA_KEYS): PRIVATE_OTA_PUBLIC_KEYS := $(OTA_PUBLIC_KEYS)
 $(RECOVERY_INSTALL_OTA_KEYS): extra_keys := $(patsubst %,%.x509.pem,$(PRODUCT_EXTRA_RECOVERY_KEYS))
-$(RECOVERY_INSTALL_OTA_KEYS): $(OTA_PUBLIC_KEYS) $(DUMPKEY_JAR) $(extra_keys)
-	@echo "DumpPublicKey: $@ <= $(PRIVATE_OTA_PUBLIC_KEYS) $(extra_keys)"
-	@rm -rf $@
-	@mkdir -p $(dir $@)
-	$(JAVA) -jar $(DUMPKEY_JAR) $(PRIVATE_OTA_PUBLIC_KEYS) $(extra_keys) > $@
+$(RECOVERY_INSTALL_OTA_KEYS): $(SOONG_ZIP) $(OTA_PUBLIC_KEYS) $(extra_keys)
+	$(hide) rm -f $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(SOONG_ZIP) -o $@ $(foreach key_file, $(PRIVATE_OTA_PUBLIC_KEYS) $(extra_keys), -C $(dir $(key_file)) -f $(key_file))
 
 RECOVERYIMAGE_ID_FILE := $(PRODUCT_OUT)/recovery.id
 
@@ -1677,7 +1675,8 @@
     cp -f $(item) $(TARGET_RECOVERY_ROOT_OUT)/system/etc/recovery.fstab)
   $(if $(strip $(recovery_wipe)), \
     $(hide) cp -f $(recovery_wipe) $(TARGET_RECOVERY_ROOT_OUT)/system/etc/recovery.wipe)
-  $(hide) cp $(RECOVERY_INSTALL_OTA_KEYS) $(TARGET_RECOVERY_ROOT_OUT)/res/keys
+  $(hide) mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/system/etc/security
+  $(hide) cp $(RECOVERY_INSTALL_OTA_KEYS) $(TARGET_RECOVERY_ROOT_OUT)/system/etc/security/otacerts.zip
   $(hide) ln -sf prop.default $(TARGET_RECOVERY_ROOT_OUT)/default.prop
   $(BOARD_RECOVERY_IMAGE_PREPARE)
   $(hide) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RECOVERY_ROOT_OUT) | $(MINIGZIP) > $(recovery_ramdisk)
@@ -2779,9 +2778,8 @@
 BOARD_AVB_MAKE_VBMETA_VENDOR_IMAGE_ARGS += --padding_size 4096
 
 ifeq (eng,$(filter eng, $(TARGET_BUILD_VARIANT)))
+# We only need the flag in top-level vbmeta.img.
 BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --set_hashtree_disabled_flag
-BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS += --set_hashtree_disabled_flag
-BOARD_AVB_MAKE_VBMETA_VENDOR_IMAGE_ARGS += --set_hashtree_disabled_flag
 endif
 
 ifdef BOARD_AVB_ROLLBACK_INDEX
@@ -3095,13 +3093,13 @@
   $(HOST_OUT_EXECUTABLES)/zipalign \
   $(HOST_OUT_EXECUTABLES)/bsdiff \
   $(HOST_OUT_EXECUTABLES)/imgdiff \
-  $(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar \
   $(HOST_OUT_JAVA_LIBRARIES)/signapk.jar \
   $(HOST_OUT_JAVA_LIBRARIES)/BootSignature.jar \
   $(HOST_OUT_JAVA_LIBRARIES)/VeritySigner.jar \
   $(HOST_OUT_EXECUTABLES)/mke2fs \
   $(HOST_OUT_EXECUTABLES)/mkuserimg_mke2fs \
   $(HOST_OUT_EXECUTABLES)/e2fsdroid \
+  $(HOST_OUT_EXECUTABLES)/tune2fs \
   $(HOST_OUT_EXECUTABLES)/mksquashfsimage.sh \
   $(HOST_OUT_EXECUTABLES)/mksquashfs \
   $(HOST_OUT_EXECUTABLES)/mkf2fsuserimg.sh \
diff --git a/core/android_manifest.mk b/core/android_manifest.mk
index 8608ca1..c3af942 100644
--- a/core/android_manifest.mk
+++ b/core/android_manifest.mk
@@ -71,6 +71,11 @@
 ifeq ($(LOCAL_PRIVATE_PLATFORM_APIS),true)
     my_manifest_fixer_flags += --uses-non-sdk-api
 endif
+
+ifeq (true,$(LOCAL_PREFER_INTEGRITY))
+    my_manifest_fixer_flags += --prefer-integrity
+endif
+
 $(fixed_android_manifest): PRIVATE_MANIFEST_FIXER_FLAGS := $(my_manifest_fixer_flags)
 # These two libs are added as optional dependencies (<uses-library> with
 # android:required set to false). This is because they haven't existed in pre-P
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 07e34e1..7f8e9c3 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -213,6 +213,7 @@
 LOCAL_PREBUILT_OBJ_FILES:=
 LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES:=
 LOCAL_PREBUILT_STRIP_COMMENTS:=
+LOCAL_PREFER_INTEGRITY:=
 LOCAL_PRESUBMIT_DISABLED:=
 LOCAL_PRIVATE_PLATFORM_APIS:=
 LOCAL_PRIVILEGED_MODULE:=
@@ -274,6 +275,7 @@
 LOCAL_STATIC_JAVA_AAR_LIBRARIES:=
 LOCAL_STATIC_JAVA_LIBRARIES:=
 LOCAL_STATIC_LIBRARIES:=
+LOCAL_STRIP_DEX:=
 LOCAL_STRIP_MODULE:=
 LOCAL_SYSTEM_SHARED_LIBRARIES:=none
 LOCAL_TARGET_REQUIRED_MODULES:=
@@ -284,6 +286,7 @@
 LOCAL_TIDY:=
 LOCAL_TIDY_CHECKS:=
 LOCAL_TIDY_FLAGS:=
+LOCAL_UNCOMPRESS_DEX:=
 LOCAL_UNINSTALLABLE_MODULE:=
 LOCAL_UNSTRIPPED_PATH:=
 LOCAL_USE_AAPT2:=$(USE_AAPT2)
diff --git a/core/definitions.mk b/core/definitions.mk
index baa2342..362680e 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2640,12 +2640,12 @@
 # Copy an apk to a target location while removing classes*.dex
 # $(1): source file
 # $(2): destination file
-# $(3): LOCAL_DEX_PREOPT, if nostripping then leave classes*.dex
+# $(3): LOCAL_STRIP_DEX, if non-empty then strip classes*.dex
 define dexpreopt-copy-jar
 $(2): $(1)
 	@echo "Copy: $$@"
 	$$(copy-file-to-target)
-	$(if $(filter nostripping,$(3)),,$$(call dexpreopt-remove-classes.dex,$$@))
+	$(if $(3),$$(call dexpreopt-remove-classes.dex,$$@))
 endef
 
 # $(1): the .jar or .apk to remove classes.dex. Note that if all dex files
@@ -2672,9 +2672,10 @@
       $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
 	@rm -rf $(dir $(2))
 	@mkdir -p $(dir $(2))
-	find $(dir $(1)) -maxdepth 1 -name "classes*.dex" | xargs -I{} cp -f {} $(dir $(2))/; \
-	find $(dir $(2)) -maxdepth 1 -name "classes*.dex" | sort | sed 's/^/--dex=/' \
-	| xargs $(HIDDENAPI) encode \
+	for INPUT_DEX in `find $(dir $(1)) -maxdepth 1 -name "classes*.dex" | sort`; do \
+	    echo "--input-dex=$$$${INPUT_DEX}"; \
+	    echo "--output-dex=$(dir $(2))/`basename $$$${INPUT_DEX}`"; \
+	done | xargs $(HIDDENAPI) encode \
 	    --light-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
 	    --dark-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
 	    --blacklist=$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
diff --git a/core/dex_preopt.mk b/core/dex_preopt.mk
index 1527047..92ed970 100644
--- a/core/dex_preopt.mk
+++ b/core/dex_preopt.mk
@@ -73,7 +73,7 @@
 _dbj_jar_no_dex := $(DEXPREOPT_BOOT_JAR_DIR_FULL_PATH)/$(1)_nodex.jar
 _dbj_src_jar := $(call intermediates-dir-for,JAVA_LIBRARIES,$(1),,COMMON)/javalib.jar
 
-$(call dexpreopt-copy-jar,$$(_dbj_src_jar),$$(_dbj_jar_no_dex),$(DEX_PREOPT_DEFAULT))
+$(call dexpreopt-copy-jar,$$(_dbj_src_jar),$$(_dbj_jar_no_dex),$(filter-out nostripping,$(DEX_PREOPT_DEFAULT)))
 
 _dbj_jar_no_dex :=
 _dbj_src_jar :=
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index cd78eda..6a892e2 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -3,9 +3,14 @@
 # Output variables: LOCAL_DEX_PREOPT, LOCAL_UNCOMPRESS_DEX, built_odex,
 #                   dexpreopt_boot_jar_module
 
+ifeq (true,$(LOCAL_PREFER_INTEGRITY))
+  LOCAL_UNCOMPRESS_DEX := true
+else
+  LOCAL_UNCOMPRESS_DEX :=
+endif
+
 # We explicitly uncompress APKs of privileged apps, and used by
 # privileged apps
-LOCAL_UNCOMPRESS_DEX := false
 ifneq (true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS))
   ifeq (true,$(LOCAL_PRIVILEGED_MODULE))
     LOCAL_UNCOMPRESS_DEX := true
@@ -33,6 +38,13 @@
   endif
 endif
 
+ifeq (nostripping,$(LOCAL_DEX_PREOPT))
+  LOCAL_DEX_PREOPT := true
+  LOCAL_STRIP_DEX :=
+else
+  LOCAL_STRIP_DEX := true
+endif
+
 ifeq (false,$(LOCAL_DEX_PREOPT))
   LOCAL_DEX_PREOPT :=
 endif
@@ -43,7 +55,7 @@
 endif
 
 # If we have product-specific config for this module?
-ifeq (disable,$(DEXPREOPT.$(TARGET_PRODUCT).$(LOCAL_MODULE).CONFIG))
+ifneq (,$(filter $(LOCAL_MODULE),$(DEXPREOPT_DISABLED_MODULES)))
   LOCAL_DEX_PREOPT :=
 endif
 
@@ -81,14 +93,14 @@
 ifeq ($(LOCAL_DEX_PREOPT),true)
   # Don't strip with dexes we explicitly uncompress (dexopt will not store the dex code).
   ifeq ($(LOCAL_UNCOMPRESS_DEX),true)
-    LOCAL_DEX_PREOPT := nostripping
+    LOCAL_STRIP_DEX :=
   endif  # LOCAL_UNCOMPRESS_DEX
 
   # system_other isn't there for an OTA, so don't strip
   # if module is on system, and odex is on system_other.
   ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true)
     ifneq ($(call install-on-system-other, $(my_module_path)),)
-      LOCAL_DEX_PREOPT := nostripping
+      LOCAL_STRIP_DEX :=
     endif  # install-on-system-other
   endif  # BOARD_USES_SYSTEM_OTHER_ODEX
 
@@ -320,7 +332,8 @@
 
     ifeq (true,$(my_generate_dm))
       LOCAL_DEX_PREOPT_FLAGS += --copy-dex-files=false
-      LOCAL_DEX_PREOPT := nostripping
+      LOCAL_DEX_PREOPT := true
+      LOCAL_STRIP_DEX :=
       my_built_dm := $(dir $(LOCAL_BUILT_MODULE))generated.dm
       my_installed_dm := $(patsubst %.apk,%,$(LOCAL_INSTALLED_MODULE)).dm
       my_copied_vdex := $(dir $(LOCAL_BUILT_MODULE))primary.vdex
diff --git a/core/java_library.mk b/core/java_library.mk
index 5716f4c..e4e51d8 100644
--- a/core/java_library.mk
+++ b/core/java_library.mk
@@ -96,7 +96,7 @@
 	@echo "Dexpreopt Jar: $(PRIVATE_MODULE) ($@)"
 	$(call dexpreopt-one-file,$<,$@)
 
-$(eval $(call dexpreopt-copy-jar,$(common_javalib.jar),$(LOCAL_BUILT_MODULE),$(LOCAL_DEX_PREOPT)))
+$(eval $(call dexpreopt-copy-jar,$(common_javalib.jar),$(LOCAL_BUILT_MODULE),$(LOCAL_STRIP_DEX)))
 
 endif # ! boot jar
 
diff --git a/core/package_internal.mk b/core/package_internal.mk
index 84d1c2c..9a52af7 100644
--- a/core/package_internal.mk
+++ b/core/package_internal.mk
@@ -665,7 +665,7 @@
 	@# Keep a copy of apk with classes.dex unstripped
 	$(hide) cp -f $@ $(dir $@)package.dex.apk
 endif  # BUILD_PLATFORM_ZIP
-ifneq (nostripping,$(LOCAL_DEX_PREOPT))
+ifdef LOCAL_STRIP_DEX
 	$(call dexpreopt-remove-classes.dex,$@)
 endif
 endif  # LOCAL_DEX_PREOPT
diff --git a/core/prebuilt_internal.mk b/core/prebuilt_internal.mk
index 809c572..a4b58fc 100644
--- a/core/prebuilt_internal.mk
+++ b/core/prebuilt_internal.mk
@@ -380,9 +380,9 @@
 	$(run-appcompat)
 endif  # module_run_appcompat
 ifdef LOCAL_DEX_PREOPT
-ifneq (nostripping,$(LOCAL_DEX_PREOPT))
+ifdef LOCAL_STRIP_DEX
 	$(call dexpreopt-remove-classes.dex,$@)
-endif  # LOCAL_DEX_PREOPT != nostripping
+endif  # LOCAL_STRIP_DEX
 endif  # LOCAL_DEX_PREOPT
 	$(sign-package)
 	# No need for align-package because sign-package takes care of alignment
@@ -400,7 +400,7 @@
 # can do optimizations based on whether the built module only
 # contains uncompressed dex code.
 ifdef LOCAL_DEX_PREOPT
-ifeq (nostripping,$(LOCAL_DEX_PREOPT))
+ifndef LOCAL_STRIP_DEX
 $(built_odex) : $(built_module)
 	$(call dexpreopt-one-file,$<,$@)
 else
@@ -470,7 +470,7 @@
 	@echo "Dexpreopt Jar: $(PRIVATE_MODULE) ($@)"
 	$(call dexpreopt-one-file,$<,$@)
 
-$(eval $(call dexpreopt-copy-jar,$(my_prebuilt_src_file),$(built_module),$(LOCAL_DEX_PREOPT)))
+$(eval $(call dexpreopt-copy-jar,$(my_prebuilt_src_file),$(built_module),$(LOCAL_STRIP_DEX)))
 endif # boot jar
 else # ! LOCAL_DEX_PREOPT
 $(built_module) : $(my_prebuilt_src_file)
diff --git a/core/product_config.mk b/core/product_config.mk
index 27af09e..577bafe 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -423,6 +423,7 @@
 # Resolve and setup per-module dex-preopt configs.
 PRODUCT_DEX_PREOPT_MODULE_CONFIGS := \
     $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEX_PREOPT_MODULE_CONFIGS))
+DEXPREOPT_DISABLED_MODULES :=
 # If a module has multiple setups, the first takes precedence.
 _pdpmc_modules :=
 $(foreach c,$(PRODUCT_DEX_PREOPT_MODULE_CONFIGS),\
@@ -431,7 +432,9 @@
     $(eval _pdpmc_modules += $(m))\
     $(eval cf := $(patsubst $(m)=%,%,$(c)))\
     $(eval cf := $(subst $(_PDPMC_SP_PLACE_HOLDER),$(space),$(cf)))\
-    $(eval DEXPREOPT.$(TARGET_PRODUCT).$(m).CONFIG := $(cf))))
+    $(if $(filter disable,$(cf)),\
+      $(eval DEXPREOPT_DISABLED_MODULES += $(m)),\
+      $(eval DEXPREOPT.$(TARGET_PRODUCT).$(m).CONFIG := $(cf)))))
 _pdpmc_modules :=
 
 # Resolve and setup per-module sanitizer configs.
diff --git a/core/soong_app_prebuilt.mk b/core/soong_app_prebuilt.mk
index d34f367..31cb28e 100644
--- a/core/soong_app_prebuilt.mk
+++ b/core/soong_app_prebuilt.mk
@@ -69,7 +69,7 @@
 ifdef LOCAL_DEX_PREOPT
   $(built_odex): $(LOCAL_SOONG_DEX_JAR)
 	$(call dexpreopt-one-file,$<,$@)
-  $(eval $(call dexpreopt-copy-jar,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE),$(LOCAL_DEX_PREOPT)))
+  $(eval $(call dexpreopt-copy-jar,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE),$(LOCAL_STRIP_DEX)))
 else
   $(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE)))
 endif
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 2f978fa..7a884e0 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -123,6 +123,11 @@
 $(call add_json_list, Platform_systemsdk_versions,       $(PLATFORM_SYSTEMSDK_VERSIONS))
 $(call add_json_bool, Malloc_not_svelte,                 $(call invert_bool,$(filter true,$(MALLOC_SVELTE))))
 $(call add_json_str,  Override_rs_driver,                $(OVERRIDE_RS_DRIVER))
+$(call add_json_bool, UncompressPrivAppDex,              $(call invert_bool,$(filter true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS))))
+$(call add_json_list, ModulesLoadedByPrivilegedModules,  $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES))
+$(call add_json_bool, DefaultStripDex,                   $(call invert_bool,$(filter nostripping,$(DEX_PREOPT_DEFAULT))))
+$(call add_json_bool, DisableDexPreopt,                  $(filter false,$(WITH_DEXPREOPT)))
+$(call add_json_list, DisableDexPreoptModules,           $(DEXPREOPT_DISABLED_MODULES))
 
 $(call add_json_bool, Product_is_iot,                    $(filter true,$(PRODUCT_IOT)))
 
diff --git a/core/soong_java_prebuilt.mk b/core/soong_java_prebuilt.mk
index 20bfc66..288a763 100644
--- a/core/soong_java_prebuilt.mk
+++ b/core/soong_java_prebuilt.mk
@@ -108,7 +108,7 @@
 	@echo "Dexpreopt Jar: $(PRIVATE_MODULE) ($@)"
 	$(call dexpreopt-one-file,$<,$@)
 
-         $(eval $(call dexpreopt-copy-jar,$(common_javalib.jar),$(LOCAL_BUILT_MODULE),$(LOCAL_DEX_PREOPT)))
+         $(eval $(call dexpreopt-copy-jar,$(common_javalib.jar),$(LOCAL_BUILT_MODULE),$(LOCAL_STRIP_DEX)))
         endif # ! boot jar
       else # LOCAL_DEX_PREOPT
         $(eval $(call copy-one-file,$(common_javalib.jar),$(LOCAL_BUILT_MODULE)))
diff --git a/core/tasks/tools/compatibility.mk b/core/tasks/tools/compatibility.mk
index d2433ea..57a5cf9 100644
--- a/core/tasks/tools/compatibility.mk
+++ b/core/tasks/tools/compatibility.mk
@@ -35,7 +35,6 @@
   $(HOST_OUT_JAVA_LIBRARIES)/compatibility-host-util-tests.jar \
   $(HOST_OUT_JAVA_LIBRARIES)/compatibility-common-util-tests.jar \
   $(HOST_OUT_JAVA_LIBRARIES)/compatibility-tradefed-tests.jar \
-  $(HOST_OUT_JAVA_LIBRARIES)/host-libprotobuf-java-full.jar \
   $(HOST_OUT_JAVA_LIBRARIES)/$(test_suite_tradefed).jar \
   $(HOST_OUT_JAVA_LIBRARIES)/$(test_suite_tradefed)-tests.jar \
   $(HOST_OUT_EXECUTABLES)/$(test_suite_tradefed) \
diff --git a/target/product/vndk/current.txt b/target/product/vndk/current.txt
index 7d8409b..6120e9d 100644
--- a/target/product/vndk/current.txt
+++ b/target/product/vndk/current.txt
@@ -245,8 +245,6 @@
 VNDK-core: libtinyxml2.so
 VNDK-core: libui.so
 VNDK-core: libusbhost.so
-VNDK-core: libvixl-arm.so
-VNDK-core: libvixl-arm64.so
 VNDK-core: libvorbisidec.so
 VNDK-core: libwifi-system-iface.so
 VNDK-core: libxml2.so
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 1e8677c..ddc50be 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -405,6 +405,9 @@
     needed_partitions: Partitions whose descriptors should be included into the
         generated VBMeta image.
 
+  Returns:
+    Path to the created image.
+
   Raises:
     AssertionError: On invalid input args.
   """
@@ -423,7 +426,8 @@
   for partition, path in partitions.items():
     if partition not in needed_partitions:
       continue
-    assert partition in common.AVB_PARTITIONS, \
+    assert (partition in common.AVB_PARTITIONS or
+            partition.startswith('vbmeta_')), \
         'Unknown partition: {}'.format(partition)
     assert os.path.exists(path), \
         'Failed to find {} for {}'.format(path, partition)
@@ -458,6 +462,7 @@
   assert proc.returncode == 0, \
       "avbtool make_vbmeta_image failed:\n{}".format(stdoutdata)
   img.Write()
+  return img.name
 
 
 def AddPartitionTable(output_zip):
@@ -836,7 +841,7 @@
     vbmeta_system = OPTIONS.info_dict.get("avb_vbmeta_system", "").strip()
     if vbmeta_system:
       banner("vbmeta_system")
-      AddVBMeta(
+      partitions["vbmeta_system"] = AddVBMeta(
           output_zip, partitions, "vbmeta_system", vbmeta_system.split())
       vbmeta_partitions = [
           item for item in vbmeta_partitions
@@ -846,7 +851,7 @@
     vbmeta_vendor = OPTIONS.info_dict.get("avb_vbmeta_vendor", "").strip()
     if vbmeta_vendor:
       banner("vbmeta_vendor")
-      AddVBMeta(
+      partitions["vbmeta_vendor"] = AddVBMeta(
           output_zip, partitions, "vbmeta_vendor", vbmeta_vendor.split())
       vbmeta_partitions = [
           item for item in vbmeta_partitions
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 4a013c2..b88171f 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -54,23 +54,53 @@
   """Returns the number of bytes that "path" occupies on host.
 
   Args:
-    path: The directory or file to calculate size on
+    path: The directory or file to calculate size on.
 
   Returns:
-    The number of bytes.
-
-  Raises:
-    BuildImageError: On error.
+    The number of bytes based on a 1K block_size.
   """
-  env_copy = os.environ.copy()
-  env_copy["POSIXLY_CORRECT"] = "1"
-  cmd = ["du", "-s", path]
+  cmd = ["du", "-k", "-s", path]
+  output = common.RunAndCheckOutput(cmd, verbose=False)
+  return int(output.split()[0]) * 1024
+
+
+def GetInodeUsage(path):
+  """Returns the number of inodes that "path" occupies on host.
+
+  Args:
+    path: The directory or file to calculate inode number on.
+
+  Returns:
+    The number of inodes used.
+  """
+  cmd = ["find", path, "-print"]
+  output = common.RunAndCheckOutput(cmd, verbose=False)
+  # increase by > 4% as number of files and directories is not whole picture.
+  return output.count('\n') * 25 // 24
+
+
+def GetFilesystemCharacteristics(sparse_image_path):
+  """Returns various filesystem characteristics of "sparse_image_path".
+
+  Args:
+    sparse_image_path: The file to analyze.
+
+  Returns:
+    The characteristics dictionary.
+  """
+  unsparse_image_path = UnsparseImage(sparse_image_path, replace=False)
+
+  cmd = ["tune2fs", "-l", unsparse_image_path]
   try:
-    output = common.RunAndCheckOutput(cmd, verbose=False, env=env_copy)
-  except common.ExternalError:
-    raise BuildImageError("Failed to get disk usage:\n{}".format(output))
-  # POSIX du returns number of blocks with block size 512
-  return int(output.split()[0]) * 512
+    output = common.RunAndCheckOutput(cmd, verbose=False)
+  finally:
+    os.remove(unsparse_image_path)
+  fs_dict = {}
+  for line in output.splitlines():
+    fields = line.split(":")
+    if len(fields) == 2:
+      fs_dict[fields[0].strip()] = fields[1].strip()
+  return fs_dict
 
 
 def UnsparseImage(sparse_image_path, replace=True):
@@ -121,6 +151,10 @@
   if prop_dict["mount_point"] != "system":
     return origin_in, fs_config
 
+  if "first_pass" in prop_dict:
+    prop_dict["mount_point"] = "/"
+    return prop_dict["first_pass"]
+
   # Construct a staging directory of the root file system.
   in_dir = common.MakeTempDir()
   root_dir = prop_dict.get("root_dir")
@@ -144,6 +178,7 @@
       with open(fs_config) as fr:
         fw.writelines(fr.readlines())
     fs_config = merged_fs_config
+  prop_dict["first_pass"] = (in_dir, fs_config)
   return in_dir, fs_config
 
 
@@ -175,7 +210,7 @@
   m = ext4fs_stats.match(last_line)
   used_blocks = int(m.groupdict().get('used_blocks'))
   total_blocks = int(m.groupdict().get('total_blocks'))
-  headroom_blocks = int(prop_dict['partition_headroom']) / BLOCK_SIZE
+  headroom_blocks = int(prop_dict['partition_headroom']) // BLOCK_SIZE
   adjusted_blocks = total_blocks - headroom_blocks
   if used_blocks > adjusted_blocks:
     mount_point = prop_dict["mount_point"]
@@ -202,6 +237,7 @@
   Raises:
     BuildImageError: On build image failures.
   """
+  original_mount_point = prop_dict["mount_point"]
   in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)
 
   build_command = []
@@ -233,7 +269,8 @@
     size = GetDiskUsage(in_dir)
     logger.info(
         "The tree size of %s is %d MB.", in_dir, size // BYTES_IN_MB)
-    size += int(prop_dict.get("partition_reserved_size", 0))
+    # If not specified, give us 16MB margin for GetDiskUsage error ...
+    size += int(prop_dict.get("partition_reserved_size", BYTES_IN_MB * 16))
     # Round this up to a multiple of 4K so that avbtool works
     size = common.RoundUpTo4K(size)
     # Adjust partition_size to add more space for AVB footer, to prevent
@@ -244,6 +281,35 @@
           lambda x: verity_utils.AVBCalcMaxImageSize(
               avbtool, avb_footer_type, x, avb_signing_args))
     prop_dict["partition_size"] = str(size)
+    if fs_type.startswith("ext"):
+      if "extfs_inode_count" not in prop_dict:
+        prop_dict["extfs_inode_count"] = str(GetInodeUsage(in_dir))
+      logger.info(
+          "First Pass based on estimates of %d MB and %s inodes.",
+          size // BYTES_IN_MB, prop_dict["extfs_inode_count"])
+      prop_dict["mount_point"] = original_mount_point
+      BuildImage(in_dir, prop_dict, out_file, target_out)
+      fs_dict = GetFilesystemCharacteristics(out_file)
+      block_size = int(fs_dict.get("Block size", "4096"))
+      free_size = int(fs_dict.get("Free blocks", "0")) * block_size
+      reserved_size = int(prop_dict.get("partition_reserved_size", 0))
+      if free_size <= reserved_size:
+        logger.info(
+            "Not worth reducing image %d <= %d.", free_size, reserved_size)
+      else:
+        size -= free_size
+        size += reserved_size
+        if block_size <= 4096:
+          size = common.RoundUpTo4K(size)
+        else:
+          size = ((size + block_size - 1) // block_size) * block_size
+      extfs_inode_count = prop_dict["extfs_inode_count"]
+      inodes = int(fs_dict.get("Inode count", extfs_inode_count))
+      inodes -= int(fs_dict.get("Free inodes", "0"))
+      prop_dict["extfs_inode_count"] = str(inodes)
+      prop_dict["partition_size"] = str(size)
+      logger.info(
+          "Allocating %d Inodes for %s.", inodes, out_file)
     logger.info(
         "Allocating %d MB for %s.", size // BYTES_IN_MB, out_file)
 
@@ -363,7 +429,7 @@
             int(prop_dict.get("partition_reserved_size", 0)),
             int(prop_dict.get("partition_reserved_size", 0)) // BYTES_IN_MB))
     print(
-        "The max image size for filsystem files is {} bytes ({} MB), out of a "
+        "The max image size for filesystem files is {} bytes ({} MB), out of a "
         "total partition size of {} bytes ({} MB).".format(
             int(prop_dict["image_size"]),
             int(prop_dict["image_size"]) // BYTES_IN_MB,
@@ -677,7 +743,7 @@
 
   glob_dict = LoadGlobalDict(glob_dict_file)
   if "mount_point" in glob_dict:
-    # The caller knows the mount point and provides a dictionay needed by
+    # The caller knows the mount point and provides a dictionary needed by
     # BuildImage().
     image_properties = glob_dict
   else:
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index d35e9e8..de3ead6 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -369,13 +369,13 @@
                       "SYSTEM/bin/install-recovery.sh"):
       OPTIONS.rebuild_recovery = True
 
-    # Don't copy OTA keys if we're replacing them.
+    # Don't copy OTA certs if we're replacing them.
     elif (
         OPTIONS.replace_ota_keys and
         filename in (
-            "BOOT/RAMDISK/res/keys",
+            "BOOT/RAMDISK/system/etc/security/otacerts.zip",
             "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
-            "RECOVERY/RAMDISK/res/keys",
+            "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
             "SYSTEM/etc/security/otacerts.zip",
             "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
       pass
@@ -548,6 +548,27 @@
   return "\n".join(output) + "\n"
 
 
+def WriteOtacerts(output_zip, filename, keys):
+  """Constructs a zipfile from given keys; and writes it to output_zip.
+
+  Args:
+    output_zip: The output target_files zip.
+    filename: The archive name in the output zip.
+    keys: A list of public keys to use during OTA package verification.
+  """
+
+  try:
+    from StringIO import StringIO
+  except ImportError:
+    from io import StringIO
+  temp_file = StringIO()
+  certs_zip = zipfile.ZipFile(temp_file, "w")
+  for k in keys:
+    common.ZipWrite(certs_zip, k)
+  common.ZipClose(certs_zip)
+  common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
+
+
 def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
   try:
     keylist = input_tf_zip.read("META/otakeys.txt").split()
@@ -585,39 +606,20 @@
     print("META/otakeys.txt has no keys; using %s for OTA package"
           " verification." % (mapped_keys[0],))
 
-  # recovery uses a version of the key that has been slightly
-  # predigested (by DumpPublicKey.java) and put in res/keys.
+  # recovery now uses the same x509.pem version of the keys.
   # extra_recovery_keys are used only in recovery.
-  cmd = ([OPTIONS.java_path] + OPTIONS.java_args +
-         ["-jar",
-          os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar")] +
-         mapped_keys + extra_recovery_keys)
-  p = common.Run(cmd, stdout=subprocess.PIPE)
-  new_recovery_keys, _ = p.communicate()
-  if p.returncode != 0:
-    raise common.ExternalError("failed to run dumpkeys")
-
   if misc_info.get("recovery_as_boot") == "true":
-    recovery_keys_location = "BOOT/RAMDISK/res/keys"
+    recovery_keys_location = "BOOT/RAMDISK/system/etc/security/otacerts.zip"
   else:
-    recovery_keys_location = "RECOVERY/RAMDISK/res/keys"
-  common.ZipWriteStr(output_tf_zip, recovery_keys_location, new_recovery_keys)
+    recovery_keys_location = "RECOVERY/RAMDISK/system/etc/security/otacerts.zip"
+
+  WriteOtacerts(output_tf_zip, recovery_keys_location,
+                mapped_keys + extra_recovery_keys)
 
   # SystemUpdateActivity uses the x509.pem version of the keys, but
   # put into a zipfile system/etc/security/otacerts.zip.
   # We DO NOT include the extra_recovery_keys (if any) here.
-
-  try:
-    from StringIO import StringIO
-  except ImportError:
-    from io import StringIO
-  temp_file = StringIO()
-  certs_zip = zipfile.ZipFile(temp_file, "w")
-  for k in mapped_keys:
-    common.ZipWrite(certs_zip, k)
-  common.ZipClose(certs_zip)
-  common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip",
-                     temp_file.getvalue())
+  WriteOtacerts(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", mapped_keys)
 
   # For A/B devices, update the payload verification key.
   if misc_info.get("ab_update") == "true":
@@ -638,8 +640,6 @@
         "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
         pubkey)
 
-  return new_recovery_keys
-
 
 def ReplaceVerityPublicKey(output_zip, filename, key_path):
   """Replaces the verity public key at the given path in the given zip.
diff --git a/tools/releasetools/test_build_image.py b/tools/releasetools/test_build_image.py
index 634c6b1..1cebd0c 100644
--- a/tools/releasetools/test_build_image.py
+++ b/tools/releasetools/test_build_image.py
@@ -19,7 +19,7 @@
 
 import common
 from build_image import (
-    BuildImageError, CheckHeadroom, SetUpInDirAndFsConfig)
+    BuildImageError, CheckHeadroom, GetFilesystemCharacteristics, SetUpInDirAndFsConfig)
 from test_utils import ReleaseToolsTestCase
 
 
@@ -176,3 +176,25 @@
     self.assertIn('fs-config-system\n', fs_config_data)
     self.assertIn('fs-config-root\n', fs_config_data)
     self.assertEqual('/', prop_dict['mount_point'])
+
+  def test_GetFilesystemCharacteristics(self):
+    input_dir = common.MakeTempDir()
+    output_image = common.MakeTempFile(suffix='.img')
+    command = ['mkuserimg_mke2fs', input_dir, output_image, 'ext4',
+               '/system', '409600', '-j', '0']
+    proc = common.Run(command)
+    ext4fs_output, _ = proc.communicate()
+    self.assertEqual(0, proc.returncode)
+
+    output_file = common.MakeTempFile(suffix='.img')
+    cmd = ["img2simg", output_image, output_file]
+    p = common.Run(cmd)
+    p.communicate()
+    self.assertEqual(0, p.returncode)
+
+    fs_dict = GetFilesystemCharacteristics(output_file)
+    self.assertEqual(int(fs_dict['Block size']), 4096)
+    self.assertGreaterEqual(int(fs_dict['Free blocks']), 0) # expect ~88
+    self.assertGreater(int(fs_dict['Inode count']), 0)      # expect ~64
+    self.assertGreaterEqual(int(fs_dict['Free inodes']), 0) # expect ~53
+    self.assertGreater(int(fs_dict['Inode count']), int(fs_dict['Free inodes']))