Merge "Add security patch level to hashtree footer of system"
diff --git a/core/Makefile b/core/Makefile
index 7c060ff..0bbaf12 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -833,8 +833,19 @@
 
 $(call dist-for-goals, sdk win_sdk sdk_addon, $(INSTALLED_FILES_FILE_ROOT))
 
+#------------------------------------------------------------------
+# dtb
+
+ifdef BOARD_PREBUILT_DTBIMAGE_DIR
+INSTALLED_DTBIMAGE_TARGET := $(PRODUCT_OUT)/dtb.img
+$(INSTALLED_DTBIMAGE_TARGET) : $(addprefix $(BOARD_PREBUILT_DTBIMAGE_DIR)/,\
+	$(call find-files-in-subdirs,$(BOARD_PREBUILT_DTBIMAGE_DIR),*.dtb,.))
+	cat $^ > $@
+endif
+
 # -----------------------------------------------------------------
 # the ramdisk
+ifdef BUILDING_RAMDISK_IMAGE
 INTERNAL_RAMDISK_FILES := $(filter $(TARGET_RAMDISK_OUT)/%, \
 	$(ALL_GENERATED_SOURCES) \
 	$(ALL_DEFAULT_INSTALLED_MODULES))
@@ -863,6 +874,9 @@
 	@echo "make $@: ignoring dependencies"
 	$(hide) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RAMDISK_OUT) | $(MINIGZIP) > $(INSTALLED_RAMDISK_TARGET)
 
+endif # BUILDING_RAMDISK_IMAGE
+
+
 INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
 
 ifneq ($(strip $(TARGET_NO_KERNEL)),true)
@@ -873,6 +887,10 @@
 	$(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
 	--kernel $(INSTALLED_KERNEL_TARGET)
 
+ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG
+  INTERNAL_BOOTIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET)
+endif
+
 ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
 INTERNAL_BOOTIMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET)
 endif
@@ -1224,15 +1242,12 @@
 
 INTERNAL_USERIMAGES_EXT_VARIANT :=
 ifeq ($(TARGET_USERIMAGES_USE_EXT2),true)
-INTERNAL_USERIMAGES_USE_EXT := true
 INTERNAL_USERIMAGES_EXT_VARIANT := ext2
 else
 ifeq ($(TARGET_USERIMAGES_USE_EXT3),true)
-INTERNAL_USERIMAGES_USE_EXT := true
 INTERNAL_USERIMAGES_EXT_VARIANT := ext3
 else
 ifeq ($(TARGET_USERIMAGES_USE_EXT4),true)
-INTERNAL_USERIMAGES_USE_EXT := true
 INTERNAL_USERIMAGES_EXT_VARIANT := ext4
 endif
 endif
@@ -1249,13 +1264,11 @@
   INTERNAL_USERIMAGES_SPARSE_EXT_FLAG := -s
 endif
 
-ifeq ($(INTERNAL_USERIMAGES_USE_EXT),true)
 INTERNAL_USERIMAGES_DEPS := $(SIMG2IMG)
 INTERNAL_USERIMAGES_DEPS += $(MKEXTUSERIMG) $(MAKE_EXT4FS) $(E2FSCK) $(TUNE2FS)
 ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
 INTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG) $(MAKE_F2FS)
 endif
-endif
 
 ifeq ($(BOARD_AVB_ENABLE),true)
 INTERNAL_USERIMAGES_DEPS += $(AVBTOOL)
@@ -1282,9 +1295,7 @@
 
 INTERNAL_USERIMAGES_DEPS += $(BLK_ALLOC_TO_BASE_FS)
 
-ifeq ($(INTERNAL_USERIMAGES_USE_EXT),true)
 INTERNAL_USERIMAGES_DEPS += $(MKE2FS_CONF)
-endif
 
 ifeq (true,$(PRODUCT_USE_DYNAMIC_PARTITIONS))
 
@@ -1412,6 +1423,12 @@
         $(hide) echo "avb_system_key_path=$(BOARD_AVB_SYSTEM_KEY_PATH)" >> $(1)
         $(hide) echo "avb_system_algorithm=$(BOARD_AVB_SYSTEM_ALGORITHM)" >> $(1)
         $(hide) echo "avb_system_rollback_index_location=$(BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION)" >> $(1)))
+$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_system_other_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1))
+$(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_system_other_add_hashtree_footer_args=$(BOARD_AVB_SYSTEM_OTHER_ADD_HASHTREE_FOOTER_ARGS)" >> $(1))
+$(if $(BOARD_AVB_ENABLE),\
+    $(if $(BOARD_AVB_SYSTEM_OTHER_KEY_PATH),\
+        $(hide) echo "avb_system_other_key_path=$(BOARD_AVB_SYSTEM_OTHER_KEY_PATH)" >> $(1)
+        $(hide) echo "avb_system_other_algorithm=$(BOARD_AVB_SYSTEM_OTHER_ALGORITHM)" >> $(1)))
 $(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_vendor_hashtree_enable=$(BOARD_AVB_ENABLE)" >> $(1))
 $(if $(BOARD_AVB_ENABLE),$(hide) echo "avb_vendor_add_hashtree_footer_args=$(BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS)" >> $(1))
 $(if $(BOARD_AVB_ENABLE),\
@@ -1780,6 +1797,9 @@
 ifdef BOARD_INCLUDE_RECOVERY_ACPIO
   INTERNAL_RECOVERYIMAGE_ARGS += --recovery_acpio $(BOARD_RECOVERY_ACPIO)
 endif
+ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG
+  INTERNAL_RECOVERYIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET)
+endif
 
 # Keys authorized to sign OTA packages this build will accept.  The
 # build always uses dev-keys for this; release packaging tools will
@@ -1869,6 +1889,9 @@
 ifdef BOARD_INCLUDE_RECOVERY_ACPIO
 $(INSTALLED_BOOTIMAGE_TARGET): $(BOARD_RECOVERY_ACPIO)
 endif
+ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG
+$(INSTALLED_BOOTIMAGE_TARGET): $(INSTALLED_DTBIMAGE_TARGET)
+endif
 
 $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $(MINIGZIP) \
 	    $(INTERNAL_ROOT_FILES) \
@@ -1892,6 +1915,9 @@
 ifdef BOARD_INCLUDE_RECOVERY_ACPIO
 $(INSTALLED_RECOVERYIMAGE_TARGET): $(BOARD_RECOVERY_ACPIO)
 endif
+ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG
+$(INSTALLED_RECOVERYIMAGE_TARGET): $(INSTALLED_DTBIMAGE_TARGET)
+endif
 
 $(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $(MINIGZIP) \
 	    $(INTERNAL_ROOT_FILES) \
@@ -1982,6 +2008,8 @@
 	        sed "s/^/-i /" | tr '\n' ' ') -o $@
 
 # -----------------------------------------------------------------
+ifdef BUILDING_SYSTEM_IMAGE
+
 # installed file list
 # Depending on anything that $(BUILT_SYSTEMIMAGE) depends on.
 # We put installed-files.txt ahead of image itself in the dependency graph
@@ -2147,6 +2175,8 @@
 endif
 endif
 
+endif # BUILDING_SYSTEM_IMAGE
+
 .PHONY: sync syncsys
 sync syncsys: $(INTERNAL_SYSTEMIMAGE_FILES)
 
@@ -2237,16 +2267,16 @@
 	echo "-D $(TARGET_OUT)" >> $@.lst
 	echo "-D $(TARGET_OUT_NOTICE_FILES)" >> $@.lst
 	echo "$(addprefix -f $(TARGET_OUT_UNSTRIPPED)/,$(PDK_SYMBOL_FILES_LIST))" >> $@.lst
-ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_VENDOR_IMAGE
 	echo "-D $(TARGET_OUT_VENDOR)" >> $@.lst
 endif
-ifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_PRODUCT_IMAGE
 	echo "-D $(TARGET_OUT_PRODUCT)" >> $@.lst
 endif
-ifdef BOARD_PRODUCT_SERVICESIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_PRODUCT_SERVICES_IMAGE
 	echo "-D $(TARGET_OUT_PRODUCT_SERVICES)" >> $@.lst
 endif
-ifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_ODM_IMAGE
 	echo "-D $(TARGET_OUT_ODM)" >> $@.lst
 endif
 ifneq ($(PDK_PLATFORM_JAVA_ZIP_CONTENTS),)
@@ -2314,15 +2344,7 @@
 INTERNAL_USERDATAIMAGE_FILES := \
     $(filter $(TARGET_OUT_DATA)/%,$(ALL_DEFAULT_INSTALLED_MODULES))
 
-# Don't build userdata.img if it's extfs but no partition size
-skip_userdata.img :=
-ifdef INTERNAL_USERIMAGES_EXT_VARIANT
-ifndef BOARD_USERDATAIMAGE_PARTITION_SIZE
-skip_userdata.img := true
-endif
-endif
-
-ifneq ($(skip_userdata.img),true)
+ifdef BUILDING_USERDATA_IMAGE
 userdataimage_intermediates := \
     $(call intermediates-dir-for,PACKAGING,userdata)
 BUILT_USERDATAIMAGE_TARGET := $(PRODUCT_OUT)/userdata.img
@@ -2351,8 +2373,7 @@
 userdataimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS)
 	$(build-userdataimage-target)
 
-endif # not skip_userdata.img
-skip_userdata.img :=
+endif # BUILDING_USERDATA_IMAGE
 
 # ASAN libraries in the system image - build rule.
 ASAN_OUT_DIRS_FOR_SYSTEM_INSTALL := $(sort $(patsubst $(PRODUCT_OUT)/%,%,\
@@ -2426,7 +2447,7 @@
 
 # -----------------------------------------------------------------
 # cache partition image
-ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_CACHE_IMAGE
 INTERNAL_CACHEIMAGE_FILES := \
     $(filter $(TARGET_OUT_CACHE)/%,$(ALL_DEFAULT_INSTALLED_MODULES))
 
@@ -2454,16 +2475,15 @@
 cacheimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS)
 	$(build-cacheimage-target)
 
-else # BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
+else # BUILDING_CACHE_IMAGE
 # we need to ignore the broken cache link when doing the rsync
 IGNORE_CACHE_LINK := --exclude=cache
-endif # BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
+endif # BUILDING_CACHE_IMAGE
 
 # -----------------------------------------------------------------
 # system_other partition image
+ifdef BUILDING_SYSTEM_OTHER_IMAGE
 ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true)
-BOARD_USES_SYSTEM_OTHER := true
-
 # Marker file to identify that odex files are installed
 INSTALLED_SYSTEM_OTHER_ODEX_MARKER := $(TARGET_OUT_SYSTEM_OTHER)/system-other-odex-marker
 ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_SYSTEM_OTHER_ODEX_MARKER)
@@ -2471,7 +2491,6 @@
 	$(hide) touch $@
 endif
 
-ifdef BOARD_USES_SYSTEM_OTHER
 INTERNAL_SYSTEMOTHERIMAGE_FILES := \
     $(filter $(TARGET_OUT_SYSTEM_OTHER)/%,\
       $(ALL_DEFAULT_INSTALLED_MODULES)\
@@ -2522,12 +2541,12 @@
 systemotherimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS)
 	$(build-systemotherimage-target)
 
-endif # BOARD_USES_SYSTEM_OTHER
+endif # BUILDING_SYSTEM_OTHER_IMAGE
 
 
 # -----------------------------------------------------------------
 # vendor partition image
-ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_VENDOR_IMAGE
 INTERNAL_VENDORIMAGE_FILES := \
     $(filter $(TARGET_OUT_VENDOR)/%,\
       $(ALL_DEFAULT_INSTALLED_MODULES)\
@@ -2614,7 +2633,7 @@
 
 # -----------------------------------------------------------------
 # product partition image
-ifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_PRODUCT_IMAGE
 INTERNAL_PRODUCTIMAGE_FILES := \
     $(filter $(TARGET_OUT_PRODUCT)/%,\
       $(ALL_DEFAULT_INSTALLED_MODULES)\
@@ -2669,7 +2688,7 @@
 
 # -----------------------------------------------------------------
 # product_services partition image
-ifdef BOARD_PRODUCT_SERVICESIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_PRODUCT_SERVICES_IMAGE
 INTERNAL_PRODUCT_SERVICESIMAGE_FILES := \
     $(filter $(TARGET_OUT_PRODUCT_SERVICES)/%,\
       $(ALL_DEFAULT_INSTALLED_MODULES)\
@@ -2724,7 +2743,7 @@
 
 # -----------------------------------------------------------------
 # odm partition image
-ifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_ODM_IMAGE
 INTERNAL_ODMIMAGE_FILES := \
     $(filter $(TARGET_OUT_ODM)/%,\
       $(ALL_DEFAULT_INSTALLED_MODULES)\
@@ -2821,6 +2840,23 @@
 BOARD_AVB_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem
 endif
 
+# AVB signing for system_other.img.
+ifdef BUILDING_SYSTEM_OTHER_IMAGE
+ifdef BOARD_AVB_SYSTEM_OTHER_KEY_PATH
+$(if $(BOARD_AVB_SYSTEM_OTHER_ALGORITHM),,$(error BOARD_AVB_SYSTEM_OTHER_ALGORITHM is not defined))
+else
+# If key path isn't specified, use the same key as BOARD_AVB_KEY_PATH.
+BOARD_AVB_SYSTEM_OTHER_KEY_PATH := $(BOARD_AVB_KEY_PATH)
+BOARD_AVB_SYSTEM_OTHER_ALGORITHM := $(BOARD_AVB_ALGORITHM)
+endif
+
+ifndef BOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX
+BOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
+endif
+
+BOARD_AVB_SYSTEM_OTHER_ADD_HASHTREE_FOOTER_ARGS += --rollback_index $(BOARD_AVB_SYSTEM_OTHER_ROLLBACK_INDEX)
+endif # end of AVB for BUILDING_SYSTEM_OTHER_IMAGE
+
 INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES := \
     $(BOARD_AVB_VBMETA_SYSTEM) \
     $(BOARD_AVB_VBMETA_VENDOR)
@@ -3512,6 +3548,9 @@
 ifdef BOARD_INCLUDE_RECOVERY_ACPIO
 	$(hide) cp $(BOARD_RECOVERY_ACPIO) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/recovery_acpio
 endif
+ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG
+	$(hide) cp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/dtb
+endif
 ifdef INTERNAL_KERNEL_CMDLINE
 	$(hide) echo "$(INTERNAL_KERNEL_CMDLINE)" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/cmdline
 endif
@@ -3539,6 +3578,9 @@
 ifdef INSTALLED_2NDBOOTLOADER_TARGET
 	$(hide) cp $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/BOOT/second
 endif
+ifdef INSTALLED_DTBIMAGE_TARGET
+	$(hide) cp $(INSTALLED_DTBIMAGE_TARGET) $(zip_root)/BOOT/dtb
+endif
 ifdef INTERNAL_KERNEL_CMDLINE
 	$(hide) echo "$(INTERNAL_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline
 endif
@@ -3552,33 +3594,37 @@
 	$(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),\
 	            mkdir -p $(zip_root)/RADIO; \
 	            cp $(t) $(zip_root)/RADIO/$(notdir $(t));)
+ifdef BUILDING_SYSTEM_IMAGE
 	@# Contents of the system image
 	$(hide) $(call package_files-copy-root, \
 	    $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM)
+endif
+ifdef BUILDING_USERDATA_IMAGE
 	@# Contents of the data image
 	$(hide) $(call package_files-copy-root, \
 	    $(TARGET_OUT_DATA),$(zip_root)/DATA)
-ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+endif
+ifdef BUILDING_VENDOR_IMAGE
 	@# Contents of the vendor image
 	$(hide) $(call package_files-copy-root, \
 	    $(TARGET_OUT_VENDOR),$(zip_root)/VENDOR)
 endif
-ifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_PRODUCT_IMAGE
 	@# Contents of the product image
 	$(hide) $(call package_files-copy-root, \
 	    $(TARGET_OUT_PRODUCT),$(zip_root)/PRODUCT)
 endif
-ifdef BOARD_PRODUCT_SERVICESIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_PRODUCT_SERVICES_IMAGE
 	@# Contents of the product_services image
 	$(hide) $(call package_files-copy-root, \
 	    $(TARGET_OUT_PRODUCT_SERVICES),$(zip_root)/PRODUCT_SERVICES)
 endif
-ifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_ODM_IMAGE
 	@# Contents of the odm image
 	$(hide) $(call package_files-copy-root, \
 	    $(TARGET_OUT_ODM),$(zip_root)/ODM)
 endif
-ifdef INSTALLED_SYSTEMOTHERIMAGE_TARGET
+ifdef BUILDING_SYSTEM_OTHER_IMAGE
 	@# Contents of the system_other image
 	$(hide) $(call package_files-copy-root, \
 	    $(TARGET_OUT_SYSTEM_OTHER),$(zip_root)/SYSTEM_OTHER)
@@ -3660,7 +3706,7 @@
 	$(hide) cp $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_ODM_BASE_FS_PATH) \
 	  $(zip_root)/META/$(notdir $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_ODM_BASE_FS_PATH))
 endif
-ifneq ($(strip $(SANITIZE_TARGET)),)
+ifneq (,$(filter address, $(SANITIZE_TARGET)))
 	# We need to create userdata.img with real data because the instrumented libraries are in userdata.img.
 	$(hide) echo "userdata_img_with_data=true" >> $(zip_root)/META/misc_info.txt
 endif
@@ -3777,17 +3823,19 @@
 	    echo $(part) >> $(zip_root)/META/pack_radioimages.txt;)
 	@# Run fs_config on all the system, vendor, boot ramdisk,
 	@# and recovery ramdisk files in the zip, and save the output
+ifdef BUILDING_SYSTEM_IMAGE
 	$(hide) $(call fs_config,$(zip_root)/SYSTEM,system/) > $(zip_root)/META/filesystem_config.txt
-ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+endif
+ifdef BUILDING_VENDOR_IMAGE
 	$(hide) $(call fs_config,$(zip_root)/VENDOR,vendor/) > $(zip_root)/META/vendor_filesystem_config.txt
 endif
-ifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_PRODUCT_IMAGE
 	$(hide) $(call fs_config,$(zip_root)/PRODUCT,product/) > $(zip_root)/META/product_filesystem_config.txt
 endif
-ifdef BOARD_PRODUCT_SERVICESIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_PRODUCT_SERVICES_IMAGE
 	$(hide) $(call fs_config,$(zip_root)/PRODUCT_SERVICES,product_services/) > $(zip_root)/META/product_services_filesystem_config.txt
 endif
-ifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE
+ifdef BUILDING_ODM_IMAGE
 	$(hide) $(call fs_config,$(zip_root)/ODM,odm/) > $(zip_root)/META/odm_filesystem_config.txt
 endif
 	@# ROOT always contains the files for the root under normal boot.
@@ -3803,7 +3851,7 @@
 ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
 	$(hide) $(call fs_config,$(zip_root)/RECOVERY/RAMDISK,) > $(zip_root)/META/recovery_filesystem_config.txt
 endif
-ifdef INSTALLED_SYSTEMOTHERIMAGE_TARGET
+ifdef BUILDING_SYSTEM_OTHER_IMAGE
 	$(hide) $(call fs_config,$(zip_root)/SYSTEM_OTHER,system/) > $(zip_root)/META/system_other_filesystem_config.txt
 endif
 	@# Metadata for compatibility verification.
diff --git a/core/app_certificate_validate.mk b/core/app_certificate_validate.mk
index 15ddd94..6d3fdc8 100644
--- a/core/app_certificate_validate.mk
+++ b/core/app_certificate_validate.mk
@@ -1,7 +1,5 @@
 
-ifeq (true,$(filter true, \
-   $(LOCAL_PRODUCT_MODULE) $(LOCAL_PRODUCT_SERVICES_MODULE) \
-   $(LOCAL_VENDOR_MODULE) $(LOCAL_PROPRIETARY_MODULE)))
+ifeq (true,$(non_system_module))
   ifneq (,$(filter $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))%,$(LOCAL_CERTIFICATE)))
     CERTIFICATE_VIOLATION_MODULES += $(LOCAL_MODULE)
     ifeq (true,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT))
diff --git a/core/base_rules.mk b/core/base_rules.mk
index cb9c35a..64e1069 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -96,6 +96,12 @@
 $(call pretty-error,Only one of LOCAL_PROPRIETARY_MODULE[$(LOCAL_PROPRIETARY_MODULE)] and LOCAL_VENDOR_MODULE[$(LOCAL_VENDOR_MODULE)] may be set, or they must be equal)
 endif
 
+non_system_module := $(filter true, \
+   $(LOCAL_PRODUCT_MODULE) \
+   $(LOCAL_PRODUCT_SERVICES_MODULE) \
+   $(LOCAL_VENDOR_MODULE) \
+   $(LOCAL_PROPRIETARY_MODULE))
+
 include $(BUILD_SYSTEM)/local_vndk.mk
 include $(BUILD_SYSTEM)/local_systemsdk.mk
 
diff --git a/core/check_elf_file.mk b/core/check_elf_file.mk
new file mode 100644
index 0000000..69d891c
--- /dev/null
+++ b/core/check_elf_file.mk
@@ -0,0 +1,45 @@
+# Check the correctness of the prebuilt ELF files
+#
+# This check ensures that DT_SONAME matches with the filename, DT_NEEDED
+# matches the shared libraries specified in LOCAL_SHARED_LIBRARIES, and all
+# undefined symbols in the prebuilt binary can be found in one of the shared
+# libraries specified in LOCAL_SHARED_LIBRARIES.
+#
+# Inputs:
+# - LOCAL_ALLOW_UNDEFINED_SYMBOLS
+# - LOCAL_BUILT_MODULE
+# - LOCAL_IS_HOST_MODULE
+# - LOCAL_MODULE_CLASS
+# - intermediates
+# - my_installed_module_stem
+# - my_prebuilt_src_file
+
+ifndef LOCAL_IS_HOST_MODULE
+ifneq ($(filter $(LOCAL_MODULE_CLASS),SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS),)
+check_elf_files_stamp := $(intermediates)/check_elf_files.timestamp
+$(check_elf_files_stamp): PRIVATE_SONAME := $(if $(filter $(LOCAL_MODULE_CLASS),SHARED_LIBRARIES),$(my_installed_module_stem))
+$(check_elf_files_stamp): PRIVATE_ALLOW_UNDEFINED_SYMBOLS := $(LOCAL_ALLOW_UNDEFINED_SYMBOLS)
+$(check_elf_files_stamp): PRIVATE_SHARED_LIBRARY_FILES := # This variable will be set by `core/main.mk`
+$(check_elf_files_stamp): $(my_prebuilt_src_file) $(CHECK_ELF_FILE) $(LLVM_READOBJ)
+	@echo Check prebuilt ELF binary: $<
+	$(hide) mkdir -p $(dir $@)
+	$(hide) rm -f $@
+	$(hide) $(CHECK_ELF_FILE) \
+	    --skip-bad-elf-magic \
+	    --skip-unknown-elf-machine \
+	    $(if $(PRIVATE_SONAME),--soname $(PRIVATE_SONAME)) \
+	    $(foreach l,$(PRIVATE_SHARED_LIBRARY_FILES),--shared-lib $(l)) \
+	    $(if $(PRIVATE_ALLOW_UNDEFINED_SYMBOLS),--allow-undefined-symbols) \
+	    --llvm-readobj=$(LLVM_READOBJ) \
+	    $<
+	$(hide) touch $@
+
+ifneq ($(PRODUCT_CHECK_ELF_FILES)$(CHECK_ELF_FILES),)
+ifneq ($(LOCAL_CHECK_ELF_FILES),false)
+$(LOCAL_BUILT_MODULE): $(check_elf_files_stamp)
+check-elf-files: $(check_elf_files_stamp)
+endif  # LOCAL_CHECK_ELF_FILES
+endif  # PRODUCT_CHECK_ELF_FILES or CHECK_ELF_FILES
+
+endif  # SHARED_LIBRARIES, EXECUTABLES, NATIVE_TESTS
+endif  # !LOCAL_IS_HOST_MODULE
diff --git a/core/clang/config.mk b/core/clang/config.mk
index ca3a1fa..063fd34 100644
--- a/core/clang/config.mk
+++ b/core/clang/config.mk
@@ -1,5 +1,7 @@
 ## Clang configurations.
 
+LLVM_READOBJ := $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-readobj
+
 LLVM_RTLIB_PATH := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/lib64/clang/$(LLVM_RELEASE_VERSION)/lib/linux/
 
 define convert-to-clang-flags
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index b47071a..e190916 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -304,6 +304,7 @@
 LOCAL_WHOLE_STATIC_LIBRARIES:=
 LOCAL_XOM:=
 LOCAL_YACCFLAGS:=
+LOCAL_CHECK_ELF_FILES:=
 # TODO: deprecate, it does nothing
 OVERRIDE_BUILT_MODULE_PATH:=
 
@@ -494,6 +495,7 @@
 LOCAL_IS_AUX_MODULE :=
 
 full_android_manifest :=
+non_system_module :=
 
 # Trim MAKEFILE_LIST so that $(call my-dir) doesn't need to
 # iterate over thousands of entries every time.
diff --git a/core/config.mk b/core/config.mk
index a3be194..ab0ec6d 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -212,11 +212,6 @@
 HIDDENAPI_EXTRA_APP_USAGE_JARS := \
     core-oj-hiddenapi \
 
-# Default to remove the org.apache.http.legacy from bootclasspath
-ifeq ($(REMOVE_OAHL_FROM_BCP),)
-REMOVE_OAHL_FROM_BCP := true
-endif
-
 # ###############################################################
 # Broken build defaults
 # ###############################################################
@@ -707,10 +702,13 @@
 DATA_BINDING_COMPILER := $(HOST_OUT_JAVA_LIBRARIES)/databinding-compiler.jar
 FAT16COPY := build/make/tools/fat16copy.py
 CHECK_LINK_TYPE := build/make/tools/check_link_type.py
+CHECK_ELF_FILE := build/make/tools/check_elf_file.py
 LPMAKE := $(HOST_OUT_EXECUTABLES)/lpmake$(HOST_EXECUTABLE_SUFFIX)
 BUILD_SUPER_IMAGE := build/make/tools/releasetools/build_super_image.py
 
-PROGUARD := external/proguard/bin/proguard.sh
+PROGUARD_HOME := external/proguard
+PROGUARD := $(PROGUARD_HOME)/bin/proguard.sh
+PROGUARD_DEPS := $(PROGUARD) $(PROGUARD_HOME)/lib/proguard.jar
 JAVATAGS := build/make/tools/java-event-log-tags.py
 MERGETAGS := build/make/tools/merge-event-log-tags.py
 BUILD_IMAGE_SRCS := $(wildcard build/make/tools/releasetools/*.py)
@@ -726,8 +724,6 @@
 
 DEXDUMP := $(HOST_OUT_EXECUTABLES)/dexdump2$(BUILD_EXECUTABLE_SUFFIX)
 PROFMAN := $(HOST_OUT_EXECUTABLES)/profman
-HIDDENAPI := $(HOST_OUT_EXECUTABLES)/hiddenapi
-CLASS2GREYLIST := $(HOST_OUT_EXECUTABLES)/class2greylist
 
 FINDBUGS_DIR := external/owasp/sanitizer/tools/findbugs/bin
 FINDBUGS := $(FINDBUGS_DIR)/findbugs
@@ -1065,11 +1061,11 @@
 # AOSP target built without vendor image), don't build the retrofit full OTA package. Because we
 # won't be able to build meaningful super_* images for retrofitting purpose.
 ifneq (,$(filter vendor,$(BOARD_SUPER_PARTITION_PARTITION_LIST)))
-ifndef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+ifndef BUILDING_VENDOR_IMAGE
 ifndef BOARD_PREBUILT_VENDORIMAGE
 BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE :=
 endif # BOARD_PREBUILT_VENDORIMAGE
-endif # BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+endif # BUILDING_VENDOR_IMAGE
 endif # BOARD_SUPER_PARTITION_PARTITION_LIST
 
 else # PRODUCT_RETROFIT_DYNAMIC_PARTITIONS
@@ -1226,9 +1222,8 @@
 INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/system-private-dex.txt
 endif
 
-INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi-public-list.txt
-INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi-private-list.txt
 INTERNAL_PLATFORM_HIDDENAPI_FLAGS := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi-flags.csv
+INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi-stub-flags.txt
 INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi-greylist.csv
 
 # Missing optional uses-libraries so that the platform doesn't create build rules that depend on
diff --git a/core/definitions.mk b/core/definitions.mk
index d5c7b91..2cf2bf1 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2679,8 +2679,8 @@
 	    echo "--output-dex=$(dir $(2))/`basename $$$${INPUT_DEX}`"; \
 	done | xargs $(HIDDENAPI) encode --api-flags=$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
 
-$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): $(1)
-$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): PRIVATE_DEX_INPUTS := $$(PRIVATE_DEX_INPUTS) $(1)
+$(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS): $(1)
+$(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS): PRIVATE_DEX_INPUTS := $$(PRIVATE_DEX_INPUTS) $(1)
 endef
 else  # UNSAFE_DISABLE_HIDDENAPI_FLAGS
 define hiddenapi-copy-dex-files
@@ -2696,12 +2696,12 @@
 define hiddenapi-generate-csv
 ifneq (,$(wildcard frameworks/base))
 # Only generate this target if we're in a tree with frameworks/base present.
-$(2): $(1) $(CLASS2GREYLIST) $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST)
-	$(CLASS2GREYLIST) --public-api-list $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) $(1) \
+$(2): $(1) $(CLASS2GREYLIST) $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS)
+	$(CLASS2GREYLIST) --stub-api-flags $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) $(1) \
 	    --write-flags-csv $(2)
 
-$(3): $(1) $(CLASS2GREYLIST) $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST)
-	$(CLASS2GREYLIST) --public-api-list $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) $(1) \
+$(3): $(1) $(CLASS2GREYLIST) $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS)
+	$(CLASS2GREYLIST) --stub-api-flags $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) $(1) \
 	    --write-metadata-csv $(3)
 
 $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): $(2)
@@ -2714,29 +2714,6 @@
 endif
 endef
 
-# File names for intermediate dex files of `hiddenapi-copy-soong-jar`.
-hiddenapi-soong-input-dex = $(dir $(1))/hiddenapi/dex-input/classes.dex
-hiddenapi-soong-output-dex = $(dir $(1))/hiddenapi/dex-output/classes.dex
-
-# Decompress a JAR with dex files, invoke $(HIDDENAPI) on them and compress again.
-define hiddenapi-copy-soong-jar
-$(call hiddenapi-soong-input-dex,$(2)): $(1)
-	@rm -rf `dirname $$@`
-	@mkdir -p `dirname $$@`
-	unzip -o -q $(1) 'classes*.dex' -d `dirname $$@`
-	find `dirname $$@` -maxdepth 1 -name 'classes*.dex' | xargs touch
-
-$(call hiddenapi-copy-dex-files,\
-    $(call hiddenapi-soong-input-dex,$(2)),\
-    $(call hiddenapi-soong-output-dex,$(2)))
-
-$(2): OUTPUT_DIR := $(dir $(call hiddenapi-soong-output-dex,$(2)))
-$(2): OUTPUT_JAR := $(dir $(call hiddenapi-soong-output-dex,$(2)))classes.jar
-$(2): $(1) $(call hiddenapi-soong-output-dex,$(2)) | $(SOONG_ZIP) $(MERGE_ZIPS)
-	$(SOONG_ZIP) -o $${OUTPUT_JAR} -C $${OUTPUT_DIR} -f "$${OUTPUT_DIR}/classes*.dex"
-	$(MERGE_ZIPS) -D -zipToNotStrip $${OUTPUT_JAR} -stripFile "classes*.dex" $(2) $${OUTPUT_JAR} $(1)
-endef
-
 
 ###########################################################
 ## Commands to call R8
diff --git a/core/dex_preopt.mk b/core/dex_preopt.mk
index 4d7d11c..60fd8db 100644
--- a/core/dex_preopt.mk
+++ b/core/dex_preopt.mk
@@ -47,6 +47,10 @@
 HIDDENAPI_STUBS_TEST := \
     $(call hiddenapi_stubs_jar,android_test_stubs_current)
 
+# Core Platform API stubs
+HIDDENAPI_STUBS_CORE_PLATFORM := \
+    $(call hiddenapi_stubs_jar,core.platform.api.stubs)
+
 # Allow products to define their own stubs for custom product jars that apps can use.
 ifdef PRODUCT_HIDDENAPI_STUBS
   HIDDENAPI_STUBS += $(foreach stub,$(PRODUCT_HIDDENAPI_STUBS), $(call hiddenapi_stubs_jar,$(stub)))
@@ -61,34 +65,34 @@
 endif
 
 # Singleton rule which applies $(HIDDENAPI) on all boot class path dex files.
-# Inputs are filled with `hiddenapi-copy-dex-files` rules.
-.KATI_RESTAT: \
-	$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
-	$(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): PRIVATE_HIDDENAPI_STUBS := $(HIDDENAPI_STUBS)
-$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): PRIVATE_HIDDENAPI_STUBS_SYSTEM := $(HIDDENAPI_STUBS_SYSTEM)
-$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): PRIVATE_HIDDENAPI_STUBS_TEST := $(HIDDENAPI_STUBS_TEST)
-$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): \
-    .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): $(HIDDENAPI) $(HIDDENAPI_STUBS) \
-                                             $(HIDDENAPI_STUBS_SYSTEM) $(HIDDENAPI_STUBS_TEST)
-	for INPUT_DEX in $(PRIVATE_DEX_INPUTS); do \
-		find `dirname $${INPUT_DEX}` -maxdepth 1 -name "classes*.dex"; \
-	done | sort | sed 's/^/--boot-dex=/' | xargs $(HIDDENAPI) list \
-	    --stub-classpath=$(call normalize-path-list, $(PRIVATE_HIDDENAPI_STUBS)) \
-	    --stub-classpath=$(call normalize-path-list, $(PRIVATE_HIDDENAPI_STUBS_SYSTEM)) \
-	    --stub-classpath=$(call normalize-path-list, $(PRIVATE_HIDDENAPI_STUBS_TEST)) \
-	    --out-public=$(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST).tmp \
-	    --out-private=$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST).tmp
-	$(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST))
-	$(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST))
+# Additional inputs are filled with `hiddenapi-copy-dex-files` rules.
+.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS)
+$(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS): $(SOONG_HIDDENAPI_DEX_INPUTS)
+$(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS): PRIVATE_DEX_INPUTS := $(SOONG_HIDDENAPI_DEX_INPUTS)
+$(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS): PRIVATE_HIDDENAPI_STUBS := $(HIDDENAPI_STUBS)
+$(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS): PRIVATE_HIDDENAPI_STUBS_SYSTEM := $(HIDDENAPI_STUBS_SYSTEM)
+$(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS): PRIVATE_HIDDENAPI_STUBS_TEST := $(HIDDENAPI_STUBS_TEST)
+$(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS): PRIVATE_HIDDENAPI_STUBS_CORE_PLATFORM := $(HIDDENAPI_STUBS_CORE_PLATFORM)
+$(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS): $(HIDDENAPI) $(HIDDENAPI_STUBS) \
+                                           $(HIDDENAPI_STUBS_SYSTEM) $(HIDDENAPI_STUBS_TEST) \
+                                           $(HIDDENAPI_STUBS_CORE_PLATFORM)
+	$(HIDDENAPI) list $(addprefix --boot-dex=,$(PRIVATE_DEX_INPUTS)) \
+	    --public-stub-classpath=$(call normalize-path-list, $(PRIVATE_HIDDENAPI_STUBS)) \
+	    --public-stub-classpath=$(call normalize-path-list, $(PRIVATE_HIDDENAPI_STUBS_SYSTEM)) \
+	    --public-stub-classpath=$(call normalize-path-list, $(PRIVATE_HIDDENAPI_STUBS_TEST)) \
+	    --core-platform-stub-classpath=$(call normalize-path-list, $(PRIVATE_HIDDENAPI_STUBS_CORE_PLATFORM)) \
+	    --out-api-flags=$(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS).tmp
+	$(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS))
 
-
+# Inputs to singleton rules located in frameworks/base
+# Additional inputs are filled with `hiddenapi-generate-csv`
+$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): $(SOONG_HIDDENAPI_GREYLIST_METADATA)
+$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): PRIVATE_METADATA_INPUTS := $(SOONG_HIDDENAPI_GREYLIST_METADATA)
 
 ifeq ($(PRODUCT_DIST_BOOT_AND_SYSTEM_JARS),true)
 boot_profile_jars_zip := $(PRODUCT_OUT)/boot_profile_jars.zip
 all_boot_jars := \
-  $(foreach m,$(PRODUCT_BOOT_JARS),$(PRODUCT_OUT)/system/framework/$(m).jar) \
+  $(foreach m,$(DEXPREOPT_BOOT_JARS_MODULES),$(PRODUCT_OUT)/system/framework/$(m).jar) \
   $(foreach m,$(PRODUCT_SYSTEM_SERVER_JARS),$(PRODUCT_OUT)/system/framework/$(m).jar)
 
 $(boot_profile_jars_zip): PRIVATE_JARS := $(all_boot_jars)
diff --git a/core/dex_preopt_config.mk b/core/dex_preopt_config.mk
index 3eaf55b..2dc187d 100644
--- a/core/dex_preopt_config.mk
+++ b/core/dex_preopt_config.mk
@@ -1,21 +1,27 @@
 DEX_PREOPT_CONFIG := $(PRODUCT_OUT)/dexpreopt.config
 
-# list of boot classpath jars for dexpreopt
-DEXPREOPT_BOOT_JARS_MODULES := $(strip $(filter-out conscrypt,$(PRODUCT_BOOT_JARS)))
-PRODUCT_BOOTCLASSPATH_JARS := $(strip $(DEXPREOPT_BOOT_JARS_MODULES) $(filter conscrypt,$(PRODUCT_BOOT_JARS)))
-PRODUCT_BOOTCLASSPATH := $(subst $(space),:,$(foreach m,$(PRODUCT_BOOTCLASSPATH_JARS),/system/framework/$(m).jar))
+RUNTIME_MODULES := $(filter-out $(PRODUCT_UPDATABLE_BOOT_MODULES), $(TARGET_CORE_JARS))
+FRAMEWORK_MODULES := $(filter-out $(PRODUCT_UPDATABLE_BOOT_MODULES) $(RUNTIME_MODULES), $(PRODUCT_BOOT_JARS))
 
-PRODUCT_SYSTEM_SERVER_CLASSPATH := $(subst $(space),:,$(foreach m,$(PRODUCT_SYSTEM_SERVER_JARS),/system/framework/$(m).jar))
+NON_UPDATABLE_BOOT_MODULES := $(RUNTIME_MODULES) $(FRAMEWORK_MODULES)
+NON_UPDATABLE_BOOT_LOCATIONS := $(foreach m,$(RUNTIME_MODULES),/apex/com.android.runtime/javalib/$(m).jar)
+NON_UPDATABLE_BOOT_LOCATIONS += $(foreach m,$(FRAMEWORK_MODULES),/system/framework/$(m).jar)
+ALL_BOOT_LOCATIONS := $(NON_UPDATABLE_BOOT_LOCATIONS) $(PRODUCT_UPDATABLE_BOOT_LOCATIONS)
+ALL_BOOT_MODULES := $(NON_UPDATABLE_BOOT_MODULES) $(PRODUCT_UPDATABLE_BOOT_MODULES)
 
+PRODUCT_BOOTCLASSPATH := $(subst $(space),:,$(ALL_BOOT_LOCATIONS))
+
+DEXPREOPT_BOOT_JARS_MODULES := $(NON_UPDATABLE_BOOT_MODULES)
+DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS := $(NON_UPDATABLE_BOOT_LOCATIONS)
+DEXPREOPT_BOOT_JARS_INPUT_PATH := $(PRODUCT_OUT)/dex_bootjars_input
+DEXPREOPT_BOOTCLASSPATH_DEX_FILES := $(foreach m,$(NON_UPDATABLE_BOOT_MODULES),$(DEXPREOPT_BOOT_JARS_INPUT_PATH)/$(m).jar)
+
+# Create paths for boot image.
 DEXPREOPT_BUILD_DIR := $(OUT_DIR)
 DEXPREOPT_PRODUCT_DIR_FULL_PATH := $(PRODUCT_OUT)/dex_bootjars
 DEXPREOPT_PRODUCT_DIR := $(patsubst $(DEXPREOPT_BUILD_DIR)/%,%,$(DEXPREOPT_PRODUCT_DIR_FULL_PATH))
 DEXPREOPT_BOOT_JAR_DIR := system/framework
 DEXPREOPT_BOOT_JAR_DIR_FULL_PATH := $(DEXPREOPT_PRODUCT_DIR_FULL_PATH)/$(DEXPREOPT_BOOT_JAR_DIR)
-
-DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS := $(foreach m,$(PRODUCT_BOOTCLASSPATH_JARS),/$(DEXPREOPT_BOOT_JAR_DIR)/$(m).jar)
-DEXPREOPT_BOOTCLASSPATH_DEX_FILES := $(foreach jar,$(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS),$(PRODUCT_OUT)$(jar))
-
 DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION := $(DEXPREOPT_BOOT_JAR_DIR_FULL_PATH)/boot.art
 DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME := $(DEXPREOPT_BOOT_JAR_DIR_FULL_PATH)/$(DEX2OAT_TARGET_ARCH)/boot.art
 
@@ -24,6 +30,8 @@
   $(TARGET_2ND_ARCH_VAR_PREFIX)DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME := $(DEXPREOPT_BOOT_JAR_DIR_FULL_PATH)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)/boot.art
 endif
 
+PRODUCT_SYSTEM_SERVER_CLASSPATH := $(subst $(space),:,$(foreach m,$(PRODUCT_SYSTEM_SERVER_JARS),/system/framework/$(m).jar))
+
 # The default value for LOCAL_DEX_PREOPT
 DEX_PREOPT_DEFAULT ?= true
 
@@ -112,10 +120,10 @@
   $(call add_json_bool, OnlyPreoptBootImageAndSystemServer, $(filter true,$(WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY)))
   $(call add_json_bool, DontUncompressPrivAppsDex,          $(filter true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS)))
   $(call add_json_list, ModulesLoadedByPrivilegedModules,   $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES))
+  $(call add_json_list, PreoptBootClassPathDexFiles,        $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES))
   $(call add_json_bool, HasSystemOther,                     $(BOARD_USES_SYSTEM_OTHER_ODEX))
   $(call add_json_list, PatternsOnSystemOther,              $(SYSTEM_OTHER_ODEX_FILTER))
   $(call add_json_bool, DisableGenerateProfile,             $(filter false,$(WITH_DEX_PREOPT_GENERATE_PROFILE)))
-  $(call add_json_list, PreoptBootClassPathDexFiles,        $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES))
   $(call add_json_list, PreoptBootClassPathDexLocations,    $(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS))
   $(call add_json_list, BootJars,                           $(PRODUCT_BOOT_JARS))
   $(call add_json_list, PreoptBootJars,                     $(DEXPREOPT_BOOT_JARS_MODULES))
@@ -204,3 +212,6 @@
     DEXPREOPT_GEN_DEPS += $($(TARGET_2ND_ARCH_VAR_PREFIX)DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME)
   endif
 endif
+
+DEXPREOPT_STRIP_DEPS := \
+  $(ZIP2ZIP) \
diff --git a/core/dex_preopt_libart.mk b/core/dex_preopt_libart.mk
index 631db0a..de7dcdd 100644
--- a/core/dex_preopt_libart.mk
+++ b/core/dex_preopt_libart.mk
@@ -7,8 +7,11 @@
 # The full system boot classpath
 
 LIBART_TARGET_BOOT_JARS := $(DEXPREOPT_BOOT_JARS_MODULES)
-LIBART_TARGET_BOOT_DEX_LOCATIONS := $(foreach jar,$(LIBART_TARGET_BOOT_JARS),/$(DEXPREOPT_BOOT_JAR_DIR)/$(jar).jar)
-LIBART_TARGET_BOOT_DEX_FILES := $(foreach jar,$(LIBART_TARGET_BOOT_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),,COMMON)/javalib.jar)
+LIBART_TARGET_BOOT_DEX_LOCATIONS := $(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS)
+LIBART_TARGET_BOOT_DEX_FILES := $(foreach mod,$(NON_UPDATABLE_BOOT_MODULES),$(call intermediates-dir-for,JAVA_LIBRARIES,$(mod),,COMMON)/javalib.jar)
+
+# Copy the files to a location Soong dex preopt will look at.
+$(foreach mod,$(LIBART_TARGET_BOOT_JARS),$(eval $(call copy-one-file,$(call intermediates-dir-for,JAVA_LIBRARIES,$(mod),,COMMON)/javalib.jar,$(DEXPREOPT_BOOT_JARS_INPUT_PATH)/$(mod).jar)))
 
 # dex preopt on the bootclasspath produces multiple files.  The first dex file
 # is converted into to boot.art (to match the legacy assumption that boot.art
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index 69790cb..58c458a 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -122,8 +122,7 @@
   ifeq ($(LOCAL_MODULE_CLASS),JAVA_LIBRARIES)
     my_module_multilib := $(LOCAL_MULTILIB)
     # If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
-    my_filtered_lib_name := $(patsubst %.impl,%,$(LOCAL_MODULE))
-    ifeq (,$(filter $(JAVA_SDK_LIBRARIES),$(my_filtered_lib_name)))
+    ifeq (,$(filter $(JAVA_SDK_LIBRARIES),$(LOCAL_MODULE)))
       # For a Java library, by default we build odex for both 1st arch and 2nd arch.
       # But it can be overridden with "LOCAL_MULTILIB := first".
       ifneq (,$(filter $(PRODUCT_SYSTEM_SERVER_JARS),$(LOCAL_MODULE)))
@@ -192,7 +191,7 @@
   $(call add_json_list, OptionalUsesLibraries,         $(LOCAL_OPTIONAL_USES_LIBRARIES))
   $(call add_json_list, UsesLibraries,                 $(LOCAL_USES_LIBRARIES))
   $(call add_json_map,  LibraryPaths)
-  $(foreach lib,$(sort $(LOCAL_USES_LIBRARIES) $(LOCAL_OPTIONAL_USES_LIBRARIES) org.apache.http.legacy.impl android.hidl.base-V1.0-java android.hidl.manager-V1.0-java),\
+  $(foreach lib,$(sort $(LOCAL_USES_LIBRARIES) $(LOCAL_OPTIONAL_USES_LIBRARIES) org.apache.http.legacy android.hidl.base-V1.0-java android.hidl.manager-V1.0-java),\
     $(call add_json_str, $(lib), $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar))
   $(call end_json_map)
   $(call add_json_list, Archs,                         $(my_dexpreopt_archs))
@@ -234,7 +233,7 @@
   my_dexpreopt_deps := $(my_dex_jar)
   my_dexpreopt_deps += $(if $(my_process_profile),$(LOCAL_DEX_PREOPT_PROFILE))
   my_dexpreopt_deps += \
-    $(foreach lib,$(sort $(LOCAL_USES_LIBRARIES) $(LOCAL_OPTIONAL_USES_LIBRARIES) org.apache.http.legacy.impl android.hidl.base-V1.0-java android.hidl.manager-V1.0-java),\
+    $(foreach lib,$(sort $(LOCAL_USES_LIBRARIES) $(LOCAL_OPTIONAL_USES_LIBRARIES) org.apache.http.legacy android.hidl.base-V1.0-java android.hidl.manager-V1.0-java),\
       $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar)
   my_dexpreopt_deps += $(LOCAL_DEX_PREOPT_IMAGE_LOCATION)
   # TODO: default boot images
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 8150fb6..390a182 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -296,26 +296,112 @@
 endif
 
 ###########################################
+# Configure whether we're building the system image
+BUILDING_SYSTEM_IMAGE := true
+ifeq ($(PRODUCT_BUILD_SYSTEM_IMAGE),)
+  ifndef PRODUCT_USE_DYNAMIC_PARTITION_SIZE
+    ifndef BOARD_SYSTEMIMAGE_PARTITION_SIZE
+      BUILDING_SYSTEM_IMAGE :=
+    endif
+  endif
+else ifeq ($(PRODUCT_BUILD_SYSTEM_IMAGE),false)
+  BUILDING_SYSTEM_IMAGE :=
+endif
+.KATI_READONLY := BUILDING_SYSTEM_IMAGE
+
+# Are we building a system_other image
+BUILDING_SYSTEM_OTHER_IMAGE :=
+ifeq ($(PRODUCT_BUILD_SYSTEM_OTHER_IMAGE),)
+  ifdef BUILDING_SYSTEM_IMAGE
+    ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true)
+      BUILDING_SYSTEM_OTHER_IMAGE := true
+    endif
+  endif
+else ifeq ($(PRODUCT_BUILD_SYSTEM_OTHER_IMAGE),true)
+  BUILDING_SYSTEM_OTHER_IMAGE := true
+  ifndef BUILDING_SYSTEM_IMAGE
+    $(error PRODUCT_BUILD_SYSTEM_OTHER_IMAGE = true requires building the system image)
+  endif
+endif
+.KATI_READONLY := BUILDING_SYSTEM_OTHER_IMAGE
+
+# Are we building a cache image
+BUILDING_CACHE_IMAGE :=
+ifeq ($(PRODUCT_BUILD_CACHE_IMAGE),)
+  ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
+    BUILDING_CACHE_IMAGE := true
+  endif
+else ifeq ($(PRODUCT_BUILD_CACHE_IMAGE),true)
+  BUILDING_CACHE_IMAGE := true
+  ifndef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
+    $(error PRODUCT_BUILD_CACHE_IMAGE set to true, but BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE not defined)
+  endif
+endif
+.KATI_READONLY := BUILDING_CACHE_IMAGE
+
+# TODO: Add BUILDING_BOOT_IMAGE / BUILDING_RECOVERY_IMAGE
+# This gets complicated with BOARD_USES_RECOVERY_AS_BOOT, so skipping for now.
+
+# Are we building a ramdisk image
+BUILDING_RAMDISK_IMAGE := true
+ifeq ($(PRODUCT_BUILD_RAMDISK_IMAGE),)
+  # TODO: Be smarter about this. This probably only needs to happen when one of the follow is true:
+  #  BUILDING_BOOT_IMAGE
+  #  BUILDING_RECOVERY_IMAGE
+else ifeq ($(PRODUCT_BUILD_RAMDISK_IMAGE),false)
+  BUILDING_RAMDISK_IMAGE :=
+endif
+.KATI_READONLY := BUILDING_RAMDISK_IMAGE
+
+# Are we building a userdata image
+BUILDING_USERDATA_IMAGE :=
+ifeq ($(PRODUCT_BUILD_USERDATA_IMAGE),)
+  ifdef BOARD_USERDATAIMAGE_PARTITION_SIZE
+    BUILDING_USERDATA_IMAGE := true
+  endif
+else ifeq ($(PRODUCT_BUILD_USERDATA_IMAGE),true)
+  BUILDING_USERDATA_IMAGE := true
+endif
+.KATI_READONLY := BUILDING_USERDATA_IMAGE
+
+###########################################
 # Now we can substitute with the real value of TARGET_COPY_OUT_VENDOR
 ifeq ($(TARGET_COPY_OUT_VENDOR),$(_vendor_path_placeholder))
-TARGET_COPY_OUT_VENDOR := system/vendor
+  TARGET_COPY_OUT_VENDOR := system/vendor
 else ifeq ($(filter vendor system/vendor,$(TARGET_COPY_OUT_VENDOR)),)
-$(error TARGET_COPY_OUT_VENDOR must be either 'vendor' or 'system/vendor', seeing '$(TARGET_COPY_OUT_VENDOR)'.)
+  $(error TARGET_COPY_OUT_VENDOR must be either 'vendor' or 'system/vendor', seeing '$(TARGET_COPY_OUT_VENDOR)'.)
 endif
 PRODUCT_COPY_FILES := $(subst $(_vendor_path_placeholder),$(TARGET_COPY_OUT_VENDOR),$(PRODUCT_COPY_FILES))
 
 BOARD_USES_VENDORIMAGE :=
 ifdef BOARD_PREBUILT_VENDORIMAGE
-BOARD_USES_VENDORIMAGE := true
+  BOARD_USES_VENDORIMAGE := true
 endif
 ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
-BOARD_USES_VENDORIMAGE := true
+  BOARD_USES_VENDORIMAGE := true
 endif
 ifeq ($(TARGET_COPY_OUT_VENDOR),vendor)
-BOARD_USES_VENDORIMAGE := true
+  BOARD_USES_VENDORIMAGE := true
 else ifdef BOARD_USES_VENDORIMAGE
-$(error TARGET_COPY_OUT_VENDOR must be set to 'vendor' to use a vendor image)
+  $(error TARGET_COPY_OUT_VENDOR must be set to 'vendor' to use a vendor image)
 endif
+.KATI_READONLY := BOARD_USES_VENDORIMAGE
+
+BUILDING_VENDOR_IMAGE :=
+ifeq ($(PRODUCT_BUILD_VENDOR_IMAGE),)
+  ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+    BUILDING_VENDOR_IMAGE := true
+  endif
+else ifeq ($(PRODUCT_BUILD_VENDOR_IMAGE),true)
+  BUILDING_VENDOR_IMAGE := true
+  ifndef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+    $(error PRODUCT_BUILD_VENDOR_IMAGE set to true, but BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE not defined)
+  endif
+endif
+ifdef BOARD_PREBUILT_VENDORIMAGE
+  BUILDING_VENDOR_IMAGE :=
+endif
+.KATI_READONLY := BUILDING_VENDOR_IMAGE
 
 ###########################################
 # Now we can substitute with the real value of TARGET_COPY_OUT_PRODUCT
@@ -328,61 +414,110 @@
 
 BOARD_USES_PRODUCTIMAGE :=
 ifdef BOARD_PREBUILT_PRODUCTIMAGE
-BOARD_USES_PRODUCTIMAGE := true
+  BOARD_USES_PRODUCTIMAGE := true
 endif
 ifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE
-BOARD_USES_PRODUCTIMAGE := true
+  BOARD_USES_PRODUCTIMAGE := true
 endif
 ifeq ($(TARGET_COPY_OUT_PRODUCT),product)
-BOARD_USES_PRODUCTIMAGE := true
+  BOARD_USES_PRODUCTIMAGE := true
 else ifdef BOARD_USES_PRODUCTIMAGE
-$(error TARGET_COPY_OUT_PRODUCT must be set to 'product' to use a product image)
+  $(error TARGET_COPY_OUT_PRODUCT must be set to 'product' to use a product image)
 endif
+.KATI_READONLY := BOARD_USES_PRODUCTIMAGE
+
+BUILDING_PRODUCT_IMAGE :=
+ifeq ($(PRODUCT_BUILD_PRODUCT_IMAGE),)
+  ifdef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE
+    BUILDING_PRODUCT_IMAGE := true
+  endif
+else ifeq ($(PRODUCT_BUILD_PRODUCT_IMAGE),true)
+  BUILDING_PRODUCT_IMAGE := true
+  ifndef BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE
+    $(error PRODUCT_BUILD_PRODUCT_IMAGE set to true, but BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE not defined)
+  endif
+endif
+ifdef BOARD_PREBUILT_PRODUCTIMAGE
+  BUILDING_PRODUCT_IMAGE :=
+endif
+.KATI_READONLY := BUILDING_PRODUCT_IMAGE
 
 ###########################################
 # Now we can substitute with the real value of TARGET_COPY_OUT_PRODUCT_SERVICES
 ifeq ($(TARGET_COPY_OUT_PRODUCT_SERVICES),$(_product_services_path_placeholder))
-TARGET_COPY_OUT_PRODUCT_SERVICES := system/product_services
+  TARGET_COPY_OUT_PRODUCT_SERVICES := system/product_services
 else ifeq ($(filter product_services system/product_services,$(TARGET_COPY_OUT_PRODUCT_SERVICES)),)
-$(error TARGET_COPY_OUT_PRODUCT_SERVICES must be either 'product_services' or 'system/product_services', seeing '$(TARGET_COPY_OUT_PRODUCT_SERVICES)'.)
+  $(error TARGET_COPY_OUT_PRODUCT_SERVICES must be either 'product_services' or 'system/product_services', seeing '$(TARGET_COPY_OUT_PRODUCT_SERVICES)'.)
 endif
 PRODUCT_COPY_FILES := $(subst $(_product_services_path_placeholder),$(TARGET_COPY_OUT_PRODUCT_SERVICES),$(PRODUCT_COPY_FILES))
 
 BOARD_USES_PRODUCT_SERVICESIMAGE :=
 ifdef BOARD_PREBUILT_PRODUCT_SERVICESIMAGE
-BOARD_USES_PRODUCT_SERVICESIMAGE := true
+  BOARD_USES_PRODUCT_SERVICESIMAGE := true
 endif
 ifdef BOARD_PRODUCT_SERVICESIMAGE_FILE_SYSTEM_TYPE
-BOARD_USES_PRODUCT_SERVICESIMAGE := true
+  BOARD_USES_PRODUCT_SERVICESIMAGE := true
 endif
 ifeq ($(TARGET_COPY_OUT_PRODUCT_SERVICES),product_services)
-BOARD_USES_PRODUCT_SERVICESIMAGE := true
+  BOARD_USES_PRODUCT_SERVICESIMAGE := true
 else ifdef BOARD_USES_PRODUCT_SERVICESIMAGE
-$(error TARGET_COPY_OUT_PRODUCT_SERVICES must be set to 'product_services' to use a product_services image)
+  $(error TARGET_COPY_OUT_PRODUCT_SERVICES must be set to 'product_services' to use a product_services image)
 endif
 
+BUILDING_PRODUCT_SERVICES_IMAGE :=
+ifeq ($(PRODUCT_SERVICES_BUILD_PRODUCT_SERVICES_IMAGE),)
+  ifdef BOARD_PRODUCT_SERVICESIMAGE_FILE_SYSTEM_TYPE
+    BUILDING_PRODUCT_SERVICES_IMAGE := true
+  endif
+else ifeq ($(PRODUCT_BUILD_PRODUCT_SERVICES_IMAGE),true)
+  BUILDING_PRODUCT_SERVICES_IMAGE := true
+  ifndef BOARD_PRODUCT_SERVICESIMAGE_FILE_SYSTEM_TYPE
+    $(error PRODUCT_BUILD_PRODUCT_SERVICES_IMAGE set to true, but BOARD_PRODUCT_SERVICESIMAGE_FILE_SYSTEM_TYPE not defined)
+  endif
+endif
+ifdef BOARD_PREBUILT_PRODUCT_SERVICESIMAGE
+  BUILDING_PRODUCT_SERVICES_IMAGE :=
+endif
+.KATI_READONLY := BUILDING_PRODUCT_SERVICES_IMAGE
+
 ###########################################
 # Now we can substitute with the real value of TARGET_COPY_OUT_ODM
 ifeq ($(TARGET_COPY_OUT_ODM),$(_odm_path_placeholder))
-TARGET_COPY_OUT_ODM := vendor/odm
+  TARGET_COPY_OUT_ODM := vendor/odm
 else ifeq ($(filter odm vendor/odm,$(TARGET_COPY_OUT_ODM)),)
-$(error TARGET_COPY_OUT_ODM must be either 'odm' or 'vendor/odm', seeing '$(TARGET_COPY_OUT_ODM)'.)
+  $(error TARGET_COPY_OUT_ODM must be either 'odm' or 'vendor/odm', seeing '$(TARGET_COPY_OUT_ODM)'.)
 endif
 PRODUCT_COPY_FILES := $(subst $(_odm_path_placeholder),$(TARGET_COPY_OUT_ODM),$(PRODUCT_COPY_FILES))
 
 BOARD_USES_ODMIMAGE :=
 ifdef BOARD_PREBUILT_ODMIMAGE
-BOARD_USES_ODMIMAGE := true
+  BOARD_USES_ODMIMAGE := true
 endif
 ifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE
-BOARD_USES_ODMIMAGE := true
+  BOARD_USES_ODMIMAGE := true
 endif
 ifeq ($(TARGET_COPY_OUT_ODM),odm)
-BOARD_USES_ODMIMAGE := true
+  BOARD_USES_ODMIMAGE := true
 else ifdef BOARD_USES_ODMIMAGE
-$(error TARGET_COPY_OUT_ODM must be set to 'odm' to use an odm image)
+  $(error TARGET_COPY_OUT_ODM must be set to 'odm' to use an odm image)
 endif
 
+BUILDING_ODM_IMAGE :=
+ifeq ($(ODM_BUILD_ODM_IMAGE),)
+  ifdef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE
+    BUILDING_ODM_IMAGE := true
+  endif
+else ifeq ($(PRODUCT_BUILD_ODM_IMAGE),true)
+  BUILDING_ODM_IMAGE := true
+  ifndef BOARD_ODMIMAGE_FILE_SYSTEM_TYPE
+    $(error PRODUCT_BUILD_ODM_IMAGE set to true, but BOARD_ODMIMAGE_FILE_SYSTEM_TYPE not defined)
+  endif
+endif
+ifdef BOARD_PREBUILT_ODMIMAGE
+  BUILDING_ODM_IMAGE :=
+endif
+.KATI_READONLY := BUILDING_ODM_IMAGE
+
 ###########################################
 # Ensure that only TARGET_RECOVERY_UPDATER_LIBS *or* AB_OTA_UPDATER is set.
 TARGET_RECOVERY_UPDATER_LIBS ?=
diff --git a/core/install_jni_libs_internal.mk b/core/install_jni_libs_internal.mk
index e786691..e0f1ad4 100644
--- a/core/install_jni_libs_internal.mk
+++ b/core/install_jni_libs_internal.mk
@@ -57,7 +57,7 @@
 
 # Create symlink in the app specific lib path
 # Skip creating this symlink when running the second part of a target sanitization build.
-ifndef SANITIZE_TARGET
+ifeq ($(filter address,$(SANITIZE_TARGET)),)
 ifdef LOCAL_POST_INSTALL_CMD
 # Add a shell command separator
 LOCAL_POST_INSTALL_CMD += ;
diff --git a/core/java_common.mk b/core/java_common.mk
index ac26e5e..f5da120 100644
--- a/core/java_common.mk
+++ b/core/java_common.mk
@@ -280,7 +280,7 @@
       sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(call resolve-prebuilt-sdk-module,system_current,$(lib_name)))
     else
       # When SDK libraries are referenced from modules built without SDK, provide the all APIs to them
-      sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(lib_name).impl)
+      sdk_libs := $(foreach lib_name,$(LOCAL_SDK_LIBRARIES),$(lib_name))
     endif
   else
     ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true)
@@ -386,6 +386,7 @@
   endif # USE_CORE_LIB_BOOTCLASSPATH
 endif # !LOCAL_IS_HOST_MODULE
 
+ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS := $(ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS) $(full_java_bootclasspath_libs)
 
 # Export the SDK libs. The sdk library names listed in LOCAL_SDK_LIBRARIES are first exported.
 # Then sdk library names exported from dependencies are all re-exported.
diff --git a/core/java_library.mk b/core/java_library.mk
index 3e54b0e..766fff5 100644
--- a/core/java_library.mk
+++ b/core/java_library.mk
@@ -95,7 +95,7 @@
 
 $(LOCAL_BUILT_MODULE): PRIVATE_STRIP_SCRIPT := $(intermediates)/strip.sh
 $(LOCAL_BUILT_MODULE): $(intermediates)/strip.sh
-$(LOCAL_BUILT_MODULE): | $(DEXPREOPT_GEN_DEPS)
+$(LOCAL_BUILT_MODULE): | $(DEXPREOPT_STRIP_DEPS)
 $(LOCAL_BUILT_MODULE): .KATI_DEPFILE := $(LOCAL_BUILT_MODULE).d
 $(LOCAL_BUILT_MODULE): $(common_javalib.jar)
 	$(PRIVATE_STRIP_SCRIPT) $< $@
diff --git a/core/main.mk b/core/main.mk
index c84cbe0..96b2132 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -786,9 +786,43 @@
 $(call resolve-shared-libs-depes,HOST_CROSS_,,true)
 endif
 
+# Pass the shared libraries dependencies to prebuilt ELF file check.
+define add-elf-file-check-shared-lib
+$(1): PRIVATE_SHARED_LIBRARY_FILES += $(2)
+$(1): $(2)
+endef
+
+define resolve-shared-libs-for-elf-file-check
+$(foreach m,$($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))$(1)DEPENDENCIES_ON_SHARED_LIBRARIES),\
+  $(eval p := $(subst :,$(space),$(m)))\
+  $(eval mod := $(firstword $(p)))\
+  \
+  $(eval deps := $(subst $(comma),$(space),$(lastword $(p))))\
+  $(if $(2),$(eval deps := $(addsuffix $($(1)2ND_ARCH_MODULE_SUFFIX),$(deps))))\
+  $(eval root := $(1)OUT$(if $(call streq,$(1),TARGET_),_ROOT))\
+  $(eval deps := $(filter $($(root))/%$($(1)SHLIB_SUFFIX),$(call module-built-files,$(deps))))\
+  \
+  $(eval r := $(firstword $(filter \
+    $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/EXECUTABLES/%\
+    $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/NATIVE_TESTS/%\
+    $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/SHARED_LIBRARIES/%,\
+    $(call module-built-files,$(mod)))))\
+  \
+  $(if $(r),\
+    $(eval stamp := $(dir $(r))check_elf_files.timestamp)\
+    $(eval $(call add-elf-file-check-shared-lib,$(stamp),$(deps)))\
+  ))
+endef
+
+$(call resolve-shared-libs-for-elf-file-check,TARGET_)
+ifdef TARGET_2ND_ARCH
+$(call resolve-shared-libs-for-elf-file-check,TARGET_,true)
+endif
+
 m :=
 r :=
 p :=
+stamp :=
 deps :=
 add-required-deps :=
 
@@ -1085,8 +1119,8 @@
   # Verify the artifact path requirements made by included products.
   is_asan := $(if $(filter address,$(SANITIZE_TARGET)),true)
   ifneq (true,$(or $(is_asan),$(DISABLE_ARTIFACT_PATH_REQUIREMENTS)))
-  # Fakes don't get installed, and host files are irrelevant.
-  static_whitelist_patterns := $(TARGET_OUT_FAKE)/% $(HOST_OUT)/%
+  # Fakes don't get installed, host files are irrelevant, and NDK stubs aren't installed to device.
+  static_whitelist_patterns := $(TARGET_OUT_FAKE)/% $(HOST_OUT)/% $(SOONG_OUT_DIR)/ndk/%
   # RROs become REQUIRED by the source module, but are always placed on the vendor partition.
   static_whitelist_patterns += %__auto_generated_rro.apk
   ifeq (true,$(BOARD_USES_SYSTEM_OTHER_ODEX))
@@ -1529,6 +1563,9 @@
 .PHONY: findlsdumps
 findlsdumps: $(FIND_LSDUMPS_FILE)
 
+.PHONY: check-elf-files
+check-elf-files:
+
 #xxx scrape this from ALL_MODULE_NAME_TAGS
 .PHONY: modules
 modules:
diff --git a/core/package_internal.mk b/core/package_internal.mk
index 31cb01e..9ddbd1b 100644
--- a/core/package_internal.mk
+++ b/core/package_internal.mk
@@ -574,9 +574,7 @@
 # Run veridex on product, product_services and vendor modules.
 # We skip it for unbundled app builds where we cannot build veridex.
 module_run_appcompat :=
-ifeq (true,$(filter true, \
-   $(LOCAL_PRODUCT_MODULE) $(LOCAL_PRODUCT_SERVICES_MODULE) \
-   $(LOCAL_VENDOR_MODULE) $(LOCAL_PROPRIETARY_MODULE)))
+ifeq (true,$(non_system_module))
 ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))  # ! unbundled app build
   module_run_appcompat := true
 endif
@@ -609,7 +607,7 @@
 ifdef LOCAL_DEX_PREOPT
 $(LOCAL_BUILT_MODULE) : PRIVATE_STRIP_SCRIPT := $(intermediates)/strip.sh
 $(LOCAL_BUILT_MODULE) : $(intermediates)/strip.sh
-$(LOCAL_BUILT_MODULE) : | $(DEXPREOPT_GEN_DEPS)
+$(LOCAL_BUILT_MODULE) : | $(DEXPREOPT_STRIP_DEPS)
 $(LOCAL_BUILT_MODULE): .KATI_DEPFILE := $(LOCAL_BUILT_MODULE).d
 endif
 $(LOCAL_BUILT_MODULE):
diff --git a/core/prebuilt_internal.mk b/core/prebuilt_internal.mk
index 960d8d1..0b48a77 100644
--- a/core/prebuilt_internal.mk
+++ b/core/prebuilt_internal.mk
@@ -72,9 +72,7 @@
 # Run veridex on product, product_services and vendor modules.
 # We skip it for unbundled app builds where we cannot build veridex.
 module_run_appcompat :=
-ifeq (true,$(filter true, \
-   $(LOCAL_PRODUCT_MODULE) $(LOCAL_PRODUCT_SERVICES_MODULE) \
-   $(LOCAL_VENDOR_MODULE) $(LOCAL_PROPRIETARY_MODULE)))
+ifeq (true,$(non_system_module))
 ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))  # ! unbundled app build
   module_run_appcompat := true
 endif
@@ -170,6 +168,9 @@
 include $(BUILD_SYSTEM)/link_type.mk
 endif  # prebuilt_module_is_a_library
 
+# Check prebuilt ELF binaries.
+include $(BUILD_SYSTEM)/check_elf_file.mk
+
 # The real dependency will be added after all Android.mks are loaded and the install paths
 # of the shared libraries are determined.
 ifdef LOCAL_INSTALLED_MODULE
@@ -363,7 +364,7 @@
 ifdef LOCAL_DEX_PREOPT
 $(built_module) : PRIVATE_STRIP_SCRIPT := $(intermediates)/strip.sh
 $(built_module) : $(intermediates)/strip.sh
-$(built_module) : | $(DEXPREOPT_GEN_DEPS)
+$(built_module) : | $(DEXPREOPT_STRIP_DEPS)
 $(built_module) : .KATI_DEPFILE := $(built_module).d
 endif
 endif
@@ -464,7 +465,7 @@
 
 $(built_module): PRIVATE_STRIP_SCRIPT := $(intermediates)/strip.sh
 $(built_module): $(intermediates)/strip.sh
-$(built_module): | $(DEXPREOPT_GEN_DEPS)
+$(built_module): | $(DEXPREOPT_STRIP_DEPS)
 $(built_module): .KATI_DEPFILE := $(built_module).d
 $(built_module): $(my_prebuilt_src_file)
 	$(PRIVATE_STRIP_SCRIPT) $< $@
diff --git a/core/product.mk b/core/product.mk
index 2d7ace2..c3cbb0c 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -216,6 +216,18 @@
     PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS \
     PRODUCT_XOM_EXCLUDE_PATHS \
     PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES \
+    PRODUCT_BUILD_SYSTEM_IMAGE \
+    PRODUCT_BUILD_SYSTEM_OTHER_IMAGE \
+    PRODUCT_BUILD_VENDOR_IMAGE \
+    PRODUCT_BUILD_PRODUCT_IMAGE \
+    PRODUCT_BUILD_PRODUCT_SERVICES_IMAGE \
+    PRODUCT_BUILD_ODM_IMAGE \
+    PRODUCT_BUILD_CACHE_IMAGE \
+    PRODUCT_BUILD_RAMDISK_IMAGE \
+    PRODUCT_BUILD_USERDATA_IMAGE \
+    PRODUCT_UPDATABLE_BOOT_MODULES \
+    PRODUCT_UPDATABLE_BOOT_LOCATIONS \
+    PRODUCT_CHECK_ELF_FILES \
 
 define dump-product
 $(info ==== $(1) ====)\
diff --git a/core/product_config.mk b/core/product_config.mk
index c58405c..1f44692 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -253,6 +253,8 @@
 
 # A list of module names of BOOTCLASSPATH (jar files)
 PRODUCT_BOOT_JARS := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_BOOT_JARS))
+PRODUCT_UPDATABLE_BOOT_MODULES := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_UPDATABLE_BOOT_MODULES))
+PRODUCT_UPDATABLE_BOOT_LOCATIONS := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_UPDATABLE_BOOT_LOCATIONS))
 PRODUCT_SYSTEM_SERVER_JARS := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_SERVER_JARS))
 PRODUCT_SYSTEM_SERVER_APPS := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_SERVER_APPS))
 PRODUCT_DEXPREOPT_SPEED_APPS := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEXPREOPT_SPEED_APPS))
@@ -475,6 +477,11 @@
 PRODUCT_ENFORCE_RRO_TARGETS := \
     $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_ENFORCE_RRO_TARGETS))
 
+# Whether the product would like to check prebuilt ELF files.
+PRODUCT_CHECK_ELF_FILES := \
+    $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_CHECK_ELF_FILES))
+.KATI_READONLY := PRODUCT_CHECK_ELF_FILES
+
 # Add reserved headroom to a system image.
 PRODUCT_SYSTEM_HEADROOM := \
     $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_HEADROOM))
@@ -566,3 +573,27 @@
 PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES := \
     $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES))
 .KATI_READONLY := PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
+
+# Macro to use below. $(1) is the name of the partition
+define product-build-image-config
+PRODUCT_BUILD_$(1)_IMAGE := $$(firstword $$(strip $$(PRODUCTS.$$(INTERNAL_PRODUCT).PRODUCT_BUILD_$(1)_IMAGE)))
+.KATI_READONLY := PRODUCT_BUILD_$(1)_IMAGE
+ifneq ($$(filter-out true false,$$(PRODUCT_BUILD_$(1)_IMAGE)),)
+    $$(error Invalid PRODUCT_BUILD_$(1)_IMAGE: $$(PRODUCT_BUILD_$(1)_IMAGE) -- true false and empty are supported)
+endif
+endef
+
+# Copy and check the value of each PRODUCT_BUILD_*_IMAGE variable
+$(foreach image, \
+    SYSTEM \
+    SYSTEM_OTHER \
+    VENDOR \
+    PRODUCT \
+    PRODUCT_SERVICES \
+    ODM \
+    CACHE \
+    RAMDISK \
+    USERDATA, \
+  $(eval $(call product-build-image-config,$(image))))
+
+product-build-image-config :=
diff --git a/core/soong_app_prebuilt.mk b/core/soong_app_prebuilt.mk
index f723633..a6315b4 100644
--- a/core/soong_app_prebuilt.mk
+++ b/core/soong_app_prebuilt.mk
@@ -27,8 +27,40 @@
 full_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar
 full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar
 
-$(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_jar)))
-$(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_pre_proguard_jar)))
+ifdef LOCAL_SOONG_CLASSES_JAR
+  $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_jar)))
+  $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_pre_proguard_jar)))
+  $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(full_classes_jar)))
+
+  ifneq ($(TURBINE_ENABLED),false)
+    ifdef LOCAL_SOONG_HEADER_JAR
+      $(eval $(call copy-one-file,$(LOCAL_SOONG_HEADER_JAR),$(full_classes_header_jar)))
+    else
+      $(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_header_jar)))
+    endif
+  endif # TURBINE_ENABLED != false
+endif
+
+# Run veridex on product, product_services and vendor modules.
+# We skip it for unbundled app builds where we cannot build veridex.
+module_run_appcompat :=
+ifeq (true,$(non_system_module))
+ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))  # ! unbundled app build
+  module_run_appcompat := true
+endif
+endif
+
+ifeq ($(module_run_appcompat),true)
+  $(LOCAL_BUILT_MODULE): $(appcompat-files)
+  $(LOCAL_BUILT_MODULE): PRIVATE_INSTALLED_MODULE := $(LOCAL_INSTALLED_MODULE)
+  $(LOCAL_BUILT_MODULE): $(LOCAL_PREBUILT_MODULE_FILE)
+	@echo "Copy: $@"
+	$(copy-file-to-target)
+	$(call appcompat-header, aapt2)
+	$(run-appcompat)
+else
+  $(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE)))
+endif
 
 ifdef LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR
   $(eval $(call copy-one-file,$(LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR),\
@@ -44,15 +76,6 @@
     $(intermediates.COMMON)/proguard_dictionary)
 endif
 
-ifneq ($(TURBINE_ENABLED),false)
-ifdef LOCAL_SOONG_HEADER_JAR
-$(eval $(call copy-one-file,$(LOCAL_SOONG_HEADER_JAR),$(full_classes_header_jar)))
-else
-$(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_header_jar)))
-endif
-endif # TURBINE_ENABLED != false
-
-
 ifdef LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE
 resource_export_package := $(intermediates.COMMON)/package-export.apk
 resource_export_stamp := $(intermediates.COMMON)/src/R.stamp
@@ -74,8 +97,6 @@
   $(eval $(call copy-one-file,$(LOCAL_SOONG_DEX_JAR),$(dir $(LOCAL_BUILT_MODULE))package.dex.apk))
 endif
 
-$(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE)))
-
 my_built_installed := $(foreach f,$(LOCAL_SOONG_BUILT_INSTALLED),\
   $(call word-colon,1,$(f)):$(PRODUCT_OUT)$(call word-colon,2,$(f)))
 my_installed := $(call copy-many-files, $(my_built_installed))
diff --git a/core/soong_cc_prebuilt.mk b/core/soong_cc_prebuilt.mk
index 088b076..39cc0f0 100644
--- a/core/soong_cc_prebuilt.mk
+++ b/core/soong_cc_prebuilt.mk
@@ -93,6 +93,12 @@
   name_without_suffix :=
 endif
 
+# Check prebuilt ELF binaries.
+ifneq ($(LOCAL_CHECK_ELF_FILES),)
+my_prebuilt_src_file := $(LOCAL_PREBUILT_MODULE_FILE)
+include $(BUILD_SYSTEM)/check_elf_file.mk
+endif
+
 # The real dependency will be added after all Android.mks are loaded and the install paths
 # of the shared libraries are determined.
 ifdef LOCAL_INSTALLED_MODULE
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 2363f2b..cd8cd7f 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -123,6 +123,8 @@
 $(call add_json_bool, Treble_linker_namespaces,          $(filter true,$(PRODUCT_TREBLE_LINKER_NAMESPACES)))
 $(call add_json_bool, Enforce_vintf_manifest,            $(filter true,$(PRODUCT_ENFORCE_VINTF_MANIFEST)))
 
+$(call add_json_bool, Check_elf_files,                   $(filter true,$(PRODUCT_CHECK_ELF_FILES)))
+
 $(call add_json_bool, Uml,                               $(filter true,$(TARGET_USER_MODE_LINUX)))
 $(call add_json_bool, Use_lmkd_stats_log,                $(filter true,$(TARGET_LMKD_STATS_LOG)))
 $(call add_json_str,  VendorPath,                        $(TARGET_COPY_OUT_VENDOR))
@@ -152,6 +154,10 @@
 $(call add_json_bool, EnforceSystemCertificate,          $(ENFORCE_SYSTEM_CERTIFICATE))
 $(call add_json_list, EnforceSystemCertificateWhitelist, $(ENFORCE_SYSTEM_CERTIFICATE_WHITELIST))
 
+$(call add_json_str,  HiddenAPIStubFlags,                $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS))
+$(call add_json_str,  HiddenAPIFlags,                    $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
+$(call add_json_list, HiddenAPIExtraAppUsageJars,        $(HIDDENAPI_EXTRA_APP_USAGE_JARS))
+
 $(call add_json_map, VendorVars)
 $(foreach namespace,$(SOONG_CONFIG_NAMESPACES),\
   $(call add_json_map, $(namespace))\
diff --git a/core/soong_droiddoc_prebuilt.mk b/core/soong_droiddoc_prebuilt.mk
index 08df019..bf1f10b 100644
--- a/core/soong_droiddoc_prebuilt.mk
+++ b/core/soong_droiddoc_prebuilt.mk
@@ -16,8 +16,9 @@
 $(eval $(call copy-one-file,$(LOCAL_DROIDDOC_DOC_ZIP),$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip))
 $(call dist-for-goals,docs,$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip)
 
-.PHONY: $(LOCAL_MODULE)-docs.zip
-$(LOCAL_MODULE)-docs.zip : $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
+.PHONY: $(LOCAL_MODULE) $(LOCAL_MODULE)-docs.zip
+$(LOCAL_MODULE) $(LOCAL_MODULE)-docs.zip : $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
+ALL_DOCS += $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
 endif
 
 ifdef LOCAL_DROIDDOC_ANNOTATIONS_ZIP
@@ -34,6 +35,6 @@
 
 ALL_DOCS += $(OUT_DOCS)/$(LOCAL_MODULE)-jdiff-docs.zip
 
-.PHONY: $(LOCAL_MODULE)-jdiff
-$(LOCAL_MODULE)-jdiff : $(OUT_DOCS)/$(LOCAL_MODULE)-jdiff-docs.zip
+.PHONY: $(LOCAL_MODULE) $(LOCAL_MODULE)-jdiff
+$(LOCAL_MODULE) $(LOCAL_MODULE)-jdiff : $(OUT_DOCS)/$(LOCAL_MODULE)-jdiff-docs.zip
 endif
diff --git a/core/soong_java_prebuilt.mk b/core/soong_java_prebuilt.mk
index cfda44e..66410e1 100644
--- a/core/soong_java_prebuilt.mk
+++ b/core/soong_java_prebuilt.mk
@@ -24,8 +24,21 @@
 hiddenapi_flags_csv := $(intermediates.COMMON)/hiddenapi/flags.csv
 hiddenapi_metadata_csv := $(intermediates.COMMON)/hiddenapi/greylist.csv
 
-$(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_jar)))
-$(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_pre_proguard_jar)))
+ifdef LOCAL_SOONG_CLASSES_JAR
+  $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_jar)))
+  $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),$(full_classes_pre_proguard_jar)))
+  $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(full_classes_jar)))
+
+  ifneq ($(TURBINE_ENABLED),false)
+    ifdef LOCAL_SOONG_HEADER_JAR
+      $(eval $(call copy-one-file,$(LOCAL_SOONG_HEADER_JAR),$(full_classes_header_jar)))
+    else
+      $(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_header_jar)))
+    endif
+  endif # TURBINE_ENABLED != false
+endif
+
+$(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE)))
 
 ifdef LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR
   $(eval $(call copy-one-file,$(LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR),\
@@ -64,69 +77,37 @@
   $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_static_library_android_manifest))
 endif # LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE
 
-ifneq ($(TURBINE_ENABLED),false)
-ifdef LOCAL_SOONG_HEADER_JAR
-$(eval $(call copy-one-file,$(LOCAL_SOONG_HEADER_JAR),$(full_classes_header_jar)))
-else
-$(eval $(call copy-one-file,$(full_classes_jar),$(full_classes_header_jar)))
-endif
-endif # TURBINE_ENABLED != false
 
 ifdef LOCAL_SOONG_DEX_JAR
-  ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true)
-    ifndef LOCAL_IS_HOST_MODULE
-      ifneq ($(filter $(LOCAL_MODULE),$(PRODUCT_BOOT_JARS)),)  # is_boot_jar
-        # Derive greylist from classes.jar.
-        # We use full_classes_jar here, which is the post-proguard jar (on the basis that we also
-        # have a full_classes_pre_proguard_jar). This is consistent with the equivalent code in
-        # java.mk.
-        $(eval $(call hiddenapi-generate-csv,$(full_classes_jar),$(hiddenapi_flags_csv),$(hiddenapi_metadata_csv)))
-        $(eval $(call hiddenapi-copy-soong-jar,$(LOCAL_SOONG_DEX_JAR),$(common_javalib.jar)))
+  ifndef LOCAL_IS_HOST_MODULE
+    ifneq ($(filter $(LOCAL_MODULE),$(PRODUCT_BOOT_JARS)),)  # is_boot_jar
+      ifeq (true,$(WITH_DEXPREOPT))
+        # For libart, the boot jars' odex files are replaced by $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE).
+        # We use this installed_odex trick to get boot.art installed.
+        installed_odex := $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE)
+        # Append the odex for the 2nd arch if we have one.
+        installed_odex += $($(TARGET_2ND_ARCH_VAR_PREFIX)DEFAULT_DEX_PREOPT_INSTALLED_IMAGE)
+        ALL_MODULES.$(my_register_name).INSTALLED += $(installed_odex)
+        # Make sure to install the .odex and .vdex when you run "make <module_name>"
+       $(my_all_targets): $(installed_odex)
+      endif
+    endif # is_boot_jar
 
-        ifeq (true,$(WITH_DEXPREOPT))
-          # For libart, the boot jars' odex files are replaced by $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE).
-          # We use this installed_odex trick to get boot.art installed.
-          installed_odex := $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE)
-          # Append the odex for the 2nd arch if we have one.
-          installed_odex += $($(TARGET_2ND_ARCH_VAR_PREFIX)DEFAULT_DEX_PREOPT_INSTALLED_IMAGE)
-          ALL_MODULES.$(my_register_name).INSTALLED += $(installed_odex)
-          # Make sure to install the .odex and .vdex when you run "make <module_name>"
-         $(my_all_targets): $(installed_odex)
-        endif
-      else # !is_boot_jar
-        $(eval $(call copy-one-file,$(LOCAL_SOONG_DEX_JAR),$(common_javalib.jar)))
-      endif # is_boot_jar
-      $(eval $(call add-dependency,$(common_javalib.jar),$(full_classes_jar) $(full_classes_header_jar)))
-
-      $(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE)))
-      $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(common_javalib.jar)))
-    else # LOCAL_IS_HOST_MODULE
-      $(eval $(call copy-one-file,$(LOCAL_SOONG_DEX_JAR),$(LOCAL_BUILT_MODULE)))
-      $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(full_classes_jar) $(full_classes_header_jar)))
-    endif
-
-    java-dex : $(LOCAL_BUILT_MODULE)
-  else  # LOCAL_UNINSTALLABLE_MODULE
-
-    ifneq ($(filter $(LOCAL_MODULE),$(HIDDENAPI_EXTRA_APP_USAGE_JARS)),)
-      # Derive greylist from classes.jar.
-      # We use full_classes_jar here, which is the post-proguard jar (on the basis that we also
-      # have a full_classes_pre_proguard_jar). This is consistent with the equivalent code in
-      # java.mk.
-      $(eval $(call hiddenapi-generate-csv,$(full_classes_jar),$(hiddenapi_flags_csv),$(hiddenapi_metadata_csv)))
-    endif
-
-    $(eval $(call copy-one-file,$(full_classes_jar),$(LOCAL_BUILT_MODULE)))
     $(eval $(call copy-one-file,$(LOCAL_SOONG_DEX_JAR),$(common_javalib.jar)))
-    java-dex : $(common_javalib.jar)
-  endif  # LOCAL_UNINSTALLABLE_MODULE
+    $(eval $(call add-dependency,$(LOCAL_BUILT_MODULE),$(common_javalib.jar)))
+    $(eval $(call add-dependency,$(common_javalib.jar),$(full_classes_jar)))
+    ifneq ($(TURBINE_ENABLED),false)
+      $(eval $(call add-dependency,$(common_javalib.jar),$(full_classes_header_jar)))
+    endif
+  endif
+
+  java-dex : $(LOCAL_BUILT_MODULE)
 else  # LOCAL_SOONG_DEX_JAR
   ifndef LOCAL_UNINSTALLABLE_MODULE
     ifndef LOCAL_IS_HOST_MODULE
       $(call pretty-error,Installable device module must have LOCAL_SOONG_DEX_JAR set)
     endif
   endif
-  $(eval $(call copy-one-file,$(full_classes_jar),$(LOCAL_BUILT_MODULE)))
 endif  # LOCAL_SOONG_DEX_JAR
 
 my_built_installed := $(foreach f,$(LOCAL_SOONG_BUILT_INSTALLED),\
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index c4f9a52..a3cd3ac 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -252,7 +252,7 @@
     #  It must be of the form "YYYY-MM-DD" on production devices.
     #  It must match one of the Android Security Patch Level strings of the Public Security Bulletins.
     #  If there is no $PLATFORM_SECURITY_PATCH set, keep it empty.
-      PLATFORM_SECURITY_PATCH := 2018-12-05
+      PLATFORM_SECURITY_PATCH := 2019-01-05
 endif
 .KATI_READONLY := PLATFORM_SECURITY_PATCH
 
diff --git a/target/board/BoardConfigGsiCommon.mk b/target/board/BoardConfigGsiCommon.mk
index f9e9ee1..1037eec 100644
--- a/target/board/BoardConfigGsiCommon.mk
+++ b/target/board/BoardConfigGsiCommon.mk
@@ -25,8 +25,8 @@
 # Enable dynamic system image size and reserved 64MB in it.
 BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE := 67108864
 
-# GSI always requires separate vendor packages to vendor.img
-TARGET_COPY_OUT_VENDOR := vendor
+# GSI forces product packages to /system for now.
+TARGET_COPY_OUT_PRODUCT := system/product
 
 # Creates metadata partition mount point under root for
 # the devices with metadata parition
@@ -58,3 +58,6 @@
 # The partition size doesn't matter, just to make build pass.
 BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
 BOARD_CACHEIMAGE_PARTITION_SIZE := 16777216
+
+# Disable 64 bit mediadrmserver
+TARGET_ENABLE_MEDIADRM_64 :=
diff --git a/target/board/BoardConfigMainlineCommon.mk b/target/board/BoardConfigMainlineCommon.mk
index ec3c74f..9a4fb3a 100644
--- a/target/board/BoardConfigMainlineCommon.mk
+++ b/target/board/BoardConfigMainlineCommon.mk
@@ -8,6 +8,10 @@
 
 TARGET_USERIMAGES_USE_EXT4 := true
 
+# Mainline devices must have /vendor and /product partitions.
+TARGET_COPY_OUT_VENDOR := vendor
+TARGET_COPY_OUT_PRODUCT := product
+
 # system-as-root is mandatory from Android P
 TARGET_NO_RECOVERY := true
 BOARD_BUILD_SYSTEM_ROOT_IMAGE := true
@@ -17,6 +21,9 @@
 # Required flag for non-64 bit devices from P.
 TARGET_USES_64_BIT_BINDER := true
 
+# 64 bit mediadrmserver
+TARGET_ENABLE_MEDIADRM_64 := true
+
 # Puts odex files on system_other, as well as causing dex files not to get
 # stripped from APKs.
 BOARD_USES_SYSTEM_OTHER_ODEX := true
diff --git a/target/product/base.mk b/target/product/base.mk
index 1ecbf4a..804a2ee 100644
--- a/target/product/base.mk
+++ b/target/product/base.mk
@@ -18,3 +18,4 @@
 # up by partition.
 $(call inherit-product, $(SRC_TARGET_DIR)/product/base_system.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/base_vendor.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/base_product.mk)
diff --git a/target/product/base_product.mk b/target/product/base_product.mk
new file mode 100644
index 0000000..bd53fb1
--- /dev/null
+++ b/target/product/base_product.mk
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2019 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.
+#
+
+# Base modules and settings for the product partition.
+PRODUCT_PACKAGES += \
+    healthd \
+    ModuleMetadata \
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index ee4a695..d6c8421 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -208,7 +208,6 @@
     MediaProvider \
     mediaserver \
     mke2fs \
-    ModuleMetadata \
     monkey \
     mtpd \
     ndc \
@@ -286,15 +285,10 @@
     telephony-common \
     voip-common \
     ims-common
+PRODUCT_UPDATABLE_BOOT_MODULES := conscrypt
+PRODUCT_UPDATABLE_BOOT_LOCATIONS := \
+    /apex/com.android.conscrypt/javalib/conscrypt.jar
 
-# Add the compatibility library that is needed when org.apache.http.legacy
-# is removed from the bootclasspath.
-ifeq ($(REMOVE_OAHL_FROM_BCP),true)
-PRODUCT_PACKAGES += framework-oahl-backward-compatibility
-PRODUCT_BOOT_JARS += framework-oahl-backward-compatibility
-else
-PRODUCT_BOOT_JARS += org.apache.http.legacy.impl
-endif
 
 PRODUCT_COPY_FILES += \
     system/core/rootdir/init.usb.rc:root/init.usb.rc \
@@ -308,7 +302,7 @@
 PRODUCT_PACKAGES += framework-atb-backward-compatibility
 PRODUCT_BOOT_JARS += framework-atb-backward-compatibility
 else
-PRODUCT_BOOT_JARS += android.test.base.impl
+PRODUCT_BOOT_JARS += android.test.base
 endif
 
 PRODUCT_COPY_FILES += system/core/rootdir/init.zygote32.rc:root/init.zygote32.rc
@@ -320,14 +314,18 @@
 PRODUCT_PACKAGES_DEBUG := \
     adb_keys \
     arping \
+    gdbserver \
+    init-debug.rc \
     iotop \
     iw \
     logpersist.start \
+    logtagd.rc \
     procrank \
     showmap \
     sqlite3 \
     ss \
     strace \
+    su \
     sanitizer-status \
     tracepath \
     tracepath6 \
diff --git a/target/product/base_vendor.mk b/target/product/base_vendor.mk
index 1bb4bee..9bb45d1 100644
--- a/target/product/base_vendor.mk
+++ b/target/product/base_vendor.mk
@@ -53,10 +53,6 @@
     vndservice \
     vndservicemanager \
 
-# Base modules and settings for the product partition.
-PRODUCT_PACKAGES += \
-    healthd \
-
 # VINTF data for vendor image
 PRODUCT_PACKAGES += \
     device_manifest.xml \
diff --git a/target/product/core_minimal.mk b/target/product/core_minimal.mk
index b432a91..9718dc6 100644
--- a/target/product/core_minimal.mk
+++ b/target/product/core_minimal.mk
@@ -18,11 +18,12 @@
 # means most android products, but excludes wearables.
 #
 # Note: Do not add any contents directly to this file. Choose either
-# media_system or media_vendor depending on partition (also consider
-# base_<x>.mk or handheld_<x>.mk.
+# media_<x> depending on partition also consider base_<x>.mk or
+# handheld_<x>.mk.
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/media_system.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/media_vendor.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/media_product.mk)
 
 PRODUCT_BRAND := generic
 PRODUCT_DEVICE := generic
diff --git a/target/product/full_base_telephony.mk b/target/product/full_base_telephony.mk
index ee59090..af4097d 100644
--- a/target/product/full_base_telephony.mk
+++ b/target/product/full_base_telephony.mk
@@ -28,5 +28,4 @@
     frameworks/native/data/etc/handheld_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/handheld_core_hardware.xml
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_vendor.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony.mk)
diff --git a/target/product/generic.mk b/target/product/generic.mk
index 7a9732d..cc856f4 100644
--- a/target/product/generic.mk
+++ b/target/product/generic.mk
@@ -18,8 +18,7 @@
 # It includes the base Android platform.
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/generic_no_telephony.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_vendor.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony.mk)
 
 # Overrides
 PRODUCT_BRAND := generic
diff --git a/target/product/generic_no_telephony.mk b/target/product/generic_no_telephony.mk
index 5346476..324d36f 100644
--- a/target/product/generic_no_telephony.mk
+++ b/target/product/generic_no_telephony.mk
@@ -22,6 +22,7 @@
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_vendor.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_product.mk)
 
 PRODUCT_BRAND := generic
 PRODUCT_DEVICE := generic
diff --git a/target/product/handheld_product.mk b/target/product/handheld_product.mk
new file mode 100644
index 0000000..063b951
--- /dev/null
+++ b/target/product/handheld_product.mk
@@ -0,0 +1,43 @@
+#
+# Copyright (C) 2019 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 makefile contains the product partition contents for
+# a generic phone or tablet device. Only add something here if
+# it definitely doesn't belong on other types of devices (if it
+# does, use base_vendor.mk).
+$(call inherit-product, $(SRC_TARGET_DIR)/product/media_product.mk)
+
+# /product packages
+PRODUCT_PACKAGES += \
+    Browser2 \
+    Calendar \
+    Camera2 \
+    Contacts \
+    DeskClock \
+    Email \
+    Gallery2 \
+    LatinIME \
+    Launcher3QuickStep \
+    Music \
+    OneTimeInitializer \
+    PrintRecommendationService \
+    Provision \
+    QuickSearchBox \
+    Settings \
+    SettingsIntelligence \
+    StorageManager \
+    SystemUI \
+    WallpaperCropper \
diff --git a/target/product/handheld_vendor.mk b/target/product/handheld_vendor.mk
index b9970e9..ca7760a 100644
--- a/target/product/handheld_vendor.mk
+++ b/target/product/handheld_vendor.mk
@@ -30,25 +30,3 @@
     power.default \
     SysuiDarkThemeOverlay \
     vibrator.default \
-
-# /product packages
-PRODUCT_PACKAGES += \
-    Browser2 \
-    Calendar \
-    Camera2 \
-    Contacts \
-    DeskClock \
-    Email \
-    Gallery2 \
-    LatinIME \
-    Launcher3QuickStep \
-    Music \
-    OneTimeInitializer \
-    PrintRecommendationService \
-    Provision \
-    QuickSearchBox \
-    Settings \
-    SettingsIntelligence \
-    StorageManager \
-    SystemUI \
-    WallpaperCropper \
diff --git a/target/product/mainline.mk b/target/product/mainline.mk
index 44dcd60..59bad98 100644
--- a/target/product/mainline.mk
+++ b/target/product/mainline.mk
@@ -18,4 +18,6 @@
 # for the other partitions.
 $(call inherit-product, $(SRC_TARGET_DIR)/product/mainline_system.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_vendor.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_product.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_vendor.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_product.mk)
diff --git a/target/product/mainline_arm64.mk b/target/product/mainline_arm64.mk
index 6122ac1..c876636 100644
--- a/target/product/mainline_arm64.mk
+++ b/target/product/mainline_arm64.mk
@@ -37,6 +37,8 @@
   system/app/Calendar/Calendar.apk \
   system/app/Camera2/Camera2.apk \
   system/app/DeskClock/DeskClock.apk \
+  system/app/DeskClock/oat/arm64/DeskClock.odex \
+  system/app/DeskClock/oat/arm64/DeskClock.vdex \
   system/app/Email/Email.apk \
   system/app/Gallery2/Gallery2.apk \
   system/app/LatinIME/LatinIME.apk \
@@ -45,6 +47,7 @@
   system/app/Music/Music.apk \
   system/app/PrintRecommendationService/PrintRecommendationService.apk \
   system/app/QuickSearchBox/QuickSearchBox.apk \
+  system/app/webview/webview.apk \
   system/bin/healthd \
   system/etc/init/healthd.rc \
   system/etc/vintf/manifest/manifest_healthd.xml \
@@ -55,6 +58,8 @@
   system/lib64/libjni_latinime.so \
   system/lib64/libjni_tinyplanet.so \
   system/priv-app/CarrierConfig/CarrierConfig.apk \
+  system/priv-app/CarrierConfig/oat/arm64/CarrierConfig.odex \
+  system/priv-app/CarrierConfig/oat/arm64/CarrierConfig.vdex \
   system/priv-app/Contacts/Contacts.apk \
   system/priv-app/Dialer/Dialer.apk \
   system/priv-app/Launcher3QuickStep/Launcher3QuickStep.apk \
diff --git a/target/product/media_product.mk b/target/product/media_product.mk
new file mode 100644
index 0000000..17c24ee
--- /dev/null
+++ b/target/product/media_product.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2019 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 makefile contains the product partition contents for
+# media-capable devices (non-wearables). Only add something here
+# if it definitely doesn't belong on wearables. Otherwise, choose
+# base_vendor.mk.
+$(call inherit-product, $(SRC_TARGET_DIR)/product/base_product.mk)
+
+# /product packages
+PRODUCT_PACKAGES += \
+    webview \
diff --git a/target/product/media_system.mk b/target/product/media_system.mk
index 992e2ed..65ee073 100644
--- a/target/product/media_system.mk
+++ b/target/product/media_system.mk
@@ -59,7 +59,6 @@
     requestsync \
     StatementService \
     vndk_snapshot_package \
-    webview \
 
 
 PRODUCT_COPY_FILES += \
@@ -75,7 +74,7 @@
     services \
     ethernet-service \
     wifi-service \
-    com.android.location.provider.impl \
+    com.android.location.provider \
 
 PRODUCT_COPY_FILES += \
     system/core/rootdir/etc/public.libraries.android.txt:system/etc/public.libraries.txt
diff --git a/target/product/media_vendor.mk b/target/product/media_vendor.mk
index f30e6f3..7d4af64 100644
--- a/target/product/media_vendor.mk
+++ b/target/product/media_vendor.mk
@@ -24,7 +24,3 @@
 PRODUCT_PACKAGES += \
     libaudiopreprocessing \
     libwebrtc_audio_preprocessing \
-
-# /product packages
-PRODUCT_PACKAGES += \
-    webview \
diff --git a/target/product/telephony.mk b/target/product/telephony.mk
new file mode 100644
index 0000000..e0eb159
--- /dev/null
+++ b/target/product/telephony.mk
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2019 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.
+#
+
+# All modules for telephony
+$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_vendor.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_product.mk)
diff --git a/target/product/telephony_product.mk b/target/product/telephony_product.mk
new file mode 100644
index 0000000..70d4828
--- /dev/null
+++ b/target/product/telephony_product.mk
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2019 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 the list of modules that are specific to products that have telephony
+# hardware, and install to the product partition.
+
+# /product packages
+PRODUCT_PACKAGES += \
+    CarrierConfig \
+    Dialer \
diff --git a/target/product/telephony_vendor.mk b/target/product/telephony_vendor.mk
index 4cff16d..86dbcc9 100644
--- a/target/product/telephony_vendor.mk
+++ b/target/product/telephony_vendor.mk
@@ -21,9 +21,4 @@
 PRODUCT_PACKAGES := \
     rild \
 
-# /product packages
-PRODUCT_PACKAGES += \
-    CarrierConfig \
-    Dialer \
-
 PRODUCT_COPY_FILES := \
diff --git a/target/product/treble_common.mk b/target/product/treble_common.mk
index 7642876..7e4a98e 100644
--- a/target/product/treble_common.mk
+++ b/target/product/treble_common.mk
@@ -21,8 +21,7 @@
 
 # Generic system image inherits from AOSP with telephony
 $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_vendor.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony.mk)
 
 # Enable dynamic partition size
 PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true
diff --git a/tools/check_elf_file.py b/tools/check_elf_file.py
new file mode 100755
index 0000000..38c1cf4
--- /dev/null
+++ b/tools/check_elf_file.py
@@ -0,0 +1,538 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2019 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.
+
+"""ELF file checker.
+
+This command ensures all undefined symbols in an ELF file can be resolved to
+global (or weak) symbols defined in shared objects specified in DT_NEEDED
+entries.
+"""
+
+from __future__ import print_function
+
+import argparse
+import collections
+import os
+import os.path
+import re
+import struct
+import subprocess
+import sys
+
+
+_ELF_MAGIC = b'\x7fELF'
+
+
+# Known machines
+_EM_386 = 3
+_EM_ARM = 40
+_EM_X86_64 = 62
+_EM_AARCH64 = 183
+
+_KNOWN_MACHINES = {_EM_386, _EM_ARM, _EM_X86_64, _EM_AARCH64}
+
+
+# ELF header struct
+_ELF_HEADER_STRUCT = (
+  ('ei_magic', '4s'),
+  ('ei_class', 'B'),
+  ('ei_data', 'B'),
+  ('ei_version', 'B'),
+  ('ei_osabi', 'B'),
+  ('ei_pad', '8s'),
+  ('e_type', 'H'),
+  ('e_machine', 'H'),
+  ('e_version', 'I'),
+)
+
+_ELF_HEADER_STRUCT_FMT = ''.join(_fmt for _, _fmt in _ELF_HEADER_STRUCT)
+
+
+ELFHeader = collections.namedtuple(
+  'ELFHeader', [_name for _name, _ in _ELF_HEADER_STRUCT])
+
+
+ELF = collections.namedtuple(
+  'ELF',
+  ('dt_soname', 'dt_needed', 'imported', 'exported', 'header'))
+
+
+def _get_os_name():
+  """Get the host OS name."""
+  if sys.platform == 'linux2':
+    return 'linux'
+  if sys.platform == 'darwin':
+    return 'darwin'
+  raise ValueError(sys.platform + ' is not supported')
+
+
+def _get_build_top():
+  """Find the build top of the source tree ($ANDROID_BUILD_TOP)."""
+  prev_path = None
+  curr_path = os.path.abspath(os.getcwd())
+  while prev_path != curr_path:
+    if os.path.exists(os.path.join(curr_path, '.repo')):
+      return curr_path
+    prev_path = curr_path
+    curr_path = os.path.dirname(curr_path)
+  return None
+
+
+def _select_latest_llvm_version(versions):
+  """Select the latest LLVM prebuilts version from a set of versions."""
+  pattern = re.compile('clang-r([0-9]+)([a-z]?)')
+  found_rev = 0
+  found_ver = None
+  for curr_ver in versions:
+    match = pattern.match(curr_ver)
+    if not match:
+      continue
+    curr_rev = int(match.group(1))
+    if not found_ver or curr_rev > found_rev or (
+        curr_rev == found_rev and curr_ver > found_ver):
+      found_rev = curr_rev
+      found_ver = curr_ver
+  return found_ver
+
+
+def _get_latest_llvm_version(llvm_dir):
+  """Find the latest LLVM prebuilts version from `llvm_dir`."""
+  return _select_latest_llvm_version(os.listdir(llvm_dir))
+
+
+def _get_llvm_dir():
+  """Find the path to LLVM prebuilts."""
+  build_top = _get_build_top()
+
+  llvm_prebuilts_base = os.environ.get('LLVM_PREBUILTS_BASE')
+  if not llvm_prebuilts_base:
+    llvm_prebuilts_base = os.path.join('prebuilts', 'clang', 'host')
+
+  llvm_dir = os.path.join(
+    build_top, llvm_prebuilts_base, _get_os_name() + '-x86')
+
+  if not os.path.exists(llvm_dir):
+    return None
+
+  llvm_prebuilts_version = os.environ.get('LLVM_PREBUILTS_VERSION')
+  if not llvm_prebuilts_version:
+    llvm_prebuilts_version = _get_latest_llvm_version(llvm_dir)
+
+  llvm_dir = os.path.join(llvm_dir, llvm_prebuilts_version)
+
+  if not os.path.exists(llvm_dir):
+    return None
+
+  return llvm_dir
+
+
+def _get_llvm_readobj():
+  """Find the path to llvm-readobj executable."""
+  llvm_dir = _get_llvm_dir()
+  llvm_readobj = os.path.join(llvm_dir, 'bin', 'llvm-readobj')
+  return llvm_readobj if os.path.exists(llvm_readobj) else 'llvm-readobj'
+
+
+class ELFError(ValueError):
+  """Generic ELF parse error"""
+  pass
+
+
+class ELFInvalidMagicError(ELFError):
+  """Invalid ELF magic word error"""
+  def __init__(self):
+    super(ELFInvalidMagicError, self).__init__('bad ELF magic')
+
+
+class ELFParser(object):
+  """ELF file parser"""
+
+  @classmethod
+  def _read_elf_header(cls, elf_file_path):
+    """Read the ELF magic word from the beginning of the file."""
+    with open(elf_file_path, 'rb') as elf_file:
+      buf = elf_file.read(struct.calcsize(_ELF_HEADER_STRUCT_FMT))
+      try:
+        return ELFHeader(*struct.unpack(_ELF_HEADER_STRUCT_FMT, buf))
+      except struct.error:
+        return None
+
+
+  @classmethod
+  def open(cls, elf_file_path, llvm_readobj):
+    """Open and parse the ELF file."""
+    # Parse the ELF header for simple sanity checks.
+    header = cls._read_elf_header(elf_file_path)
+    if not header or header.ei_magic != _ELF_MAGIC:
+      raise ELFInvalidMagicError()
+
+    # Run llvm-readobj and parse the output.
+    return cls._read_llvm_readobj(elf_file_path, header, llvm_readobj)
+
+
+  @classmethod
+  def _find_prefix(cls, pattern, lines_it):
+    """Iterate `lines_it` until finding a string that starts with `pattern`."""
+    for line in lines_it:
+      if line.startswith(pattern):
+        return True
+    return False
+
+
+  @classmethod
+  def _read_llvm_readobj(cls, elf_file_path, header, llvm_readobj):
+    """Run llvm-readobj and parse the output."""
+    proc = subprocess.Popen(
+      [llvm_readobj, '-dynamic-table', '-dyn-symbols', elf_file_path],
+      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    out, _ = proc.communicate()
+    lines = out.splitlines()
+    return cls._parse_llvm_readobj(elf_file_path, header, lines)
+
+
+  @classmethod
+  def _parse_llvm_readobj(cls, elf_file_path, header, lines):
+    """Parse the output of llvm-readobj."""
+    lines_it = iter(lines)
+    imported, exported = cls._parse_dynamic_symbols(lines_it)
+    dt_soname, dt_needed = cls._parse_dynamic_table(elf_file_path, lines_it)
+    return ELF(dt_soname, dt_needed, imported, exported, header)
+
+
+  _DYNAMIC_SECTION_START_PATTERN = 'DynamicSection ['
+
+  _DYNAMIC_SECTION_NEEDED_PATTERN = re.compile(
+    '^  0x[0-9a-fA-F]+\\s+NEEDED\\s+Shared library: \\[(.*)\\]$')
+
+  _DYNAMIC_SECTION_SONAME_PATTERN = re.compile(
+    '^  0x[0-9a-fA-F]+\\s+SONAME\\s+Library soname: \\[(.*)\\]$')
+
+  _DYNAMIC_SECTION_END_PATTERN = ']'
+
+
+  @classmethod
+  def _parse_dynamic_table(cls, elf_file_path, lines_it):
+    """Parse the dynamic table section."""
+    dt_soname = os.path.basename(elf_file_path)
+    dt_needed = []
+
+    dynamic = cls._find_prefix(cls._DYNAMIC_SECTION_START_PATTERN, lines_it)
+    if not dynamic:
+      return (dt_soname, dt_needed)
+
+    for line in lines_it:
+      if line == cls._DYNAMIC_SECTION_END_PATTERN:
+        break
+
+      match = cls._DYNAMIC_SECTION_NEEDED_PATTERN.match(line)
+      if match:
+        dt_needed.append(match.group(1))
+        continue
+
+      match = cls._DYNAMIC_SECTION_SONAME_PATTERN.match(line)
+      if match:
+        dt_soname = match.group(1)
+        continue
+
+    return (dt_soname, dt_needed)
+
+
+  _DYNAMIC_SYMBOLS_START_PATTERN = 'DynamicSymbols ['
+  _DYNAMIC_SYMBOLS_END_PATTERN = ']'
+
+  _SYMBOL_ENTRY_START_PATTERN = '  Symbol {'
+  _SYMBOL_ENTRY_PATTERN = re.compile('^    ([A-Za-z0-9_]+): (.*)$')
+  _SYMBOL_ENTRY_PAREN_PATTERN = re.compile(
+    '\\s+\\((?:(?:\\d+)|(?:0x[0-9a-fA-F]+))\\)$')
+  _SYMBOL_ENTRY_END_PATTERN = '  }'
+
+
+  @classmethod
+  def _parse_symbol_name(cls, name_with_version):
+    """Split `name_with_version` into name and version. This function may split
+    at last occurrence of `@@` or `@`."""
+    name, version = name_with_version.rsplit('@', 1)
+    if name and name[-1] == '@':
+      name = name[:-1]
+    return (name, version)
+
+
+  @classmethod
+  def _parse_dynamic_symbols(cls, lines_it):
+    """Parse dynamic symbol table and collect imported and exported symbols."""
+    imported = collections.defaultdict(set)
+    exported = collections.defaultdict(set)
+
+    for symbol in cls._parse_dynamic_symbols_internal(lines_it):
+      name, version = cls._parse_symbol_name(symbol['Name'])
+      if name:
+        if symbol['Section'] == 'Undefined':
+          if symbol['Binding'] != 'Weak':
+            imported[name].add(version)
+        else:
+          if symbol['Binding'] != 'Local':
+            exported[name].add(version)
+
+    # Freeze the returned imported/exported dict.
+    return (dict(imported), dict(exported))
+
+
+  @classmethod
+  def _parse_dynamic_symbols_internal(cls, lines_it):
+    """Parse symbols entries and yield each symbols."""
+
+    if not cls._find_prefix(cls._DYNAMIC_SYMBOLS_START_PATTERN, lines_it):
+      return
+
+    for line in lines_it:
+      if line == cls._DYNAMIC_SYMBOLS_END_PATTERN:
+        return
+
+      if line == cls._SYMBOL_ENTRY_START_PATTERN:
+        symbol = {}
+        continue
+
+      if line == cls._SYMBOL_ENTRY_END_PATTERN:
+        yield symbol
+        symbol = None
+        continue
+
+      match = cls._SYMBOL_ENTRY_PATTERN.match(line)
+      if match:
+        key = match.group(1)
+        value = cls._SYMBOL_ENTRY_PAREN_PATTERN.sub('', match.group(2))
+        symbol[key] = value
+        continue
+
+
+class Checker(object):
+  """ELF file checker that checks DT_SONAME, DT_NEEDED, and symbols."""
+
+  def __init__(self, llvm_readobj):
+    self._file_path = ''
+    self._file_under_test = None
+    self._shared_libs = []
+
+    self._llvm_readobj = llvm_readobj
+
+
+  if sys.stderr.isatty():
+    _ERROR_TAG = '\033[0;1;31merror:\033[m'  # Red error
+    _NOTE_TAG = '\033[0;1;30mnote:\033[m'  # Black note
+  else:
+    _ERROR_TAG = 'error:'  # Red error
+    _NOTE_TAG = 'note:'  # Black note
+
+
+  def _error(self, *args):
+    """Emit an error to stderr."""
+    print(self._file_path + ': ' + self._ERROR_TAG, *args, file=sys.stderr)
+
+
+  def _note(self, *args):
+    """Emit a note to stderr."""
+    print(self._file_path + ': ' + self._NOTE_TAG, *args, file=sys.stderr)
+
+
+  def _load_elf_file(self, path, skip_bad_elf_magic):
+    """Load an ELF file from the `path`."""
+    try:
+      return ELFParser.open(path, self._llvm_readobj)
+    except (IOError, OSError):
+      self._error('Failed to open "{}".'.format(path))
+      sys.exit(2)
+    except ELFInvalidMagicError:
+      if skip_bad_elf_magic:
+        sys.exit(0)
+      else:
+        self._error('File "{}" must have a valid ELF magic word.'.format(path))
+        sys.exit(2)
+    except:
+      self._error('An unknown error occurred while opening "{}".'.format(path))
+      raise
+
+
+  def load_file_under_test(self, path, skip_bad_elf_magic,
+                           skip_unknown_elf_machine):
+    """Load file-under-test (either an executable or a shared lib)."""
+    self._file_path = path
+    self._file_under_test = self._load_elf_file(path, skip_bad_elf_magic)
+
+    if skip_unknown_elf_machine and \
+        self._file_under_test.header.e_machine not in _KNOWN_MACHINES:
+      sys.exit(0)
+
+
+  def load_shared_libs(self, shared_lib_paths):
+    """Load shared libraries."""
+    for path in shared_lib_paths:
+      self._shared_libs.append(self._load_elf_file(path, False))
+
+
+  def check_dt_soname(self, soname):
+    """Check whether DT_SONAME matches installation file name."""
+    if self._file_under_test.dt_soname != soname:
+      self._error('DT_SONAME "{}" must be equal to the file name "{}".'
+                  .format(self._file_under_test.dt_soname, soname))
+      sys.exit(2)
+
+
+  def check_dt_needed(self):
+    """Check whether all DT_NEEDED entries are specified in the build
+    system."""
+
+    missing_shared_libs = False
+
+    # Collect the DT_SONAMEs from shared libs specified in the build system.
+    specified_sonames = {lib.dt_soname for lib in self._shared_libs}
+
+    # Chech whether all DT_NEEDED entries are specified.
+    for lib in self._file_under_test.dt_needed:
+      if lib not in specified_sonames:
+        self._error('DT_NEEDED "{}" is not specified in shared_libs.'
+                    .format(lib.decode('utf-8')))
+        missing_shared_libs = True
+
+    if missing_shared_libs:
+      dt_needed = sorted(set(self._file_under_test.dt_needed))
+      modules = [re.sub('\\.so$', '', lib) for lib in dt_needed]
+
+      self._note()
+      self._note('Fix suggestions:')
+      self._note(
+        '  Android.bp: shared_libs: [' +
+        ', '.join('"' + module + '"' for module in modules) + '],')
+      self._note(
+        '  Android.mk: LOCAL_SHARED_LIBRARIES := ' + ' '.join(modules))
+
+      self._note()
+      self._note('If the fix above doesn\'t work, bypass this check with:')
+      self._note('  Android.bp: check_elf_files: false,')
+      self._note('  Android.mk: LOCAL_CHECK_ELF_FILES := false')
+
+      sys.exit(2)
+
+
+  @staticmethod
+  def _find_symbol(lib, name, version):
+    """Check whether the symbol name and version matches a definition in
+    lib."""
+    try:
+      lib_sym_vers = lib.exported[name]
+    except KeyError:
+      return False
+    if version == '':  # Symbol version is not requested
+      return True
+    return version in lib_sym_vers
+
+
+  @classmethod
+  def _find_symbol_from_libs(cls, libs, name, version):
+    """Check whether the symbol name and version is defined in one of the
+    shared libraries in libs."""
+    for lib in libs:
+      if cls._find_symbol(lib, name, version):
+        return lib
+    return None
+
+
+  def check_symbols(self):
+    """Check whether all undefined symbols are resolved to a definition."""
+    all_elf_files = [self._file_under_test] + self._shared_libs
+    missing_symbols = []
+    for sym, imported_vers in self._file_under_test.imported.iteritems():
+      for imported_ver in imported_vers:
+        lib = self._find_symbol_from_libs(all_elf_files, sym, imported_ver)
+        if not lib:
+          missing_symbols.append((sym, imported_ver))
+
+    if missing_symbols:
+      for sym, ver in sorted(missing_symbols):
+        sym = sym.decode('utf-8')
+        if ver:
+          sym += '@' + ver.decode('utf-8')
+        self._error('Unresolved symbol: {}'.format(sym))
+
+      self._note()
+      self._note('Some dependencies might be changed, thus the symbol(s) '
+                 'above cannot be resolved.')
+      self._note('Please re-build the prebuilt file: "{}".'
+                 .format(self._file_path))
+
+      self._note()
+      self._note('If this is a new prebuilt file and it is designed to have '
+                 'unresolved symbols, add one of the following properties:')
+      self._note('  Android.bp: allow_undefined_symbols: true,')
+      self._note('  Android.mk: LOCAL_ALLOW_UNDEFINED_SYMBOLS := true')
+
+      sys.exit(2)
+
+
+def _parse_args():
+  """Parse command line options."""
+  parser = argparse.ArgumentParser()
+
+  # Input file
+  parser.add_argument('file',
+                      help='Path to the input file to be checked')
+  parser.add_argument('--soname',
+                      help='Shared object name of the input file')
+
+  # Shared library dependencies
+  parser.add_argument('--shared-lib', action='append', default=[],
+                      help='Path to shared library dependencies')
+
+  # Check options
+  parser.add_argument('--skip-bad-elf-magic', action='store_true',
+                      help='Ignore the input file without the ELF magic word')
+  parser.add_argument('--skip-unknown-elf-machine', action='store_true',
+                      help='Ignore the input file with unknown machine ID')
+  parser.add_argument('--allow-undefined-symbols', action='store_true',
+                      help='Ignore unresolved undefined symbols')
+
+  # Other options
+  parser.add_argument('--llvm-readobj',
+                      help='Path to the llvm-readobj executable')
+
+  return parser.parse_args()
+
+
+def main():
+  """Main function"""
+  args = _parse_args()
+
+  llvm_readobj = args.llvm_readobj
+  if not llvm_readobj:
+    llvm_readobj = _get_llvm_readobj()
+
+  # Load ELF files
+  checker = Checker(llvm_readobj)
+  checker.load_file_under_test(
+    args.file, args.skip_bad_elf_magic, args.skip_unknown_elf_machine)
+  checker.load_shared_libs(args.shared_lib)
+
+  # Run checks
+  if args.soname:
+    checker.check_dt_soname(args.soname)
+
+  checker.check_dt_needed()
+
+  if not args.allow_undefined_symbols:
+    checker.check_symbols()
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/releasetools/blockimgdiff.py b/tools/releasetools/blockimgdiff.py
index 80d4023..456f791 100644
--- a/tools/releasetools/blockimgdiff.py
+++ b/tools/releasetools/blockimgdiff.py
@@ -483,7 +483,8 @@
       # choose the transfers for conversion. The number '1024' can be further
       # tweaked here to balance the package size and build time.
       if max_stashed_blocks > stash_limit + 1024:
-        self.SelectAndConvertDiffTransfersToNew()
+        self.SelectAndConvertDiffTransfersToNew(
+            max_stashed_blocks - stash_limit)
         # Regenerate the sequence as the graph has changed.
         self.FindSequenceForTransfers()
 
@@ -830,7 +831,8 @@
     num_of_bytes = new_blocks * self.tgt.blocksize
     logger.info(
         "  Total %d blocks (%d bytes) are packed as new blocks due to "
-        "insufficient cache size.", new_blocks, num_of_bytes)
+        "insufficient cache size. Maximum blocks stashed simultaneously: %d",
+        new_blocks, num_of_bytes, max_stashed_blocks)
     return new_blocks, max_stashed_blocks
 
   def ComputePatches(self, prefix):
@@ -1339,7 +1341,7 @@
 
     return patches
 
-  def SelectAndConvertDiffTransfersToNew(self):
+  def SelectAndConvertDiffTransfersToNew(self, violated_stash_blocks):
     """Converts the diff transfers to reduce the max simultaneous stash.
 
     Since the 'new' data is compressed with deflate, we can select the 'diff'
@@ -1347,6 +1349,8 @@
     compressed data. Ideally, we want to convert the transfers with a small
     size increase, but using a large number of stashed blocks.
     """
+    TransferSizeScore = namedtuple("TransferSizeScore",
+                                   "xf, used_stash_blocks, score")
 
     logger.info("Selecting diff commands to convert to new.")
     diff_queue = []
@@ -1360,7 +1364,7 @@
     # for the remaining.
     result = self.ComputePatchesForInputList(diff_queue, True)
 
-    removed_stashed_blocks = 0
+    conversion_candidates = []
     for xf_index, patch_info, compressed_size in result:
       xf = self.transfers[xf_index]
       if not xf.patch_info:
@@ -1368,21 +1372,43 @@
 
       size_ratio = len(xf.patch_info.content) * 100.0 / compressed_size
       diff_style = "imgdiff" if xf.patch_info.imgdiff else "bsdiff"
-      logger.info("%s, target size: %d, style: %s, patch size: %d,"
+      logger.info("%s, target size: %d blocks, style: %s, patch size: %d,"
                   " compression_size: %d, ratio %.2f%%", xf.tgt_name,
                   xf.tgt_ranges.size(), diff_style,
                   len(xf.patch_info.content), compressed_size, size_ratio)
 
+      used_stash_blocks = sum(sr.size() for _, sr in xf.use_stash)
       # Convert the transfer to new if the compressed size is smaller or equal.
       # We don't need to maintain the stash_before lists here because the
       # graph will be regenerated later.
       if len(xf.patch_info.content) >= compressed_size:
-        removed_stashed_blocks += sum(sr.size() for _, sr in xf.use_stash)
-        logger.info("Converting %s to new", xf.tgt_name)
-        xf.ConvertToNew()
+        # Add the transfer to the candidate list with negative score. And it
+        # will be converted later.
+        conversion_candidates.append(TransferSizeScore(xf, used_stash_blocks,
+                                                       -1))
+      elif used_stash_blocks > 0:
+        # This heuristic represents the size increase in the final package to
+        # remove per unit of stashed data.
+        score = ((compressed_size - len(xf.patch_info.content)) * 100.0
+                 / used_stash_blocks)
+        conversion_candidates.append(TransferSizeScore(xf, used_stash_blocks,
+                                                       score))
+    # Transfers with lower score (i.e. less expensive to convert) will be
+    # converted first.
+    conversion_candidates.sort(key=lambda x: x.score)
 
-    # TODO(xunchang) convert more transfers by sorting:
-    # (compressed size - patch_size) / used_stashed_blocks
+    # TODO(xunchang), improve the logic to find the transfers to convert, e.g.
+    # convert the ones that contribute to the max stash, run ReviseStashSize
+    # multiple times etc.
+    removed_stashed_blocks = 0
+    for xf, used_stash_blocks, _ in conversion_candidates:
+      logger.info("Converting %s to new", xf.tgt_name)
+      xf.ConvertToNew()
+      removed_stashed_blocks += used_stash_blocks
+      # Experiments show that we will get a smaller package size if we remove
+      # slightly more stashed blocks than the violated stash blocks.
+      if removed_stashed_blocks >= violated_stash_blocks:
+        break
 
     logger.info("Removed %d stashed blocks", removed_stashed_blocks)
 
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 6aaff6b..ba1d60e 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -76,7 +76,12 @@
   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
+  inodes = output.count('\n')
+  spare_inodes = inodes * 4 // 100
+  min_spare_inodes = 12
+  if spare_inodes < min_spare_inodes:
+    spare_inodes = min_spare_inodes
+  return inodes + spare_inodes
 
 
 def GetFilesystemCharacteristics(image_path, sparse_image=True):
@@ -436,8 +441,8 @@
         size -= free_size
         size += reserved_size
         if reserved_size == 0:
-          # add .2% margin
-          size = size * 1002 // 1000
+          # add .3% margin
+          size = size * 1003 // 1000
         # Use a minimum size, otherwise we will fail to calculate an AVB footer
         # or fail to construct an ext4 image.
         size = max(size, 256 * 1024)
@@ -448,8 +453,12 @@
       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"))
-      # add .2% margin
-      inodes = inodes * 1002 // 1000
+      # add .2% margin or 1 inode, whichever is greater
+      spare_inodes = inodes * 2 // 1000
+      min_spare_inodes = 1
+      if spare_inodes < min_spare_inodes:
+        spare_inodes = min_spare_inodes
+      inodes += spare_inodes
       prop_dict["extfs_inode_count"] = str(inodes)
       prop_dict["partition_size"] = str(size)
       logger.info(
@@ -560,11 +569,11 @@
   elif mount_point == "system_other":
     # We inherit the selinux policies of /system since we contain some of its
     # files.
-    copy_prop("avb_system_hashtree_enable", "avb_hashtree_enable")
-    copy_prop("avb_system_add_hashtree_footer_args",
+    copy_prop("avb_system_other_hashtree_enable", "avb_hashtree_enable")
+    copy_prop("avb_system_other_add_hashtree_footer_args",
               "avb_add_hashtree_footer_args")
-    copy_prop("avb_system_key_path", "avb_key_path")
-    copy_prop("avb_system_algorithm", "avb_algorithm")
+    copy_prop("avb_system_other_key_path", "avb_key_path")
+    copy_prop("avb_system_other_algorithm", "avb_algorithm")
     copy_prop("fs_type", "fs_type")
     copy_prop("system_fs_type", "fs_type")
     copy_prop("system_size", "partition_size")
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index dcc083c..de3e3b8 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -14,6 +14,7 @@
 
 from __future__ import print_function
 
+import collections
 import copy
 import errno
 import getopt
@@ -587,6 +588,11 @@
     cmd.append("--second")
     cmd.append(fn)
 
+  fn = os.path.join(sourcedir, "dtb")
+  if os.access(fn, os.F_OK):
+    cmd.append("--dtb")
+    cmd.append(fn)
+
   fn = os.path.join(sourcedir, "cmdline")
   if os.access(fn, os.F_OK):
     cmd.append("--cmdline")
@@ -1523,6 +1529,13 @@
     """Called at the start of full OTA installation."""
     return self._DoCall("FullOTA_InstallBegin")
 
+  def FullOTA_GetBlockDifferences(self):
+    """Called during full OTA installation and verification.
+    Implementation should return a list of BlockDifference objects describing
+    the update on each additional partitions.
+    """
+    return self._DoCall("FullOTA_GetBlockDifferences")
+
   def FullOTA_InstallEnd(self):
     """Called at the end of full OTA installation; typically this is
     used to install the image for the device's baseband processor."""
@@ -1551,6 +1564,13 @@
     verification is complete)."""
     return self._DoCall("IncrementalOTA_InstallBegin")
 
+  def IncrementalOTA_GetBlockDifferences(self):
+    """Called during incremental OTA installation and verification.
+    Implementation should return a list of BlockDifference objects describing
+    the update on each additional partitions.
+    """
+    return self._DoCall("IncrementalOTA_GetBlockDifferences")
+
   def IncrementalOTA_InstallEnd(self):
     """Called at the end of incremental OTA installation; typically
     this is used to install the image for the device's baseband
@@ -1745,11 +1765,29 @@
     self.touched_src_ranges = b.touched_src_ranges
     self.touched_src_sha1 = b.touched_src_sha1
 
-    if src is None:
-      _, self.device = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
+    # On devices with dynamic partitions, for new partitions,
+    # src is None but OPTIONS.source_info_dict is not.
+    if OPTIONS.source_info_dict is None:
+      is_dynamic_build = OPTIONS.info_dict.get(
+          "use_dynamic_partitions") == "true"
     else:
-      _, self.device = GetTypeAndDevice("/" + partition,
-                                        OPTIONS.source_info_dict)
+      is_dynamic_build = OPTIONS.source_info_dict.get(
+          "use_dynamic_partitions") == "true"
+
+    # For dynamic partitions builds, always check partition list in target build
+    # because new partitions may be added.
+    is_dynamic = is_dynamic_build and partition in shlex.split(
+        OPTIONS.info_dict.get("dynamic_partition_list", "").strip())
+
+    if is_dynamic:
+      self.device = 'map_partition("%s")' % partition
+    else:
+      if OPTIONS.source_info_dict is None:
+        _, device_path = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
+      else:
+        _, device_path = GetTypeAndDevice("/" + partition,
+                                          OPTIONS.source_info_dict)
+      self.device = '"%s"' % device_path
 
   @property
   def required_cache(self):
@@ -1768,7 +1806,7 @@
     self._WriteUpdate(script, output_zip)
 
     if write_verify_script:
-      self._WritePostInstallVerifyScript(script)
+      self.WritePostInstallVerifyScript(script)
 
   def WriteStrictVerifyScript(self, script):
     """Verify all the blocks in the care_map, including clobbered blocks.
@@ -1782,11 +1820,11 @@
     ranges = self.tgt.care_map
     ranges_str = ranges.to_string_raw()
     script.AppendExtra(
-        'range_sha1("%s", "%s") == "%s" && ui_print("    Verified.") || '
-        'ui_print("\\"%s\\" has unexpected contents.");' % (
+        'range_sha1(%s, "%s") == "%s" && ui_print("    Verified.") || '
+        'ui_print("%s has unexpected contents.");' % (
             self.device, ranges_str,
             self.tgt.TotalSha1(include_clobbered_blocks=True),
-            self.device))
+            self.partition))
     script.AppendExtra("")
 
   def WriteVerifyScript(self, script, touched_blocks_only=False):
@@ -1811,7 +1849,7 @@
 
       ranges_str = ranges.to_string_raw()
       script.AppendExtra(
-          'if (range_sha1("%s", "%s") == "%s" || block_image_verify("%s", '
+          'if (range_sha1(%s, "%s") == "%s" || block_image_verify(%s, '
           'package_extract_file("%s.transfer.list"), "%s.new.dat", '
           '"%s.patch.dat")) then' % (
               self.device, ranges_str, expected_sha1,
@@ -1828,7 +1866,7 @@
         # this check fails, give an explicit log message about the partition
         # having been remounted R/W (the most likely explanation).
         if self.check_first_block:
-          script.AppendExtra('check_first_block("%s");' % (self.device,))
+          script.AppendExtra('check_first_block(%s);' % (self.device,))
 
         # If version >= 4, try block recovery before abort update
         if partition == "system":
@@ -1836,8 +1874,8 @@
         else:
           code = ErrorCode.VENDOR_RECOVER_FAILURE
         script.AppendExtra((
-            'ifelse (block_image_recover("{device}", "{ranges}") && '
-            'block_image_verify("{device}", '
+            'ifelse (block_image_recover({device}, "{ranges}") && '
+            'block_image_verify({device}, '
             'package_extract_file("{partition}.transfer.list"), '
             '"{partition}.new.dat", "{partition}.patch.dat"), '
             'ui_print("{partition} recovered successfully."), '
@@ -1859,14 +1897,14 @@
             'abort("E%d: %s partition has unexpected contents");\n'
             'endif;') % (code, partition))
 
-  def _WritePostInstallVerifyScript(self, script):
+  def WritePostInstallVerifyScript(self, script):
     partition = self.partition
     script.Print('Verifying the updated %s image...' % (partition,))
     # Unlike pre-install verification, clobbered_blocks should not be ignored.
     ranges = self.tgt.care_map
     ranges_str = ranges.to_string_raw()
     script.AppendExtra(
-        'if range_sha1("%s", "%s") == "%s" then' % (
+        'if range_sha1(%s, "%s") == "%s" then' % (
             self.device, ranges_str,
             self.tgt.TotalSha1(include_clobbered_blocks=True)))
 
@@ -1875,7 +1913,7 @@
     if self.tgt.extended:
       ranges_str = self.tgt.extended.to_string_raw()
       script.AppendExtra(
-          'if range_sha1("%s", "%s") == "%s" then' % (
+          '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,))
@@ -1941,7 +1979,7 @@
     else:
       code = ErrorCode.VENDOR_UPDATE_FAILURE
 
-    call = ('block_image_update("{device}", '
+    call = ('block_image_update({device}, '
             'package_extract_file("{partition}.transfer.list"), '
             '"{new_data_name}", "{partition}.patch.dat") ||\n'
             '  abort("E{code}: Failed to update {partition} image.");'.format(
@@ -2134,3 +2172,219 @@
   logger.info("putting script in %s", sh_location)
 
   output_sink(sh_location, sh)
+
+
+class DynamicPartitionUpdate(object):
+  def __init__(self, src_group=None, tgt_group=None, progress=None,
+               block_difference=None):
+    self.src_group = src_group
+    self.tgt_group = tgt_group
+    self.progress = progress
+    self.block_difference = block_difference
+
+  @property
+  def src_size(self):
+    if not self.block_difference:
+      return 0
+    return DynamicPartitionUpdate._GetSparseImageSize(self.block_difference.src)
+
+  @property
+  def tgt_size(self):
+    if not self.block_difference:
+      return 0
+    return DynamicPartitionUpdate._GetSparseImageSize(self.block_difference.tgt)
+
+  @staticmethod
+  def _GetSparseImageSize(img):
+    if not img:
+      return 0
+    return img.blocksize * img.total_blocks
+
+
+class DynamicGroupUpdate(object):
+  def __init__(self, src_size=None, tgt_size=None):
+    # None: group does not exist. 0: no size limits.
+    self.src_size = src_size
+    self.tgt_size = tgt_size
+
+
+class DynamicPartitionsDifference(object):
+  def __init__(self, info_dict, block_diffs, progress_dict=None,
+               source_info_dict=None):
+    if progress_dict is None:
+      progress_dict = dict()
+
+    self._remove_all_before_apply = False
+    if source_info_dict is None:
+      self._remove_all_before_apply = True
+      source_info_dict = dict()
+
+    block_diff_dict = {e.partition:e for e in block_diffs}
+    assert len(block_diff_dict) == len(block_diffs), \
+        "Duplicated BlockDifference object for {}".format(
+            [partition for partition, count in
+             collections.Counter(e.partition for e in block_diffs).items()
+             if count > 1])
+
+    self._partition_updates = dict()
+
+    for p, block_diff in block_diff_dict.items():
+      self._partition_updates[p] = DynamicPartitionUpdate()
+      self._partition_updates[p].block_difference = block_diff
+
+    for p, progress in progress_dict.items():
+      if p in self._partition_updates:
+        self._partition_updates[p].progress = progress
+
+    tgt_groups = shlex.split(info_dict.get(
+        "super_partition_groups", "").strip())
+    src_groups = shlex.split(source_info_dict.get(
+        "super_partition_groups", "").strip())
+
+    for g in tgt_groups:
+      for p in shlex.split(info_dict.get(
+          "super_%s_partition_list" % g, "").strip()):
+        assert p in self._partition_updates, \
+            "{} is in target super_{}_partition_list but no BlockDifference " \
+            "object is provided.".format(p, g)
+        self._partition_updates[p].tgt_group = g
+
+    for g in src_groups:
+      for p in shlex.split(source_info_dict.get(
+          "super_%s_partition_list" % g, "").strip()):
+        assert p in self._partition_updates, \
+            "{} is in source super_{}_partition_list but no BlockDifference " \
+            "object is provided.".format(p, g)
+        self._partition_updates[p].src_group = g
+
+    target_dynamic_partitions = set(shlex.split(info_dict.get(
+        "dynamic_partition_list", "").strip()))
+    block_diffs_with_target = set(p for p, u in self._partition_updates.items()
+                                  if u.tgt_size)
+    assert block_diffs_with_target == target_dynamic_partitions, \
+        "Target Dynamic partitions: {}, BlockDifference with target: {}".format(
+            list(target_dynamic_partitions), list(block_diffs_with_target))
+
+    source_dynamic_partitions = set(shlex.split(source_info_dict.get(
+        "dynamic_partition_list", "").strip()))
+    block_diffs_with_source = set(p for p, u in self._partition_updates.items()
+                                  if u.src_size)
+    assert block_diffs_with_source == source_dynamic_partitions, \
+        "Source Dynamic partitions: {}, BlockDifference with source: {}".format(
+            list(source_dynamic_partitions), list(block_diffs_with_source))
+
+    if self._partition_updates:
+      logger.info("Updating dynamic partitions %s",
+                  self._partition_updates.keys())
+
+    self._group_updates = dict()
+
+    for g in tgt_groups:
+      self._group_updates[g] = DynamicGroupUpdate()
+      self._group_updates[g].tgt_size = int(info_dict.get(
+          "super_%s_group_size" % g, "0").strip())
+
+    for g in src_groups:
+      if g not in self._group_updates:
+        self._group_updates[g] = DynamicGroupUpdate()
+      self._group_updates[g].src_size = int(source_info_dict.get(
+          "super_%s_group_size" % g, "0").strip())
+
+    self._Compute()
+
+  def WriteScript(self, script, output_zip, write_verify_script=False):
+    script.Comment('--- Start patching dynamic partitions ---')
+    for p, u in self._partition_updates.items():
+      if u.src_size and u.tgt_size and u.src_size > u.tgt_size:
+        script.Comment('Patch partition %s' % p)
+        u.block_difference.WriteScript(script, output_zip, progress=u.progress,
+                                       write_verify_script=False)
+
+    op_list_path = MakeTempFile()
+    with open(op_list_path, 'w') as f:
+      for line in self._op_list:
+        f.write('{}\n'.format(line))
+
+    ZipWrite(output_zip, op_list_path, "dynamic_partitions_op_list")
+
+    script.Comment('Update dynamic partition metadata')
+    script.AppendExtra('assert(update_dynamic_partitions('
+                       'package_extract_file("dynamic_partitions_op_list")));')
+
+    if write_verify_script:
+      for p, u in self._partition_updates.items():
+        if u.src_size and u.tgt_size and u.src_size > u.tgt_size:
+          u.block_difference.WritePostInstallVerifyScript(script)
+          script.AppendExtra('unmap_partition("%s");' % p) # ignore errors
+
+    for p, u in self._partition_updates.items():
+      if u.tgt_size and u.src_size <= u.tgt_size:
+        script.Comment('Patch partition %s' % p)
+        u.block_difference.WriteScript(script, output_zip, progress=u.progress,
+                                       write_verify_script=write_verify_script)
+        if write_verify_script:
+          script.AppendExtra('unmap_partition("%s");' % p) # ignore errors
+
+    script.Comment('--- End patching dynamic partitions ---')
+
+  def _Compute(self):
+    self._op_list = list()
+
+    def append(line):
+      self._op_list.append(line)
+
+    def comment(line):
+      self._op_list.append("# %s" % line)
+
+    if self._remove_all_before_apply:
+      comment('Remove all existing dynamic partitions and groups before '
+              'applying full OTA')
+      append('remove_all_groups')
+
+    for p, u in self._partition_updates.items():
+      if u.src_group and not u.tgt_group:
+        append('remove %s' % p)
+
+    for p, u in self._partition_updates.items():
+      if u.src_group and u.tgt_group and u.src_group != u.tgt_group:
+        comment('Move partition %s from %s to default' % (p, u.src_group))
+        append('move %s default' % p)
+
+    for p, u in self._partition_updates.items():
+      if u.src_size and u.tgt_size and u.src_size > u.tgt_size:
+        comment('Shrink partition %s from %d to %d' %
+                (p, u.src_size, u.tgt_size))
+        append('resize %s %s' % (p, u.tgt_size))
+
+    for g, u in self._group_updates.items():
+      if u.src_size is not None and u.tgt_size is None:
+        append('remove_group %s' % g)
+      if (u.src_size is not None and u.tgt_size is not None and
+          u.src_size > u.tgt_size):
+        comment('Shrink group %s from %d to %d' % (g, u.src_size, u.tgt_size))
+        append('resize_group %s %d' % (g, u.tgt_size))
+
+    for g, u in self._group_updates.items():
+      if u.src_size is None and u.tgt_size is not None:
+        comment('Add group %s with maximum size %d' % (g, u.tgt_size))
+        append('add_group %s %d' % (g, u.tgt_size))
+      if (u.src_size is not None and u.tgt_size is not None and
+          u.src_size < u.tgt_size):
+        comment('Grow group %s from %d to %d' % (g, u.src_size, u.tgt_size))
+        append('resize_group %s %d' % (g, u.tgt_size))
+
+    for p, u in self._partition_updates.items():
+      if u.tgt_group and not u.src_group:
+        comment('Add partition %s to group %s' % (p, u.tgt_group))
+        append('add %s %s' % (p, u.tgt_group))
+
+    for p, u in self._partition_updates.items():
+      if u.tgt_size and u.src_size < u.tgt_size:
+        comment('Grow partition %s from %d to %d' % (p, u.src_size, u.tgt_size))
+        append('resize %s %d' % (p, u.tgt_size))
+
+    for p, u in self._partition_updates.items():
+      if u.src_group and u.tgt_group and u.src_group != u.tgt_group:
+        comment('Move partition %s from default to %s' %
+                (p, u.tgt_group))
+        append('move %s %s' % (p, u.tgt_group))
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 7ec8ad8..3fbcbcf 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -826,32 +826,51 @@
   # See the notes in WriteBlockIncrementalOTAPackage().
   allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
 
-  # Full OTA is done as an "incremental" against an empty source image. This
-  # has the effect of writing new data from the package to the entire
-  # partition, but lets us reuse the updater code that writes incrementals to
-  # do it.
-  system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
-                                     allow_shared_blocks)
-  system_tgt.ResetFileMap()
-  system_diff = common.BlockDifference("system", system_tgt, src=None)
-  system_diff.WriteScript(script, output_zip,
-                          write_verify_script=OPTIONS.verify)
+  def GetBlockDifference(partition):
+    # Full OTA is done as an "incremental" against an empty source image. This
+    # has the effect of writing new data from the package to the entire
+    # partition, but lets us reuse the updater code that writes incrementals to
+    # do it.
+    tgt = common.GetSparseImage(partition, OPTIONS.input_tmp, input_zip,
+                                allow_shared_blocks)
+    tgt.ResetFileMap()
+    diff = common.BlockDifference(partition, tgt, src=None)
+    return diff
 
-  boot_img = common.GetBootableImage(
-      "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
+  device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
+  if device_specific_diffs:
+    assert all(isinstance(diff, common.BlockDifference)
+               for diff in device_specific_diffs), \
+        "FullOTA_GetBlockDifferences is not returning a list of " \
+        "BlockDifference objects"
 
+  progress_dict = dict()
+  block_diffs = [GetBlockDifference("system")]
   if HasVendorPartition(input_zip):
-    script.ShowProgress(0.1, 0)
+    block_diffs.append(GetBlockDifference("vendor"))
+    progress_dict["vendor"] = 0.1
+  if device_specific_diffs:
+    block_diffs += device_specific_diffs
 
-    vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
-                                       allow_shared_blocks)
-    vendor_tgt.ResetFileMap()
-    vendor_diff = common.BlockDifference("vendor", vendor_tgt)
-    vendor_diff.WriteScript(script, output_zip,
-                            write_verify_script=OPTIONS.verify)
+  if target_info.get('use_dynamic_partitions') == "true":
+    # Use empty source_info_dict to indicate that all partitions / groups must
+    # be re-added.
+    dynamic_partitions_diff = common.DynamicPartitionsDifference(
+        info_dict=OPTIONS.info_dict,
+        block_diffs=block_diffs,
+        progress_dict=progress_dict)
+    dynamic_partitions_diff.WriteScript(script, output_zip,
+                                        write_verify_script=OPTIONS.verify)
+  else:
+    for block_diff in block_diffs:
+      block_diff.WriteScript(script, output_zip,
+                             progress=progress_dict.get(block_diff.partition),
+                             write_verify_script=OPTIONS.verify)
 
   AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
 
+  boot_img = common.GetBootableImage(
+      "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
   common.CheckSize(boot_img.data, "boot.img", target_info)
   common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
 
@@ -1571,18 +1590,43 @@
   system_diff.WriteVerifyScript(script, touched_blocks_only=True)
   if vendor_diff:
     vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
+  device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
+  if device_specific_diffs:
+    assert all(isinstance(diff, common.BlockDifference)
+               for diff in device_specific_diffs), \
+        "IncrementalOTA_GetBlockDifferences is not returning a list of " \
+        "BlockDifference objects"
+    for diff in device_specific_diffs:
+      diff.WriteVerifyScript(script, touched_blocks_only=True)
 
   script.Comment("---- start making changes here ----")
 
   device_specific.IncrementalOTA_InstallBegin()
 
-  system_diff.WriteScript(script, output_zip,
-                          progress=0.8 if vendor_diff else 0.9,
-                          write_verify_script=OPTIONS.verify)
-
+  block_diffs = [system_diff]
+  progress_dict = {"system": 0.8 if vendor_diff else 0.9}
   if vendor_diff:
-    vendor_diff.WriteScript(script, output_zip, progress=0.1,
-                            write_verify_script=OPTIONS.verify)
+    block_diffs.append(vendor_diff)
+    progress_dict["vendor"] = 0.1
+  if device_specific_diffs:
+    block_diffs += device_specific_diffs
+
+  if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
+    if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
+      raise RuntimeError(
+          "can't generate incremental that disables dynamic partitions")
+    dynamic_partitions_diff = common.DynamicPartitionsDifference(
+        info_dict=OPTIONS.target_info_dict,
+        source_info_dict=OPTIONS.source_info_dict,
+        block_diffs=block_diffs,
+        progress_dict=progress_dict)
+    dynamic_partitions_diff.WriteScript(
+        script, output_zip, write_verify_script=OPTIONS.verify)
+  else:
+    for block_diff in block_diffs:
+      block_diff.WriteScript(script, output_zip,
+                             progress=progress_dict.get(block_diff.partition),
+                             write_verify_script=OPTIONS.verify)
 
   if OPTIONS.two_step:
     common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index c99049a..3619633 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -988,3 +988,201 @@
                              recovery_image, boot_image, self._info)
     validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
                                                         self._info)
+
+
+class MockScriptWriter(object):
+  """A class that mocks edify_generator.EdifyGenerator.
+  """
+  def __init__(self, enable_comments=False):
+    self.lines = []
+    self.enable_comments = enable_comments
+  def Comment(self, comment):
+    if self.enable_comments:
+      self.lines.append("# {}".format(comment))
+  def AppendExtra(self, extra):
+    self.lines.append(extra)
+  def __str__(self):
+    return "\n".join(self.lines)
+
+
+class MockBlockDifference(object):
+  def __init__(self, partition, tgt, src=None):
+    self.partition = partition
+    self.tgt = tgt
+    self.src = src
+  def WriteScript(self, script, _, progress=None,
+                  write_verify_script=False):
+    if progress:
+      script.AppendExtra("progress({})".format(progress))
+    script.AppendExtra("patch({});".format(self.partition))
+    if write_verify_script:
+      self.WritePostInstallVerifyScript(script)
+  def WritePostInstallVerifyScript(self, script):
+    script.AppendExtra("verify({});".format(self.partition))
+
+
+class FakeSparseImage(object):
+  def __init__(self, size):
+    self.blocksize = 4096
+    self.total_blocks = size // 4096
+    assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
+
+
+class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
+  @staticmethod
+  def get_op_list(output_path):
+    with zipfile.ZipFile(output_path, 'r') as output_zip:
+      with output_zip.open("dynamic_partitions_op_list") as op_list:
+        return [line.strip() for line in op_list.readlines()
+                if not line.startswith("#")]
+
+  def setUp(self):
+    self.script = MockScriptWriter()
+    self.output_path = common.MakeTempFile(suffix='.zip')
+
+  def test_full(self):
+    target_info = common.LoadDictionaryFromLines("""
+dynamic_partition_list=system vendor
+super_partition_groups=group_foo
+super_group_foo_group_size={group_size}
+super_group_foo_partition_list=system vendor
+""".format(group_size=4 * GiB).split("\n"))
+    block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
+                   MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
+
+    dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
+    with zipfile.ZipFile(self.output_path, 'w') as output_zip:
+      dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
+
+    self.assertEqual(str(self.script).strip(), """
+assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
+patch(vendor);
+verify(vendor);
+unmap_partition("vendor");
+patch(system);
+verify(system);
+unmap_partition("system");
+""".strip())
+
+    lines = self.get_op_list(self.output_path)
+
+    remove_all_groups = lines.index("remove_all_groups")
+    add_group = lines.index("add_group group_foo 4294967296")
+    add_vendor = lines.index("add vendor group_foo")
+    add_system = lines.index("add system group_foo")
+    resize_vendor = lines.index("resize vendor 1073741824")
+    resize_system = lines.index("resize system 3221225472")
+
+    self.assertLess(remove_all_groups, add_group,
+                    "Should add groups after removing all groups")
+    self.assertLess(add_group, min(add_vendor, add_system),
+                    "Should add partitions after adding group")
+    self.assertLess(add_system, resize_system,
+                    "Should resize system after adding it")
+    self.assertLess(add_vendor, resize_vendor,
+                    "Should resize vendor after adding it")
+
+  def test_inc_groups(self):
+    source_info = common.LoadDictionaryFromLines("""
+super_partition_groups=group_foo group_bar group_baz
+super_group_foo_group_size={group_foo_size}
+super_group_bar_group_size={group_bar_size}
+""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
+    target_info = common.LoadDictionaryFromLines("""
+super_partition_groups=group_foo group_baz group_qux
+super_group_foo_group_size={group_foo_size}
+super_group_baz_group_size={group_baz_size}
+super_group_qux_group_size={group_qux_size}
+""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
+           group_qux_size=1 * GiB).split("\n"))
+
+    dp_diff = common.DynamicPartitionsDifference(target_info,
+                                                 block_diffs=[],
+                                                 source_info_dict=source_info)
+    with zipfile.ZipFile(self.output_path, 'w') as output_zip:
+      dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
+
+    lines = self.get_op_list(self.output_path)
+
+    removed = lines.index("remove_group group_bar")
+    shrunk = lines.index("resize_group group_foo 3221225472")
+    grown = lines.index("resize_group group_baz 4294967296")
+    added = lines.index("add_group group_qux 1073741824")
+
+    self.assertLess(max(removed, shrunk) < min(grown, added),
+                    "ops that remove / shrink partitions must precede ops that "
+                    "grow / add partitions")
+
+  def test_inc_partitions(self):
+    source_info = common.LoadDictionaryFromLines("""
+dynamic_partition_list=system vendor product product_services
+super_partition_groups=group_foo
+super_group_foo_group_size={group_foo_size}
+super_group_foo_partition_list=system vendor product product_services
+""".format(group_foo_size=4 * GiB).split("\n"))
+    target_info = common.LoadDictionaryFromLines("""
+dynamic_partition_list=system vendor product odm
+super_partition_groups=group_foo group_bar
+super_group_foo_group_size={group_foo_size}
+super_group_foo_partition_list=system vendor odm
+super_group_bar_group_size={group_bar_size}
+super_group_bar_partition_list=product
+""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
+
+    block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
+                                       src=FakeSparseImage(1024 * MiB)),
+                   MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
+                                       src=FakeSparseImage(1024 * MiB)),
+                   MockBlockDifference("product", FakeSparseImage(1024 * MiB),
+                                       src=FakeSparseImage(1024 * MiB)),
+                   MockBlockDifference("product_services", None,
+                                       src=FakeSparseImage(1024 * MiB)),
+                   MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
+                                       src=None)]
+
+    dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
+                                                 source_info_dict=source_info)
+    with zipfile.ZipFile(self.output_path, 'w') as output_zip:
+      dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
+
+    metadata_idx = self.script.lines.index(
+        'assert(update_dynamic_partitions(package_extract_file('
+        '"dynamic_partitions_op_list")));')
+    self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
+    self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
+    for p in ("product", "system", "odm"):
+      patch_idx = self.script.lines.index("patch({});".format(p))
+      verify_idx = self.script.lines.index("verify({});".format(p))
+      self.assertLess(metadata_idx, patch_idx,
+                      "Should patch {} after updating metadata".format(p))
+      self.assertLess(patch_idx, verify_idx,
+                      "Should verify {} after patching".format(p))
+
+    self.assertNotIn("patch(product_services);", self.script.lines)
+
+    lines = self.get_op_list(self.output_path)
+
+    remove = lines.index("remove product_services")
+    move_product_out = lines.index("move product default")
+    shrink = lines.index("resize vendor 536870912")
+    shrink_group = lines.index("resize_group group_foo 3221225472")
+    add_group_bar = lines.index("add_group group_bar 1073741824")
+    add_odm = lines.index("add odm group_foo")
+    grow_existing = lines.index("resize system 1610612736")
+    grow_added = lines.index("resize odm 1073741824")
+    move_product_in = lines.index("move product group_bar")
+
+    max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
+    min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
+
+    self.assertLess(max_idx_move_partition_out_foo, shrink_group,
+                    "Must shrink group after partitions inside group are shrunk"
+                    " / removed")
+
+    self.assertLess(add_group_bar, move_product_in,
+                    "Must add partitions to group after group is added")
+
+    self.assertLess(max_idx_move_partition_out_foo,
+                    min_idx_move_partition_in_foo,
+                    "Must shrink partitions / remove partitions from group"
+                    "before adding / moving partitions into group")