Merge "Ensure special Dalvik annotations are kept" into main
diff --git a/Changes.md b/Changes.md
index fc6701d..6836528 100644
--- a/Changes.md
+++ b/Changes.md
@@ -10,8 +10,9 @@
 build team via email android-building@googlegroups.com (external) for any
 questions, or see [go/soong](http://go/soong) (internal).
 
-To omit the validation, `BUILD_BROKEN_PLUGIN_VALIDATION` expects a list of
-plugins to omit from the validation.
+To omit the validation, `BUILD_BROKEN_PLUGIN_VALIDATION` expects a
+space-separated list of plugins to omit from the validation. This must be set
+within a product configuration .mk file, board config .mk file, or buildspec.mk.
 
 ## Python 2 to 3 migration
 
diff --git a/core/BUILD.bazel b/core/BUILD.bazel
index 3e69e62..f4869d4 100644
--- a/core/BUILD.bazel
+++ b/core/BUILD.bazel
@@ -1,4 +1,28 @@
+# Copyright (C) 2023 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
+
 # Export tradefed templates for tests.
 exports_files(
     glob(["*.xml"]),
 )
+
+# Export proguard flag files for r8.
+filegroup(
+    name = "global_proguard_flags",
+    srcs = [
+        "proguard.flags",
+        "proguard_basic_keeps.flags",
+    ],
+    visibility = ["//visibility:public"],
+)
diff --git a/core/Makefile b/core/Makefile
index 649982d..b715ac3 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -16,53 +16,93 @@
 ODM_DLKM_NOTICE_DEPS :=
 SYSTEM_DLKM_NOTICE_DEPS :=
 
-# -----------------------------------------------------------------
-# Release Config Flags
 
-# Create a summary file of build flags for each partition
-# $(1): build flags json file
-# $(2): flag names
-define generate-partition-build-flag-file
-$(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))
-$(eval $(strip $(1)): PRIVATE_FLAG_NAMES := $(strip $(2)))
-$(strip $(1)):
-	mkdir -p $$(dir $$(PRIVATE_OUT))
-	echo '{' > $$(PRIVATE_OUT)
-	echo '"flags": [' >> $$(PRIVATE_OUT)
-	$$(foreach flag, $$(PRIVATE_FLAG_NAMES), \
-		( \
-			printf '  { "name": "%s", "value": "%s", ' \
-					'$$(flag)' \
-					'$$(_ALL_RELEASE_FLAGS.$$(flag).VALUE)' \
-					; \
-			printf '"set": "%s", "default": "%s", "declared": "%s" }' \
-					'$$(_ALL_RELEASE_FLAGS.$$(flag).SET_IN)' \
-					'$$(_ALL_RELEASE_FLAGS.$$(flag).DEFAULT)' \
-					'$$(_ALL_RELEASE_FLAGS.$$(flag).DECLARED_IN)' \
-					; \
-			printf '$$(if $$(filter $$(lastword $$(PRIVATE_FLAG_NAMES)),$$(flag)),,$$(comma))\n' ; \
-		) >> $$(PRIVATE_OUT) \
-	)
-	echo "]" >> $$(PRIVATE_OUT)
-	echo "}" >> $$(PRIVATE_OUT)
+# IMAGES_TO_BUILD is a list of the partition .img files that will be created.
+IMAGES_TO_BUILD:=
+ifneq ($(BUILDING_BOOT_IMAGE),)
+  IMAGES_TO_BUILD += boot
+endif
+ifneq ($(BUILDING_CACHE_IMAGE),)
+  IMAGES_TO_BUILD += cache
+endif
+ifneq ($(BUILDING_DEBUG_BOOT_IMAGE),)
+  IMAGES_TO_BUILD += debug_boot
+endif
+ifneq ($(BUILDING_DEBUG_VENDOR_BOOT_IMAGE),)
+  IMAGES_TO_BUILD += debug_vendor_boot
+endif
+ifneq ($(BUILDING_INIT_BOOT_IMAGE),)
+  IMAGES_TO_BUILD += init_boot
+endif
+ifneq ($(BUILDING_ODM_DLKM_IMAGE),)
+  IMAGES_TO_BUILD += odm_dlkm
+endif
+ifneq ($(BUILDING_ODM_IMAGE),)
+  IMAGES_TO_BUILD += odm
+endif
+ifneq ($(BUILDING_PRODUCT_IMAGE),)
+  IMAGES_TO_BUILD += product
+endif
+ifneq ($(BUILDING_RAMDISK_IMAGE),)
+  IMAGES_TO_BUILD += ramdisk
+endif
+ifneq ($(BUILDING_RECOVERY_IMAGE),)
+  IMAGES_TO_BUILD += recovery
+endif
+ifneq ($(BUILDING_SUPER_EMPTY_IMAGE),)
+  IMAGES_TO_BUILD += super_empty
+endif
+ifneq ($(BUILDING_SYSTEM_DLKM_IMAGE),)
+  IMAGES_TO_BUILD += system_dlkm
+endif
+ifneq ($(BUILDING_SYSTEM_EXT_IMAGE),)
+  IMAGES_TO_BUILD += system_ext
+endif
+ifneq ($(BUILDING_SYSTEM_IMAGE),)
+  IMAGES_TO_BUILD += system
+endif
+ifneq ($(BUILDING_SYSTEM_OTHER_IMAGE),)
+  IMAGES_TO_BUILD += system_other
+endif
+ifneq ($(BUILDING_USERDATA_IMAGE),)
+  IMAGES_TO_BUILD += userdata
+endif
+ifneq ($(BUILDING_VBMETA_IMAGE),)
+  IMAGES_TO_BUILD += vbmeta
+endif
+ifneq ($(BUILDING_VENDOR_BOOT_IMAGE),)
+  IMAGES_TO_BUILD += vendor_boot
+endif
+ifneq ($(BUILDING_VENDOR_DLKM_IMAGE),)
+  IMAGES_TO_BUILD += vendor_dlkm
+endif
+ifneq ($(BUILDING_VENDOR_IMAGE),)
+  IMAGES_TO_BUILD += vendor
+endif
+ifneq ($(BUILDING_VENDOR_KERNEL_BOOT_IMAGE),)
+  IMAGES_TO_BUILD += vendor_kernel_boot
+endif
+
+
+###########################################################
+# Get the module names suitable for ALL_MODULES.* variables that are installed
+# for a given partition
+#
+# $(1): Partition
+###########################################################
+define register-names-for-partition
+$(sort $(foreach m,$(product_MODULES),\
+	$(if $(filter $(PRODUCT_OUT)/$(strip $(1))/%, $(ALL_MODULES.$(m).INSTALLED)), \
+		$(m)
+	) \
+))
 endef
 
-_FLAG_PARTITIONS := product system system_ext vendor
 
-$(foreach partition, $(_FLAG_PARTITIONS), \
-	$(eval BUILD_FLAG_SUMMARIES.$(partition) \
-			:= $(TARGET_OUT_FLAGS)/$(partition)/etc/build_flags.json) \
-	$(eval $(call generate-partition-build-flag-file, \
-				$(BUILD_FLAG_SUMMARIES.$(partition)), \
-				$(_ALL_RELEASE_FLAGS.PARTITIONS.$(partition)) \
-            ) \
-    ) \
-)
+# Release & Aconfig Flags
+# -----------------------------------------------------------------
+include $(BUILD_SYSTEM)/packaging/flags.mk
 
-# TODO: Remove
-.PHONY: flag-files
-flag-files: $(foreach partition, $(_FLAG_PARTITIONS), \
-		$(TARGET_OUT_FLAGS)/$(partition)/etc/build_flags.json)
 
 # -----------------------------------------------------------------
 # Define rules to copy PRODUCT_COPY_FILES defined by the product.
@@ -323,6 +363,10 @@
 )
 INTERNAL_VENDOR_RAMDISK_FRAGMENTS += $(BOARD_VENDOR_RAMDISK_FRAGMENTS)
 
+ifneq ($(BOARD_KERNEL_MODULES_16K),)
+INTERNAL_VENDOR_RAMDISK_FRAGMENTS += 16K
+endif
+
 # Strip the list in case of any whitespace.
 INTERNAL_VENDOR_RAMDISK_FRAGMENTS := \
   $(strip $(INTERNAL_VENDOR_RAMDISK_FRAGMENTS))
@@ -1010,16 +1054,32 @@
 BUILT_RAMDISK_16K_TARGET := $(PRODUCT_OUT)/ramdisk_16k.img
 RAMDISK_16K_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_ramdisk_16k)
 
-$(BUILT_RAMDISK_16K_TARGET): $(DEPMOD) $(MKBOOTFS)
-$(BUILT_RAMDISK_16K_TARGET): $(call copy-many-files,$(foreach file,$(BOARD_KERNEL_MODULES_16K),$(file):$(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/$(notdir $(file))))
+$(foreach \
+  file,\
+  $(BOARD_KERNEL_MODULES_16K),\
+  $(eval \
+    $(call copy-and-strip-kernel-module,\
+      $(file),\
+      $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/$(notdir $(file)) \
+    ) \
+  ) \
+)
+
+BOARD_VENDOR_RAMDISK_FRAGMENT.16K.PREBUILT := $(BUILT_RAMDISK_16K_TARGET)
+
+$(BUILT_RAMDISK_16K_TARGET): $(DEPMOD) $(MKBOOTFS) $(EXTRACT_KERNEL) $(COMPRESSION_COMMAND_DEPS)
+$(BUILT_RAMDISK_16K_TARGET): $(foreach file,$(BOARD_KERNEL_MODULES_16K),$(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/$(notdir $(file)))
 	$(DEPMOD) -b $(RAMDISK_16K_STAGING_DIR) 0.0
 	for MODULE in $(BOARD_KERNEL_MODULES_16K); do \
 		basename $$MODULE >> $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0/modules.load ; \
 	done;
-	mkdir -p $(TARGET_OUT_RAMDISK_16K)/lib
 	rm -rf $(TARGET_OUT_RAMDISK_16K)/lib/modules
-	cp -r $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0 $(TARGET_OUT_RAMDISK_16K)/lib/modules
-	$(MKBOOTFS) $(TARGET_OUT_RAMDISK_16K) > $@
+	mkdir -p $(TARGET_OUT_RAMDISK_16K)/lib/modules
+	KERNEL_RELEASE=`$(EXTRACT_KERNEL) --tools lz4:$(LZ4) --input $(BOARD_KERNEL_PATH_16K) --output-release` ;\
+	IS_16K_KERNEL=`$(EXTRACT_KERNEL) --tools lz4:$(LZ4) --input $(BOARD_KERNEL_PATH_16K) --output-config` ;\
+	if [[ "$$IS_16K_KERNEL" == *"CONFIG_ARM64_16K_PAGES=y"* ]]; then SUFFIX=_16k; fi ;\
+	cp -r $(RAMDISK_16K_STAGING_DIR)/lib/modules/0.0 $(TARGET_OUT_RAMDISK_16K)/lib/modules/$$KERNEL_RELEASE$$SUFFIX
+	$(MKBOOTFS) $(TARGET_OUT_RAMDISK_16K) | $(COMPRESSION_COMMAND) > $@
 
 # Builds a ramdisk using modules defined in BOARD_KERNEL_MODULES_16K
 ramdisk_16k: $(BUILT_RAMDISK_16K_TARGET)
@@ -1036,6 +1096,30 @@
 kernel_16k: $(BUILT_KERNEL_16K_TARGET)
 .PHONY: kernel_16k
 
+BUILT_BOOTIMAGE_16K_TARGET := $(PRODUCT_OUT)/boot_16k.img
+
+BOARD_KERNEL_16K_BOOTIMAGE_PARTITION_SIZE := $(BOARD_BOOTIMAGE_PARTITION_SIZE)
+
+$(BUILT_BOOTIMAGE_16K_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH) $(INTERNAL_GKI_CERTIFICATE_DEPS) $(BUILT_KERNEL_16K_TARGET)
+	$(call pretty,"Target boot 16k image: $@")
+	$(call build_boot_from_kernel_avb_enabled,$@,$(BUILT_KERNEL_16K_TARGET))
+
+
+bootimage_16k: $(BUILT_BOOTIMAGE_16K_TARGET)
+.PHONY: bootimage_16k
+
+BUILT_BOOT_OTA_PACKAGE_16K := $(PRODUCT_OUT)/boot_ota_16k.zip
+$(BUILT_BOOT_OTA_PACKAGE_16K): $(OTA_FROM_RAW_IMG) $(BUILT_BOOTIMAGE_16K_TARGET) $(DEFAULT_SYSTEM_DEV_CERTIFICATE).pk8
+	$(OTA_FROM_RAW_IMG) --package_key $(DEFAULT_SYSTEM_DEV_CERTIFICATE) \
+                      --max_timestamp `cat $(BUILD_DATETIME_FILE)` \
+                      --path $(HOST_OUT) \
+                      --partition_name boot \
+                      --output $@ \
+                      $(BUILT_BOOTIMAGE_16K_TARGET)
+
+boototapackage_16k: $(BUILT_BOOT_OTA_PACKAGE_16K)
+.PHONY: boototapackage_16k
+
 endif
 
 
@@ -1045,8 +1129,8 @@
 COMPRESSION_COMMAND := $(LZ4) -l -12 --favor-decSpeed
 RAMDISK_EXT := .lz4
 else
-COMPRESSION_COMMAND_DEPS := $(MINIGZIP)
-COMPRESSION_COMMAND := $(MINIGZIP)
+COMPRESSION_COMMAND_DEPS := $(GZIP)
+COMPRESSION_COMMAND := $(GZIP)
 RAMDISK_EXT := .gz
 endif
 
@@ -1131,6 +1215,29 @@
   $(if $(1),--partition_size $(1),--dynamic_partition_size)
 endef
 
+# $1: output boot image target
+# $2: input path to kernel binary
+define build_boot_from_kernel_avb_enabled
+  $(eval kernel := $(2))
+  $(MKBOOTIMG) --kernel $(kernel) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1)
+  $(if $(BOARD_GKI_SIGNING_KEY_PATH), \
+    $(eval boot_signature := $(call intermediates-dir-for,PACKAGING,generic_boot)/$(notdir $(1)).boot_signature) \
+    $(eval kernel_signature := $(call intermediates-dir-for,PACKAGING,generic_kernel)/$(notdir $(kernel)).boot_signature) \
+    $(call generate_generic_boot_image_certificate,$(1),$(boot_signature),boot,$(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)) $(newline) \
+    $(call generate_generic_boot_image_certificate,$(kernel),$(kernel_signature),generic_kernel,$(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)) $(newline) \
+    cat $(kernel_signature) >> $(boot_signature) $(newline) \
+    $(call assert-max-image-size,$(boot_signature),16 << 10) $(newline) \
+    truncate -s $$(( 16 << 10 )) $(boot_signature) $(newline) \
+    cat "$(boot_signature)" >> $(1))
+  $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),boot)))
+  $(AVBTOOL) add_hash_footer \
+          --image $(1) \
+          $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),boot)) \
+          --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) \
+          $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)
+endef
+
+
 ifndef BOARD_PREBUILT_BOOTIMAGE
 
 ifneq ($(strip $(TARGET_NO_KERNEL)),true)
@@ -1235,22 +1342,7 @@
 # $1: boot image target
 define build_boot_board_avb_enabled
   $(eval kernel := $(call bootimage-to-kernel,$(1)))
-  $(MKBOOTIMG) --kernel $(kernel) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1)
-  $(if $(BOARD_GKI_SIGNING_KEY_PATH), \
-    $(eval boot_signature := $(call intermediates-dir-for,PACKAGING,generic_boot)/$(notdir $(1)).boot_signature) \
-    $(eval kernel_signature := $(call intermediates-dir-for,PACKAGING,generic_kernel)/$(notdir $(kernel)).boot_signature) \
-    $(call generate_generic_boot_image_certificate,$(1),$(boot_signature),boot,$(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)) $(newline) \
-    $(call generate_generic_boot_image_certificate,$(kernel),$(kernel_signature),generic_kernel,$(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)) $(newline) \
-    cat $(kernel_signature) >> $(boot_signature) $(newline) \
-    $(call assert-max-image-size,$(boot_signature),16 << 10) $(newline) \
-    truncate -s $$(( 16 << 10 )) $(boot_signature) $(newline) \
-    cat "$(boot_signature)" >> $(1))
-  $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),boot)))
-  $(AVBTOOL) add_hash_footer \
-          --image $(1) \
-          $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),boot)) \
-          --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) \
-          $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)
+  $(call build_boot_from_kernel_avb_enabled,$(1),$(kernel))
 endef
 
 $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH) $(INTERNAL_GKI_CERTIFICATE_DEPS)
@@ -2170,6 +2262,8 @@
 $(hide) echo "root_dir=$(TARGET_ROOT_OUT)" >> $(1)
 $(if $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITION_SIZE)),\
     $(hide) echo "use_dynamic_partition_size=true" >> $(1))
+$(if $(COPY_IMAGES_FOR_TARGET_FILES_ZIP),\
+    $(hide) echo "use_fixed_timestamp=true" >> $(1))
 $(if $(3),$(hide) $(foreach kv,$(3),echo "$(kv)" >> $(1);))
 endef
 
@@ -3237,6 +3331,12 @@
   ALL_DEFAULT_INSTALLED_MODULES += $(_systemext_symlink)
 endif
 
+# Create symlink /system/bin/hwservicemanager -> /system/system_ext/bin/hwservicemanager
+# Old vendor libraries (like libhidlbase) may require /system/bin/hwservicemanager to be present
+$(call symlink-file,$(TARGET_OUT_SYSTEM_EXT)/bin/hwservicemanager,/system/system_ext/bin/hwservicemanager, $(TARGET_OUT)/bin/hwservicemanager)
+INTERNAL_SYSTEMIMAGE_FILES += \
+    $(TARGET_OUT)/bin/hwservicemanager
+
 # -----------------------------------------------------------------
 # system_dlkm partition image
 
@@ -3313,6 +3413,10 @@
     $(call intermediates-dir-for,PACKAGING,systemimage)
 BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img
 
+# Used by the bazel sandwich to request the staging dir be built
+$(systemimage_intermediates)/staging_dir.stamp: $(FULL_SYSTEMIMAGE_DEPS)
+	touch $@
+
 # $(1): output file
 define build-systemimage-target
   @echo "Target system fs image: $(1)"
@@ -3465,45 +3569,6 @@
 	tar cfj $(ASAN_IN_SYSTEM_INSTALLED) $(ASAN_SYSTEM_INSTALL_OPTIONS) -C $(TARGET_OUT_DATA)/.. $(ASAN_OUT_DIRS_FOR_SYSTEM_INSTALL) >/dev/null
 
 # -----------------------------------------------------------------
-# partition table image
-ifdef BOARD_BPT_INPUT_FILES
-
-BUILT_BPTIMAGE_TARGET := $(PRODUCT_OUT)/partition-table.img
-BUILT_BPTJSON_TARGET := $(PRODUCT_OUT)/partition-table.bpt
-
-INTERNAL_BVBTOOL_MAKE_TABLE_ARGS := \
-	--output_gpt $(BUILT_BPTIMAGE_TARGET) \
-	--output_json $(BUILT_BPTJSON_TARGET) \
-	$(foreach file, $(BOARD_BPT_INPUT_FILES), --input $(file))
-
-ifdef BOARD_BPT_DISK_SIZE
-INTERNAL_BVBTOOL_MAKE_TABLE_ARGS += --disk_size $(BOARD_BPT_DISK_SIZE)
-endif
-
-define build-bptimage-target
-  $(call pretty,"Target partition table image: $(INSTALLED_BPTIMAGE_TARGET)")
-  $(hide) $(BPTTOOL) make_table $(INTERNAL_BVBTOOL_MAKE_TABLE_ARGS) $(BOARD_BPT_MAKE_TABLE_ARGS)
-endef
-
-INSTALLED_BPTIMAGE_TARGET := $(BUILT_BPTIMAGE_TARGET)
-$(BUILT_BPTJSON_TARGET): $(INSTALLED_BPTIMAGE_TARGET)
-	$(hide) touch -c $(BUILT_BPTJSON_TARGET)
-
-$(INSTALLED_BPTIMAGE_TARGET): $(BPTTOOL) $(BOARD_BPT_INPUT_FILES)
-	$(build-bptimage-target)
-
-$(call declare-1p-container,$(INSTALLED_BPTIMAGE_TARGET),)
-$(call declare-container-license-deps,$(INSTALLED_BPTIMAGE_TARGET),$(BOARD_BPT_INPUT_FILES),$(PRODUCT_OUT)/:/)
-
-UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_BPTIMAGE_TARGET)
-
-.PHONY: bptimage-nodeps
-bptimage-nodeps:
-	$(build-bptimage-target)
-
-endif # BOARD_BPT_INPUT_FILES
-
-# -----------------------------------------------------------------
 # cache partition image
 INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_OUT_CACHE)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))
 ifdef BUILDING_CACHE_IMAGE
@@ -4166,7 +4231,7 @@
 
 INSTALLED_PVMFWIMAGE_TARGET := $(PRODUCT_OUT)/pvmfw.img
 INSTALLED_PVMFW_EMBEDDED_AVBKEY_TARGET := $(PRODUCT_OUT)/pvmfw_embedded.avbpubkey
-INSTALLED_PVMFW_BINARY_TARGET := $(call module-installed-files,pvmfw_bin)
+INSTALLED_PVMFW_BINARY_TARGET := $(call module-target-built-files,pvmfw_bin)
 INTERNAL_PVMFWIMAGE_FILES := $(call module-target-built-files,pvmfw_img)
 INTERNAL_PVMFW_EMBEDDED_AVBKEY := $(call module-target-built-files,pvmfw_embedded_key)
 INTERNAL_PVMFW_SYMBOL := $(TARGET_OUT_EXECUTABLES_UNSTRIPPED)/pvmfw
@@ -4204,6 +4269,13 @@
 
 ifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),)
 INTERNAL_AVB_CUSTOMIMAGES_SIGNING_ARGS :=
+BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST :=
+# If BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH is set, the image will be included in
+# BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST, otherwise the image won't be AVB signed.
+$(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
+	$(if $(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH), \
+	$(eval BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST += $(partition)) \
+	$(eval BOARD_$(call to-upper,$(partition))_IMAGE_LIST := $(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST))))
 
 # Sign custom image.
 # $(1): the prebuilt custom image.
@@ -4228,9 +4300,26 @@
 INSTALLED_CUSTOMIMAGES_TARGET += $(3)
 endef
 
-$(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
+# Copy unsigned custom image.
+# $(1): the prebuilt custom image.
+# $(2): the signed custom image target.
+define copy_custom_image
+$(2): $(1) $(INTERNAL_USERIMAGES_DEPS)
+	@echo Target custom image: $(2)
+	mkdir -p $(dir $(2))
+	cp $(1) $(2)
+INSTALLED_CUSTOMIMAGES_TARGET += $(2)
+endef
+
+# Add AVB custom image to droid target
+$(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \
   $(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST), \
      $(eval $(call sign_custom_image,$(image),$(partition),$(PRODUCT_OUT)/$(notdir $(image))))))
+
+# Add unsigned custom image to droid target
+$(foreach partition,$(filter-out $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), $(BOARD_CUSTOMIMAGES_PARTITION_LIST)), \
+  $(foreach image,$(BOARD_$(call to-upper,$(partition))_IMAGE_LIST), \
+     $(eval $(call copy_custom_image,$(image),$(PRODUCT_OUT)/$(notdir $(image))))))
 endif
 
 # -----------------------------------------------------------------
@@ -4507,7 +4596,9 @@
 $(eval part := $(1))
 $(eval PART=$(call to-upper,$(part)))
 $(eval _rollback_index_location := BOARD_AVB_$(PART)_ROLLBACK_INDEX_LOCATION)
+$(eval _key_path := BOARD_AVB_$(PART)_KEY_PATH)
 $(if $($(_rollback_index_location)),,$(error $(_rollback_index_location) is not defined))
+$(if $($(_key_path)),,$(error $(_key_path) is not defined))
 
 INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \
     --chain_partition $(part):$($(_rollback_index_location)):$(AVB_CHAIN_KEY_DIR)/$(part).avbpubkey
@@ -4587,8 +4678,8 @@
 $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),$(eval BOARD_AVB_MAKE_VBMETA_$(call to-upper,$(partition))_IMAGE_ARGS += --padding_size 4096))
 endif
 
-ifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),)
-$(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
+ifneq ($(strip $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST)),)
+$(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \
     $(eval $(call check-and-set-custom-avb-chain-args,$(partition))))
 endif
 
@@ -4675,8 +4766,8 @@
   $(if $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH),\
     $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH) \
         --output $(1)/vbmeta_vendor.avbpubkey)
-  $(if $(BOARD_CUSTOMIMAGES_PARTITION_LIST),\
-    $(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
+  $(if $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST),\
+    $(hide) $(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \
         $(AVBTOOL) extract_public_key --key $(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH) \
             --output $(1)/$(partition).avbpubkey;)) \
   $(if $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),\
@@ -4999,7 +5090,9 @@
 endif # INSTALLED_BOOTIMAGE_TARGET
 endif # my_board_extracted_kernel
 
-ifneq ($(my_board_extracted_kernel),true)
+ifeq ($(my_board_extracted_kernel),true)
+$(call dist-for-goals, droid_targets, $(BUILT_KERNEL_VERSION_FILE))
+else
 $(warning Neither INSTALLED_KERNEL_TARGET nor INSTALLED_BOOTIMAGE_TARGET is defined when \
     PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS  is true. Information about the updated kernel \
     cannot be built into OTA update package. You can fix this by: \
@@ -5262,7 +5355,6 @@
   make_f2fs_casefold \
   merge_ota \
   merge_target_files \
-  minigzip \
   mk_combined_img \
   mkbootfs \
   mkbootimg \
@@ -5289,6 +5381,7 @@
   toybox \
   tune2fs \
   unpack_bootimg \
+  update_device \
   update_host_simulator \
   validate_target_files \
   verity_signer \
@@ -5419,6 +5512,9 @@
 ifeq ($(BOARD_USES_PVMFWIMAGE),true)
 	$(hide) echo "flash pvmfw" >> $@
 endif
+ifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),)
+	$(hide) echo "flash vendor_boot" >> $@
+endif
 ifeq ($(BOARD_AVB_ENABLE),true)
 ifeq ($(BUILDING_VBMETA_IMAGE),true)
 	$(hide) echo "flash --apply-vbmeta vbmeta" >> $@
@@ -5429,9 +5525,6 @@
 ifneq (,$(strip $(BOARD_AVB_VBMETA_VENDOR)))
 	$(hide) echo "flash --apply-vbmeta vbmeta_vendor" >> $@
 endif
-ifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),)
-	$(hide) echo "flash vendor_boot" >> $@
-endif
 ifneq (,$(strip $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS)))
 	$(hide) $(foreach partition,$(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS), \
 	  echo "flash vbmeta_$(partition)" >> $@;)
@@ -5595,15 +5688,20 @@
 	$(hide) echo "avb_recovery_rollback_index_location=$(BOARD_AVB_RECOVERY_ROLLBACK_INDEX_LOCATION)" >> $@
 endif # BOARD_AVB_RECOVERY_KEY_PATH
 ifneq (,$(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)))
-	$(hide) echo "avb_custom_images_partition_list=$(BOARD_CUSTOMIMAGES_PARTITION_LIST)" >> $@
-	$(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
+	$(hide) echo "custom_images_partition_list=$(filter-out $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), $(BOARD_CUSTOMIMAGES_PARTITION_LIST))" >> $@
+	$(hide) $(foreach partition,$(filter-out $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), $(BOARD_CUSTOMIMAGES_PARTITION_LIST)), \
+	    echo "$(partition)_image_list=$(foreach image,$(BOARD_$(call to-upper,$(partition))_IMAGE_LIST),$(notdir $(image)))" >> $@;)
+endif # BOARD_CUSTOMIMAGES_PARTITION_LIST
+ifneq (,$(strip $(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST)))
+	$(hide) echo "avb_custom_images_partition_list=$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST)" >> $@
+	$(hide) $(foreach partition,$(BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST), \
 	    echo "avb_$(partition)_key_path=$(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH)"  >> $@; \
 	    echo "avb_$(partition)_algorithm=$(BOARD_AVB_$(call to-upper,$(partition))_ALGORITHM)"  >> $@; \
 	    echo "avb_$(partition)_add_hashtree_footer_args=$(BOARD_AVB_$(call to-upper,$(partition))_ADD_HASHTREE_FOOTER_ARGS)"  >> $@; \
 	    echo "avb_$(partition)_rollback_index_location=$(BOARD_AVB_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)"  >> $@; \
 	    echo "avb_$(partition)_partition_size=$(BOARD_AVB_$(call to-upper,$(partition))_PARTITION_SIZE)"  >> $@; \
 	    echo "avb_$(partition)_image_list=$(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST),$(notdir $(image)))" >> $@;)
-endif # BOARD_CUSTOMIMAGES_PARTITION_LIST
+endif # BOARD_AVB_CUSTOMIMAGES_PARTITION_LIST
 ifneq (,$(strip $(BOARD_AVB_VBMETA_SYSTEM)))
 	$(hide) echo "avb_vbmeta_system=$(BOARD_AVB_VBMETA_SYSTEM)" >> $@
 	$(hide) echo "avb_vbmeta_system_args=$(BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS)" >> $@
@@ -5628,14 +5726,6 @@
 	echo "avb_vbmeta_$(partition)_rollback_index_location=$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)" >> $@ ;)
 endif # BOARD_AVB_VBMETA_CUSTOM_PARTITIONS
 endif # BOARD_AVB_ENABLE
-ifdef BOARD_BPT_INPUT_FILES
-	$(hide) echo "board_bpt_enable=true" >> $@
-	$(hide) echo "board_bpt_make_table_args=$(BOARD_BPT_MAKE_TABLE_ARGS)" >> $@
-	$(hide) echo "board_bpt_input_files=$(BOARD_BPT_INPUT_FILES)" >> $@
-endif
-ifdef BOARD_BPT_DISK_SIZE
-	$(hide) echo "board_bpt_disk_size=$(BOARD_BPT_DISK_SIZE)" >> $@
-endif
 	$(call generate-userimage-prop-dictionary, $@)
 ifeq ($(AB_OTA_UPDATER),true)
 	@# Include the build type in META/misc_info.txt so the server can easily differentiate production builds.
@@ -5698,9 +5788,6 @@
 ifeq ($(BUILDING_WITH_VSDK),true)
 	$(hide) echo "building_with_vsdk=true" >> $@
 endif
-ifeq ($(TARGET_FLATTEN_APEX),false)
-	$(hide) echo "target_flatten_apex=false" >> $@
-endif
 
 $(call declare-0p-target,$(INSTALLED_FASTBOOT_INFO_TARGET))
 
@@ -5737,11 +5824,11 @@
 
 # $(1): Directory to copy
 # $(2): Location to copy it to
-# The "ls -A" is to prevent "acp s/* d" from failing if s is empty.
+# The "ls -A" is to skip if $(1) is empty.
 define package_files-copy-root
   if [ -d "$(strip $(1))" -a "$$(ls -A $(1))" ]; then \
     mkdir -p $(2) && \
-    $(ACP) -rd $(strip $(1))/* $(2); \
+    $(ACP) -rd $(strip $(1))/. $(strip $(2))/; \
   fi
 endef
 
@@ -5902,17 +5989,19 @@
 # $(2): target out directory
 # $(3): image name to generate a map file. skip generating map file if empty
 define copy-image-and-generate-map
-  $(eval _supported_fs_for_map_file_generator := erofs ext%)
-  $(eval _img := $(call to-upper,$(3)))
-  $(if $(3),$(eval _map_fs_type := $(BOARD_$(_img)IMAGE_FILE_SYSTEM_TYPE)),\
-    $(eval _no_map_file := "true"))
-  $(if $(filter $(_supported_fs_for_map_file_generator),$(_map_fs_type))$(_no_map_file),\
-    mkdir -p $(2); \
-    cp $(1) $(2); \
-    $(if $(3),$(HOST_OUT_EXECUTABLES)/map_file_generator $(1) $(2)/$(3).map))
-  $(eval _img :=)
-  $(eval _map_fs_type :=)
-  $(eval _no_map_file :=)
+  $(if $(COPY_IMAGES_FOR_TARGET_FILES_ZIP), \
+    $(eval _supported_fs_for_map_file_generator := erofs ext%) \
+    $(eval _img := $(call to-upper,$(3))) \
+    $(if $(3),$(eval _map_fs_type := $(BOARD_$(_img)IMAGE_FILE_SYSTEM_TYPE)),\
+      $(eval _no_map_file := "true")) \
+    $(if $(filter $(_supported_fs_for_map_file_generator),$(_map_fs_type))$(_no_map_file),\
+      mkdir -p $(2); \
+      cp $(1) $(2); \
+      $(if $(3),$(HOST_OUT_EXECUTABLES)/map_file_generator $(1) $(2)/$(3).map)) \
+    $(eval _img :=) \
+    $(eval _map_fs_type :=) \
+    $(eval _no_map_file :=) \
+  )
 endef
 
 # By conditionally including the dependency of the target files package on the
@@ -5920,7 +6009,9 @@
 # image.
 ifdef BUILDING_SYSTEM_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(FULL_SYSTEMIMAGE_DEPS)
-  $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEMIMAGE)
+  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP
+    $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEMIMAGE)
+  endif
 else
   # releasetools may need the system build.prop even when building a
   # system-image-less product.
@@ -5933,7 +6024,9 @@
 
 ifdef BUILDING_SYSTEM_OTHER_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEMOTHERIMAGE_FILES)
-  $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEMOTHERIMAGE_TARGET)
+  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP
+    $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEMOTHERIMAGE_TARGET)
+  endif
 endif
 
 ifdef BUILDING_VENDOR_BOOT_IMAGE
@@ -5965,21 +6058,27 @@
 
 ifdef BUILDING_VENDOR_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDORIMAGE_FILES)
-  $(BUILT_TARGET_FILES_DIR): $(BUILT_VENDORIMAGE_TARGET)
+  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP
+    $(BUILT_TARGET_FILES_DIR): $(BUILT_VENDORIMAGE_TARGET)
+  endif
 else ifdef BOARD_PREBUILT_VENDORIMAGE
   $(BUILT_TARGET_FILES_DIR): $(INSTALLED_VENDORIMAGE_TARGET)
 endif
 
 ifdef BUILDING_PRODUCT_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(INTERNAL_PRODUCTIMAGE_FILES)
-  $(BUILT_TARGET_FILES_DIR): $(BUILT_PRODUCTIMAGE_TARGET)
+  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP
+    $(BUILT_TARGET_FILES_DIR): $(BUILT_PRODUCTIMAGE_TARGET)
+  endif
 else ifdef BOARD_PREBUILT_PRODUCTIMAGE
   $(BUILT_TARGET_FILES_DIR): $(INSTALLED_PRODUCTIMAGE_TARGET)
 endif
 
 ifdef BUILDING_SYSTEM_EXT_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEM_EXTIMAGE_FILES)
-  $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEM_EXTIMAGE_TARGET)
+  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP
+    $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEM_EXTIMAGE_TARGET)
+  endif
 else ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE
   $(BUILT_TARGET_FILES_DIR): $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)
 endif
@@ -5994,28 +6093,36 @@
 
 ifdef BUILDING_ODM_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(INTERNAL_ODMIMAGE_FILES)
-  $(BUILT_TARGET_FILES_DIR): $(BUILT_ODMIMAGE_TARGET)
+  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP
+    $(BUILT_TARGET_FILES_DIR): $(BUILT_ODMIMAGE_TARGET)
+  endif
 else ifdef BOARD_PREBUILT_ODMIMAGE
   $(BUILT_TARGET_FILES_DIR): $(INSTALLED_ODMIMAGE_TARGET)
 endif
 
 ifdef BUILDING_VENDOR_DLKM_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_DLKMIMAGE_FILES)
-  $(BUILT_TARGET_FILES_DIR): $(BUILT_VENDOR_DLKMIMAGE_TARGET)
+  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP
+    $(BUILT_TARGET_FILES_DIR): $(BUILT_VENDOR_DLKMIMAGE_TARGET)
+  endif
 else ifdef BOARD_PREBUILT_VENDOR_DLKMIMAGE
   $(BUILT_TARGET_FILES_DIR): $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)
 endif
 
 ifdef BUILDING_ODM_DLKM_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(INTERNAL_ODM_DLKMIMAGE_FILES)
-  $(BUILT_TARGET_FILES_DIR): $(BUILT_ODM_DLKMIMAGE_TARGET)
+  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP
+    $(BUILT_TARGET_FILES_DIR): $(BUILT_ODM_DLKMIMAGE_TARGET)
+  endif
 else ifdef BOARD_PREBUILT_ODM_DLKMIMAGE
   $(BUILT_TARGET_FILES_DIR): $(INSTALLED_ODM_DLKMIMAGE_TARGET)
 endif
 
 ifdef BUILDING_SYSTEM_DLKM_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEM_DLKMIMAGE_FILES)
-  $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEM_DLKMIMAGE_TARGET)
+  ifdef COPY_IMAGES_FOR_TARGET_FILES_ZIP
+    $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEM_DLKMIMAGE_TARGET)
+  endif
 else ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE
   $(BUILT_TARGET_FILES_DIR): $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)
 endif
@@ -6414,7 +6521,7 @@
 ifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),)
 	$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES
 	$(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
-	    $(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST),cp $(image) $(zip_root)/PREBUILT_IMAGES/;))
+	    $(foreach image,$(BOARD_$(call to-upper,$(partition))_IMAGE_LIST),cp $(image) $(zip_root)/PREBUILT_IMAGES/;))
 endif # BOARD_CUSTOMIMAGES_PARTITION_LIST
 	@# The radio images in BOARD_PACK_RADIOIMAGES will be additionally copied from RADIO/ into
 	@# IMAGES/, which then will be added into <product>-img.zip. Such images must be listed in
@@ -6781,7 +6888,7 @@
 ifeq (true,$(CLANG_COVERAGE))
   LLVM_PROFDATA := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-profdata
   LLVM_COV := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-cov
-  LIBCXX := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/lib/x86_64-unknown-linux-gnu/libc++.so.1
+  LIBCXX := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/lib/x86_64-unknown-linux-gnu/libc++.so
   # Use llvm-profdata.zip for backwards compatibility with tradefed code.
   LLVM_COVERAGE_TOOLS_ZIP := $(PRODUCT_OUT)/llvm-profdata.zip
 
@@ -6838,6 +6945,14 @@
 ifeq (,$(TARGET_BUILD_UNBUNDLED))
   $(JACOCO_REPORT_CLASSES_ALL): $(INTERNAL_ALLIMAGES_FILES)
 endif
+
+# This is not ideal, but it is difficult to correctly figure out the actual jacoco report
+# jars we need to add here as dependencies, so we add the device-tests as a dependency when
+# the env variable is set and this should guarantee thaat all the jacoco report jars are ready
+# when we package the final report jar here.
+ifeq ($(JACOCO_PACKAGING_INCLUDE_DEVICE_TESTS),true)
+  $(JACOCO_REPORT_CLASSES_ALL): $(COMPATIBILITY.device-tests.FILES)
+endif
 endif # EMMA_INSTRUMENT=true
 
 
@@ -6849,29 +6964,34 @@
 # finding the appropriate dictionary to deobfuscate a stack trace frame.
 #
 
-# The path to the zip file containing proguard dictionaries.
-PROGUARD_DICT_ZIP := $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict.zip
-# The path to the zip file containing mappings from dictionary hashes to filenames.
-PROGUARD_DICT_MAPPING := $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict-mapping.textproto
-.KATI_READONLY := PROGUARD_DICT_ZIP PROGUARD_DICT_MAPPING
-# For apps_only build we'll establish the dependency later in build/make/core/main.mk.
 ifeq (,$(TARGET_BUILD_UNBUNDLED))
-$(PROGUARD_DICT_ZIP): $(INTERNAL_ALLIMAGES_FILES) $(updater_dep)
+  _proguard_dict_zip_modules := $(call product-installed-modules,$(INTERNAL_PRODUCT))
+else
+  _proguard_dict_zip_modules := $(unbundled_build_modules)
 endif
-$(PROGUARD_DICT_ZIP): PRIVATE_PACKAGING_DIR := $(call intermediates-dir-for,PACKAGING,proguard_dictionary)
-$(PROGUARD_DICT_ZIP): PRIVATE_MAPPING_PACKAGING_DIR := $(call intermediates-dir-for,PACKAGING,proguard_dictionary_mapping)
-$(PROGUARD_DICT_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,proguard_dictionary_filelist)/filelist
-$(PROGUARD_DICT_ZIP): $(SOONG_ZIP) $(SYMBOLS_MAP)
+
+# The path to the zip file containing proguard dictionaries.
+PROGUARD_DICT_ZIP :=$= $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict.zip
+$(PROGUARD_DICT_ZIP): PRIVATE_SOONG_ZIP_ARGUMENTS := $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_DICTIONARY_SOONG_ZIP_ARGUMENTS))
+$(PROGUARD_DICT_ZIP): $(SOONG_ZIP) $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_DICTIONARY_FILES))
 	@echo "Packaging Proguard obfuscation dictionary files."
-	rm -rf $@ $(PRIVATE_LIST_FILE)
-	mkdir -p $(PRIVATE_PACKAGING_DIR) $(PRIVATE_MAPPING_PACKAGING_DIR) $(dir $(PRIVATE_LIST_FILE))
-	# Zip all of the files in the proguard dictionary directory.
-	$(SOONG_ZIP) --ignore_missing_files -d -o $@ -C $(PRIVATE_PACKAGING_DIR) -D $(PRIVATE_PACKAGING_DIR)
-	# Find all of the files in the proguard dictionary mapping directory and merge them into the mapping textproto.
-	# Strip the PRIVATE_PACKAGING_DIR off the filenames to match soong_zip's -C argument.
-	$(hide) find -L $(PRIVATE_MAPPING_PACKAGING_DIR) -type f | sort >$(PRIVATE_LIST_FILE)
-	$(SYMBOLS_MAP) -merge $(PROGUARD_DICT_MAPPING) -strip_prefix $(PRIVATE_PACKAGING_DIR)/ -ignore_missing_files @$(PRIVATE_LIST_FILE)
-$(PROGUARD_DICT_ZIP): .KATI_IMPLICIT_OUTPUTS := $(PROGUARD_DICT_MAPPING)
+	# Zip all of the files in PROGUARD_DICTIONARY_FILES.
+	echo -n > $@.tmparglist
+	$(foreach arg,$(PRIVATE_SOONG_ZIP_ARGUMENTS),printf "%s\n" "$(arg)" >> $@.tmparglist$(newline))
+	$(SOONG_ZIP) -d -o $@ @$@.tmparglist
+	rm -f $@.tmparglist
+
+# The path to the zip file containing mappings from dictionary hashes to filenames.
+PROGUARD_DICT_MAPPING :=$= $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict-mapping.textproto
+_proguard_dict_mapping_files := $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_DICTIONARY_MAPPING))
+$(PROGUARD_DICT_MAPPING): PRIVATE_MAPPING_FILES := $(_proguard_dict_mapping_files)
+$(PROGUARD_DICT_MAPPING): $(SYMBOLS_MAP) $(_proguard_dict_mapping_files)
+	@echo "Packaging Proguard obfuscation dictionary mapping files."
+	# Merge all the mapping files together
+	echo -n > $@.tmparglist
+	$(foreach mf,$(PRIVATE_MAPPING_FILES),echo "$(mf)" >> $@.tmparglist$(newline))
+	$(SYMBOLS_MAP) -merge $(PROGUARD_DICT_MAPPING) @$@.tmparglist
+	rm -f $@.tmparglist
 
 $(call declare-1p-container,$(PROGUARD_DICT_ZIP),)
 ifeq (,$(TARGET_BUILD_UNBUNDLED))
@@ -6881,31 +7001,19 @@
 #------------------------------------------------------------------
 # A zip of Proguard usage files.
 #
-PROGUARD_USAGE_ZIP := $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-usage.zip
-# For apps_only build we'll establish the dependency later in build/make/core/main.mk.
-ifeq (,$(TARGET_BUILD_UNBUNDLED))
-$(PROGUARD_USAGE_ZIP): \
-    $(INSTALLED_SYSTEMIMAGE_TARGET) \
-    $(INSTALLED_RAMDISK_TARGET) \
-    $(INSTALLED_BOOTIMAGE_TARGET) \
-    $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \
-    $(INSTALLED_USERDATAIMAGE_TARGET) \
-    $(INSTALLED_VENDORIMAGE_TARGET) \
-    $(INSTALLED_PRODUCTIMAGE_TARGET) \
-    $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \
-    $(INSTALLED_ODMIMAGE_TARGET) \
-    $(INSTALLED_VENDOR_DLKMIMAGE_TARGET) \
-    $(INSTALLED_ODM_DLKMIMAGE_TARGET) \
-    $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \
-    $(updater_dep)
-endif
-$(PROGUARD_USAGE_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,proguard_usage.zip)/filelist
-$(PROGUARD_USAGE_ZIP): PRIVATE_PACKAGING_DIR := $(call intermediates-dir-for,PACKAGING,proguard_usage)
-$(PROGUARD_USAGE_ZIP): $(MERGE_ZIPS)
+PROGUARD_USAGE_ZIP :=$= $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-usage.zip
+_proguard_usage_zips := $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_USAGE_ZIP))
+$(PROGUARD_USAGE_ZIP): PRIVATE_ZIPS := $(_proguard_usage_zips)
+$(PROGUARD_USAGE_ZIP): $(MERGE_ZIPS) $(_proguard_usage_zips)
 	@echo "Packaging Proguard usage files."
-	mkdir -p $(dir $@) $(PRIVATE_PACKAGING_DIR) $(dir $(PRIVATE_LIST_FILE))
-	find $(PRIVATE_PACKAGING_DIR) -name proguard_usage.zip > $(PRIVATE_LIST_FILE)
-	$(MERGE_ZIPS) $@ @$(PRIVATE_LIST_FILE)
+	echo -n > $@.tmparglist
+	$(foreach z,$(PRIVATE_ZIPS),echo "$(z)" >> $@.tmparglist$(newline))
+	$(MERGE_ZIPS) $@ @$@.tmparglist
+	rm -rf $@.tmparglist
+
+_proguard_dict_mapping_files :=
+_proguard_usage_zips :=
+_proguard_dict_zip_modules :=
 
 $(call declare-1p-container,$(PROGUARD_USAGE_ZIP),)
 ifeq (,$(TARGET_BUILD_UNBUNDLED))
@@ -7067,6 +7175,7 @@
 	PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$(dir $(ZIP2ZIP)):$$PATH \
 	    $(IMG_FROM_TARGET_FILES) \
 	        --additional IMAGES/VerifiedBootParams.textproto:VerifiedBootParams.textproto \
+	        --build_super_image $(BUILD_SUPER_IMAGE) \
 	        $(BUILT_TARGET_FILES_PACKAGE) $@
 
 $(call declare-1p-container,$(INTERNAL_UPDATE_PACKAGE_TARGET),)
@@ -7412,6 +7521,9 @@
 haiku-rust: $(SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_RUST_FUZZ_TARGETS)
 $(call dist-for-goals,haiku-rust,$(SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES))
 $(call dist-for-goals,haiku-rust,$(PRODUCT_OUT)/module-info.json)
+.PHONY: haiku-presubmit
+haiku-presubmit: $(SOONG_PRESUBMIT_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_PRESUBMIT_FUZZ_TARGETS)
+$(call dist-for-goals,haiku-presubmit,$(SOONG_PRESUBMIT_FUZZ_PACKAGING_ARCH_MODULES))
 
 # -----------------------------------------------------------------
 # Extract platform fonts used in Layoutlib
diff --git a/core/aapt2.mk b/core/aapt2.mk
index 7b17df4..0e23477 100644
--- a/core/aapt2.mk
+++ b/core/aapt2.mk
@@ -68,6 +68,8 @@
 # support for it.
 my_static_library_resources := $(foreach l, $(call reverse-list,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),\
   $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/package-res.apk)
+my_static_library_transitive_resource_packages_lists := $(foreach l, $(call reverse-list,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),\
+  $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/transitive-res-packages)
 my_static_library_extra_packages := $(foreach l, $(call reverse-list,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES)),\
   $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/extra_packages)
 my_shared_library_resources := $(foreach l, $(LOCAL_SHARED_ANDROID_LIBRARIES),\
@@ -95,6 +97,7 @@
 $(my_res_package): PRIVATE_JAVA_GEN_DIR := $(intermediates.COMMON)/aapt2
 $(my_res_package): PRIVATE_SRCJAR := $(my_srcjar)
 $(my_res_package): PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES := $(my_static_library_extra_packages)
+$(my_res_package): PRIVATE_STATIC_LIBRARY_TRANSITIVE_RES_PACKAGES_LISTS := $(my_static_library_transitive_resource_packages_lists)
 $(my_res_package): PRIVATE_AAPT_EXTRA_PACKAGES := $(aapt_extra_packages)
 $(my_res_package): .KATI_IMPLICIT_OUTPUTS := $(my_srcjar) $(aapt_extra_packages)
 
@@ -117,7 +120,7 @@
 $(my_res_package): .KATI_IMPLICIT_OUTPUTS += $(proguard_options_file)
 endif
 
-$(my_res_package): $(full_android_manifest) $(my_static_library_resources) $(my_shared_library_resources)
+$(my_res_package): $(full_android_manifest) $(my_static_library_resources) $(my_static_library_transitive_resource_packages_lists) $(my_shared_library_resources)
 $(my_res_package): $(my_full_asset_paths)
 $(my_res_package): $(my_res_resources_flat) $(my_overlay_resources_flat) \
   $(my_resources_flata) $(my_static_library_resources) $(my_static_library_extra_packages) \
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index f132d13..66d96b1 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -29,10 +29,6 @@
 $(call add_soong_config_var,ANDROID,TARGET_DYNAMIC_64_32_MEDIASERVER)
 $(call add_soong_config_var,ANDROID,TARGET_DYNAMIC_64_32_DRMSERVER)
 $(call add_soong_config_var,ANDROID,TARGET_ENABLE_MEDIADRM_64)
-$(call add_soong_config_var,ANDROID,IS_TARGET_MIXED_SEPOLICY)
-ifeq ($(IS_TARGET_MIXED_SEPOLICY),true)
-$(call add_soong_config_var_value,ANDROID,MIXED_SEPOLICY_VERSION,$(BOARD_SEPOLICY_VERS))
-endif
 $(call add_soong_config_var,ANDROID,BOARD_USES_ODMIMAGE)
 $(call add_soong_config_var,ANDROID,BOARD_USES_RECOVERY_AS_BOOT)
 $(call add_soong_config_var,ANDROID,PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT)
@@ -158,6 +154,8 @@
 $(call add_soong_config_var_value,ANDROID,avf_kernel_modules_enabled,$(PRODUCT_AVF_KERNEL_MODULES_ENABLED))
 endif
 
+$(call add_soong_config_var_value,ANDROID,release_avf_enable_multi_tenant_microdroid_vm,$(RELEASE_AVF_ENABLE_MULTI_TENANT_MICRODROID_VM))
+
 # Enable system_server optimizations by default unless explicitly set or if
 # there may be dependent runtime jars.
 # TODO(b/240588226): Remove the off-by-default exceptions after handling
diff --git a/core/app_prebuilt_internal.mk b/core/app_prebuilt_internal.mk
index 9fab44d..b141a98 100644
--- a/core/app_prebuilt_internal.mk
+++ b/core/app_prebuilt_internal.mk
@@ -227,7 +227,7 @@
 $(built_module): PRIVATE_EMBEDDED_JNI_LIBS := $(embedded_prebuilt_jni_libs)
 
 ifdef LOCAL_COMPRESSED_MODULE
-$(built_module) : $(MINIGZIP)
+$(built_module) : $(GZIP)
 endif
 
 ifeq ($(module_run_appcompat),true)
@@ -305,4 +305,4 @@
 ###########################################################
 ## SBOM generation
 ###########################################################
-include $(BUILD_SBOM_GEN)
\ No newline at end of file
+include $(BUILD_SBOM_GEN)
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 9ad1cc5..b3c5b94 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -578,7 +578,9 @@
 
       ALL_VINTF_MANIFEST_FRAGMENTS_LIST += $(my_vintf_new_pairs)
 
-      $(my_all_targets) : $(my_vintf_new_installed)
+      $(my_all_targets) : $(my_vintf_installed)
+      # Install fragments together with the target
+      $(LOCAL_INSTALLED_MODULE) : | $(my_vintf_installed)
     endif # my_vintf_fragments
 
     # Rule to install the module's companion init.rc.
@@ -615,6 +617,8 @@
       ALL_INIT_RC_INSTALLED_PAIRS += $(my_init_rc_new_pairs)
 
       $(my_all_targets) : $(my_init_rc_installed)
+      # Install init_rc together with the target
+      $(LOCAL_INSTALLED_MODULE) : | $(my_init_rc_installed)
     endif # my_init_rc
 
   endif # !LOCAL_IS_HOST_MODULE
@@ -961,6 +965,9 @@
       $(my_init_rc_installed) \
       $(my_installed_test_data) \
       $(my_vintf_installed))
+
+  ALL_MODULES.$(my_register_name).INSTALLED_SYMLINKS := $(LOCAL_SOONG_INSTALL_SYMLINKS)
+
   # Store the list of colon-separated pairs of the built and installed locations
   # of files provided by this module.  Used by custom packaging rules like
   # package-modules.mk that need to copy the built files to a custom install
@@ -975,6 +982,11 @@
       $(my_init_rc_pairs) \
       $(my_test_data_pairs) \
       $(my_vintf_pairs))
+  # Store the list of vintf/init_rc as order-only dependencies
+  ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED := \
+    $(strip $(ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED) \
+      $(my_init_rc_installed) \
+      $(my_vintf_installed))
 else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
   ALL_MODULES.$(my_register_name).INSTALLED := \
     $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \
@@ -984,6 +996,10 @@
     $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \
     $(LOCAL_BUILT_MODULE):$(LOCAL_INSTALLED_MODULE) \
     $(my_init_rc_pairs) $(my_test_data_pairs) $(my_vintf_pairs))
+  ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED := \
+    $(strip $(ALL_MODULES.$(my_register_name).ORDERONLY_INSTALLED) \
+      $(my_init_rc_installed) \
+      $(my_vintf_installed))
 endif
 ifdef LOCAL_PICKUP_FILES
 # Files or directories ready to pick up by the build system
@@ -1045,6 +1061,10 @@
   $(ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS) \
   $(filter-out $(ALL_MODULES.$(my_register_name).SUPPORTED_VARIANTS),$(my_supported_variant))
 
+ALL_MODULES.$(my_register_name).ACONFIG_FILES := \
+    $(ALL_MODULES.$(my_register_name).ACONFIG_FILES) $(LOCAL_ACONFIG_FILES)
+
+
 ##########################################################################
 ## When compiling against API imported module, use API import stub
 ## libraries.
diff --git a/core/board_config.mk b/core/board_config.mk
index 663ec7c..2699512 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -144,9 +144,6 @@
 _board_strip_list += BOARD_AVB_PVMFW_ALGORITHM
 _board_strip_list += BOARD_AVB_PVMFW_ROLLBACK_INDEX_LOCATION
 _board_strip_list += BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST
-_board_strip_list += BOARD_BPT_DISK_SIZE
-_board_strip_list += BOARD_BPT_INPUT_FILES
-_board_strip_list += BOARD_BPT_MAKE_TABLE_ARGS
 _board_strip_list += BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION
 _board_strip_list += BOARD_AVB_VBMETA_VENDOR_ALGORITHM
 _board_strip_list += BOARD_AVB_VBMETA_VENDOR_KEY_PATH
@@ -174,7 +171,6 @@
 
 
 _build_broken_var_list := \
-  BUILD_BROKEN_PLUGIN_VALIDATION \
   BUILD_BROKEN_CLANG_PROPERTY \
   BUILD_BROKEN_CLANG_ASFLAGS \
   BUILD_BROKEN_CLANG_CFLAGS \
@@ -189,7 +185,6 @@
   BUILD_BROKEN_PREBUILT_ELF_FILES \
   BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW \
   BUILD_BROKEN_USES_NETWORK \
-  BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES \
   BUILD_BROKEN_VENDOR_PROPERTY_NAMESPACE \
   BUILD_BROKEN_VINTF_PRODUCT_COPY_FILES \
 
@@ -976,24 +971,6 @@
 endif
 TARGET_VENDOR_TEST_SUFFIX := /vendor
 
-###########################################
-# APEXes are by default not flattened, i.e. updatable.
-#
-# APEX flattening can also be forcibly enabled (resp. disabled) by
-# setting OVERRIDE_TARGET_FLATTEN_APEX to true (resp. false), e.g. by
-# setting the OVERRIDE_TARGET_FLATTEN_APEX environment variable.
-ifdef OVERRIDE_TARGET_FLATTEN_APEX
-  TARGET_FLATTEN_APEX := $(OVERRIDE_TARGET_FLATTEN_APEX)
-endif
-
-# TODO(b/278826656) Remove the following message
-ifeq (true,$(TARGET_FLATTEN_APEX))
-  $(warning ********************************************************************************)
-  $(warning Flattened APEX will be deprecated soon. Please stop using flattened APEX and use)
-  $(warning "image" APEX instead.)
-  $(warning ********************************************************************************)
-endif
-
 ifeq (,$(TARGET_BUILD_UNBUNDLED))
 ifdef PRODUCT_EXTRA_VNDK_VERSIONS
   $(foreach v,$(PRODUCT_EXTRA_VNDK_VERSIONS),$(call check_vndk_version,$(v)))
diff --git a/core/build_id.mk b/core/build_id.mk
index ba5ca42..4c2c7fa 100644
--- a/core/build_id.mk
+++ b/core/build_id.mk
@@ -18,4 +18,4 @@
 # (like "CRB01").  It must be a single word, and is
 # capitalized by convention.
 
-BUILD_ID=AOSP.MASTER
+BUILD_ID=AOSP.MAIN
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index bb7ba1b..409e559 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -9,6 +9,7 @@
 LOCAL_AAPT_FLAGS:=
 LOCAL_AAPT_INCLUDE_ALL_RESOURCES:=
 LOCAL_AAPT_NAMESPACES:=
+LOCAL_ACONFIG_FILES:=
 LOCAL_ADDITIONAL_CERTIFICATES:=
 LOCAL_ADDITIONAL_CHECKED_MODULE:=
 LOCAL_ADDITIONAL_DEPENDENCIES:=
@@ -297,6 +298,7 @@
 LOCAL_SOONG_PROGUARD_DICT :=
 LOCAL_SOONG_PROGUARD_USAGE_ZIP :=
 LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=
+LOCAL_SOONG_TRANSITIVE_RES_PACKAGES :=
 LOCAL_SOONG_DEVICE_RRO_DIRS :=
 LOCAL_SOONG_PRODUCT_RRO_DIRS :=
 LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=
diff --git a/core/config.mk b/core/config.mk
index c166ef7..8ff85cf 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -342,6 +342,23 @@
 JAVA_TMPDIR_ARG :=
 endif
 
+# These build broken variables are intended to be set in a buildspec file,
+# while other build broken flags are expected to be set in a board config.
+# These are build broken variables that are expected to apply across board
+# configs, generally for cross-cutting features.
+
+# Build broken variables that should be treated as booleans
+_build_broken_bool_vars := \
+  BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES \
+
+# Build broken variables that should be treated as lists
+_build_broken_list_vars := \
+  BUILD_BROKEN_PLUGIN_VALIDATION \
+
+_build_broken_var_names := $(_build_broken_bool_vars)
+_build_broken_var_names += $(_build_broken_list_vars)
+$(foreach v,$(_build_broken_var_names),$(eval $(v) :=))
+
 # ###############################################################
 # Include sub-configuration files
 # ###############################################################
@@ -371,6 +388,13 @@
 # are specific to the user's build configuration.
 include $(BUILD_SYSTEM)/envsetup.mk
 
+
+$(foreach var,$(_build_broken_bool_vars), \
+  $(if $(filter-out true false,$($(var))), \
+    $(error Valid values of $(var) are "true", "false", and "". Not "$($(var))")))
+
+.KATI_READONLY := $(_build_broken_var_names)
+
 # Returns true if it is a low memory device, otherwise it returns false.
 define is-low-mem-device
 $(if $(findstring ro.config.low_ram=true,$(PRODUCT_PROPERTY_OVERRIDES)),true,\
@@ -384,23 +408,8 @@
 $(if $(findstring ro.config.low_ram=true,$(PRODUCT_ODM_PROPERTIES)),true,false)))))))))
 endef
 
-# Get the board API level.
-board_api_level := $(PLATFORM_SDK_VERSION)
-ifdef BOARD_API_LEVEL
-  board_api_level := $(BOARD_API_LEVEL)
-else ifdef BOARD_SHIPPING_API_LEVEL
-  # Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level.
-  board_api_level := $(BOARD_SHIPPING_API_LEVEL)
-endif
-
-# Calculate the VSR vendor API level.
-vsr_vendor_api_level := $(board_api_level)
-
-ifdef PRODUCT_SHIPPING_API_LEVEL
-  vsr_vendor_api_level := $(call math_min,$(PRODUCT_SHIPPING_API_LEVEL),$(board_api_level))
-endif
-
 # Set TARGET_MAX_PAGE_SIZE_SUPPORTED.
+# TARGET_MAX_PAGE_SIZE_SUPPORTED indicates the alignment of the ELF segments.
 ifdef PRODUCT_MAX_PAGE_SIZE_SUPPORTED
   TARGET_MAX_PAGE_SIZE_SUPPORTED := $(PRODUCT_MAX_PAGE_SIZE_SUPPORTED)
 else ifeq ($(strip $(call is-low-mem-device)),true)
@@ -410,12 +419,34 @@
   # The default binary alignment for userspace is 4096.
   TARGET_MAX_PAGE_SIZE_SUPPORTED := 4096
   # When VSR vendor API level >= 34, binary alignment will be 65536.
-  ifeq ($(call math_gt_or_eq,$(vsr_vendor_api_level),34),true)
+  ifeq ($(call math_gt_or_eq,$(VSR_VENDOR_API_LEVEL),34),true)
+    ifeq ($(TARGET_ARCH),arm64)
       TARGET_MAX_PAGE_SIZE_SUPPORTED := 65536
+    endif
   endif
 endif
 .KATI_READONLY := TARGET_MAX_PAGE_SIZE_SUPPORTED
 
+# Only arm64 arch supports TARGET_MAX_PAGE_SIZE_SUPPORTED greater than 4096.
+ifneq ($(TARGET_MAX_PAGE_SIZE_SUPPORTED),4096)
+  ifneq ($(TARGET_ARCH),arm64)
+    $(error TARGET_MAX_PAGE_SIZE_SUPPORTED=$(TARGET_MAX_PAGE_SIZE_SUPPORTED) is greater than 4096. Only supported in arm64 arch)
+  endif
+endif
+
+# Boolean variable determining if AOSP is page size agnostic. This means
+# that AOSP can use a kernel configured with 4k/16k/64k PAGE SIZES.
+TARGET_PAGE_SIZE_AGNOSTIC := false
+ifdef PRODUCT_PAGE_SIZE_AGNOSTIC
+  TARGET_PAGE_SIZE_AGNOSTIC := $(PRODUCT_PAGE_SIZE_AGNOSTIC)
+  ifeq ($(TARGET_PAGE_SIZE_AGNOSTIC),true)
+      ifneq ($(TARGET_MAX_PAGE_SIZE_SUPPORTED),65536)
+          $(error TARGET_MAX_PAGE_SIZE_SUPPORTED has to be 65536 to support page size agnostic)
+      endif
+  endif
+endif
+.KATI_READONLY := TARGET_PAGE_SIZE_AGNOSTIC
+
 # Pruned directory options used when using findleaves.py
 # See envsetup.mk for a description of SCAN_EXCLUDE_DIRS
 FIND_LEAVES_EXCLUDES := $(addprefix --prune=, $(SCAN_EXCLUDE_DIRS) .repo .git)
@@ -625,6 +656,7 @@
 FILESLIST_UTIL :=$= build/make/tools/fileslist_util.py
 HOST_INIT_VERIFIER := $(HOST_OUT_EXECUTABLES)/host_init_verifier
 XMLLINT := $(HOST_OUT_EXECUTABLES)/xmllint
+ACONFIG := $(HOST_OUT_EXECUTABLES)/aconfig
 
 # SOONG_ZIP is exported by Soong, but needs to be defined early for
 # $OUT/dexpreopt.global.  It will be verified against the Soong version.
@@ -645,10 +677,11 @@
 # For non-supported hosts, do not generate breakpad symbols.
 BREAKPAD_GENERATE_SYMBOLS := false
 endif
+GZIP := prebuilts/build-tools/path/$(BUILD_OS)-$(HOST_PREBUILT_ARCH)/gzip
 PROTOC := $(HOST_OUT_EXECUTABLES)/aprotoc$(HOST_EXECUTABLE_SUFFIX)
 NANOPB_SRCS := $(HOST_OUT_EXECUTABLES)/protoc-gen-nanopb
 MKBOOTFS := $(HOST_OUT_EXECUTABLES)/mkbootfs$(HOST_EXECUTABLE_SUFFIX)
-MINIGZIP := $(HOST_OUT_EXECUTABLES)/minigzip$(HOST_EXECUTABLE_SUFFIX)
+MINIGZIP := $(GZIP)
 LZ4 := $(HOST_OUT_EXECUTABLES)/lz4$(HOST_EXECUTABLE_SUFFIX)
 GENERATE_GKI_CERTIFICATE := $(HOST_OUT_EXECUTABLES)/generate_gki_certificate$(HOST_EXECUTABLE_SUFFIX)
 ifeq (,$(strip $(BOARD_CUSTOM_MKBOOTIMG)))
@@ -691,6 +724,7 @@
 IMG_FROM_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/img_from_target_files$(HOST_EXECUTABLE_SUFFIX)
 MAKE_RECOVERY_PATCH := $(HOST_OUT_EXECUTABLES)/make_recovery_patch$(HOST_EXECUTABLE_SUFFIX)
 OTA_FROM_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/ota_from_target_files$(HOST_EXECUTABLE_SUFFIX)
+OTA_FROM_RAW_IMG := $(HOST_OUT_EXECUTABLES)/ota_from_raw_img$(HOST_EXECUTABLE_SUFFIX)
 SPARSE_IMG := $(HOST_OUT_EXECUTABLES)/sparse_img$(HOST_EXECUTABLE_SUFFIX)
 CHECK_PARTITION_SIZES := $(HOST_OUT_EXECUTABLES)/check_partition_sizes$(HOST_EXECUTABLE_SUFFIX)
 SYMBOLS_MAP := $(HOST_OUT_EXECUTABLES)/symbols_map
@@ -722,7 +756,7 @@
 # Path to tools.jar
 HOST_JDK_TOOLS_JAR := $(ANDROID_JAVA8_HOME)/lib/tools.jar
 
-APICHECK_COMMAND := $(JAVA) -Xmx4g -jar $(APICHECK) --no-banner
+APICHECK_COMMAND := $(JAVA) -Xmx4g -jar $(APICHECK)
 
 # Boolean variable determining if the allow list for compatible properties is enabled
 PRODUCT_COMPATIBLE_PROPERTY := true
@@ -777,6 +811,9 @@
 
 requirements :=
 
+# Set default value of KEEP_VNDK.
+KEEP_VNDK ?= true
+
 # BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED can be true only if early-mount of
 # partitions is supported. But the early-mount must be supported for full
 # treble products, and so BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED should be set
@@ -884,22 +921,15 @@
 BOARD_SEPOLICY_VERS := $(PLATFORM_SEPOLICY_VERSION)
 endif
 
-ifeq ($(BOARD_SEPOLICY_VERS),$(PLATFORM_SEPOLICY_VERSION))
-IS_TARGET_MIXED_SEPOLICY :=
-else
-IS_TARGET_MIXED_SEPOLICY := true
-endif
-
-.KATI_READONLY := IS_TARGET_MIXED_SEPOLICY
-
 # A list of SEPolicy versions, besides PLATFORM_SEPOLICY_VERSION, that the framework supports.
-PLATFORM_SEPOLICY_COMPAT_VERSIONS := \
+PLATFORM_SEPOLICY_COMPAT_VERSIONS := $(filter-out $(PLATFORM_SEPOLICY_VERSION), \
     29.0 \
     30.0 \
     31.0 \
     32.0 \
     33.0 \
     34.0 \
+    )
 
 .KATI_READONLY := \
     PLATFORM_SEPOLICY_COMPAT_VERSIONS \
diff --git a/core/config_sanitizers.mk b/core/config_sanitizers.mk
index 7fa190f..82b17be 100644
--- a/core/config_sanitizers.mk
+++ b/core/config_sanitizers.mk
@@ -413,7 +413,6 @@
     my_cflags += -fvisibility=default
   endif
   my_ldflags += $(CFI_EXTRA_LDFLAGS)
-  my_arflags += --plugin $(LLVM_PREBUILTS_PATH)/../lib64/LLVMgold.so
 
   ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)
         my_ldflags := $(filter-out -fsanitize-cfi-cross-dso,$(my_ldflags))
diff --git a/core/definitions.mk b/core/definitions.mk
index be40584..0777904 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -1547,10 +1547,10 @@
 #
 # You must call this with $(eval).
 define define-aidl-java-rule
-define-aidl-java-rule-src := $(patsubst %.aidl,%.java,$(subst ../,dotdot/,$(addprefix $(2)/,$(1))))
-$$(define-aidl-java-rule-src) : $(call clean-path,$(LOCAL_PATH)/$(1)) $(AIDL)
+define_aidl_java_rule_src := $(patsubst %.aidl,%.java,$(subst ../,dotdot/,$(addprefix $(2)/,$(1))))
+$$(define_aidl_java_rule_src) : $(call clean-path,$(LOCAL_PATH)/$(1)) $(AIDL)
 	$$(transform-aidl-to-java)
-$(3) += $$(define-aidl-java-rule-src)
+$(3) += $$(define_aidl_java_rule_src)
 endef
 
 ## Given a .aidl file path generate the rule to compile it a .cpp file.
@@ -1560,10 +1560,10 @@
 #
 # You must call this with $(eval).
 define define-aidl-cpp-rule
-define-aidl-cpp-rule-src := $(patsubst %.aidl,%$(LOCAL_CPP_EXTENSION),$(subst ../,dotdot/,$(addprefix $(2)/,$(1))))
-$$(define-aidl-cpp-rule-src) : $(call clean-path,$(LOCAL_PATH)/$(1)) $(AIDL_CPP)
+define_aidl_cpp_rule_src := $(patsubst %.aidl,%$(LOCAL_CPP_EXTENSION),$(subst ../,dotdot/,$(addprefix $(2)/,$(1))))
+$$(define_aidl_cpp_rule_src) : $(call clean-path,$(LOCAL_PATH)/$(1)) $(AIDL_CPP)
 	$$(transform-aidl-to-cpp)
-$(3) += $$(define-aidl-cpp-rule-src)
+$(3) += $$(define_aidl_cpp_rule_src)
 endef
 
 ###########################################################
@@ -1585,10 +1585,10 @@
 #
 # You must call this with $(eval).
 define define-vts-cpp-rule
-define-vts-cpp-rule-src := $(patsubst %.vts,%$(LOCAL_CPP_EXTENSION),$(subst ../,dotdot/,$(addprefix $(2)/,$(1))))
-$$(define-vts-cpp-rule-src) : $(LOCAL_PATH)/$(1) $(VTSC)
+define_vts_cpp_rule_src := $(patsubst %.vts,%$(LOCAL_CPP_EXTENSION),$(subst ../,dotdot/,$(addprefix $(2)/,$(1))))
+$$(define_vts_cpp_rule_src) : $(LOCAL_PATH)/$(1) $(VTSC)
 	$$(transform-vts-to-cpp)
-$(3) += $$(define-vts-cpp-rule-src)
+$(3) += $$(define_vts_cpp_rule_src)
 endef
 
 ###########################################################
@@ -2377,6 +2377,7 @@
 mkdir -p $(PRIVATE_JAVA_GEN_DIR)
 $(call dump-words-to-file,$(PRIVATE_RES_FLAT),$(dir $@)aapt2-flat-list)
 $(call dump-words-to-file,$(PRIVATE_OVERLAY_FLAT),$(dir $@)aapt2-flat-overlay-list)
+cat $(PRIVATE_STATIC_LIBRARY_TRANSITIVE_RES_PACKAGES_LISTS) | sort -u | tr '\n' ' ' > $(dir $@)aapt2-transitive-overlay-list
 $(hide) $(AAPT2) link -o $@ \
   $(PRIVATE_AAPT_FLAGS) \
   $(if $(PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES),$$(cat $(PRIVATE_STATIC_LIBRARY_EXTRA_PACKAGES))) \
@@ -2396,6 +2397,7 @@
   $(addprefix --rename-manifest-package ,$(PRIVATE_MANIFEST_PACKAGE_NAME)) \
   $(addprefix --rename-instrumentation-target-package ,$(PRIVATE_MANIFEST_INSTRUMENTATION_FOR)) \
   -R \@$(dir $@)aapt2-flat-overlay-list \
+  -R \@$(dir $@)aapt2-transitive-overlay-list \
   \@$(dir $@)aapt2-flat-list
 $(SOONG_ZIP) -o $(PRIVATE_SRCJAR) -C $(PRIVATE_JAVA_GEN_DIR) -D $(PRIVATE_JAVA_GEN_DIR)
 $(EXTRACT_JAR_PACKAGES) -i $(PRIVATE_SRCJAR) -o $(PRIVATE_AAPT_EXTRA_PACKAGES) --prefix '--extra-packages '
@@ -2941,7 +2943,7 @@
 define compress-package
 $(hide) \
   mv $@ $@.uncompressed; \
-  $(MINIGZIP) -9 -c $@.uncompressed > $@.compressed; \
+  $(GZIP) -9 -c $@.uncompressed > $@.compressed; \
   rm -f $@.uncompressed; \
   mv $@.compressed $@;
 endef
@@ -3201,8 +3203,9 @@
 define copy-vintf-manifest-checked
 $(2): $(1) $(HOST_OUT_EXECUTABLES)/assemble_vintf
 	@echo "Copy xml: $$@"
-	$(hide) $(HOST_OUT_EXECUTABLES)/assemble_vintf -i $$< >/dev/null  # Don't print the xml file to stdout.
-	$$(copy-file-to-target)
+	$(hide) mkdir -p "$$(dir $$@)"
+	$(hide) VINTF_IGNORE_TARGET_FCM_VERSION=true\
+		$(HOST_OUT_EXECUTABLES)/assemble_vintf -i $$< -o $$@
 endef
 
 # Copies many vintf manifest files checked.
@@ -3425,16 +3428,6 @@
 .KATI_RESTAT: $(2)
 endef
 
-# Returns the directory to copy proguard dictionaries into
-define local-proguard-dictionary-directory
-$(call intermediates-dir-for,PACKAGING,proguard_dictionary)/out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates
-endef
-
-# Returns the directory to copy proguard dictionary mappings into
-define local-proguard-dictionary-mapping-directory
-$(call intermediates-dir-for,PACKAGING,proguard_dictionary_mapping)/out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates
-endef
-
 
 ###########################################################
 ## Commands to call R8
diff --git a/core/dex_preopt_config.mk b/core/dex_preopt_config.mk
index 7b9c4db..6739459 100644
--- a/core/dex_preopt_config.mk
+++ b/core/dex_preopt_config.mk
@@ -111,6 +111,7 @@
   $(call add_json_list, BootJars,                                $(PRODUCT_BOOT_JARS))
   $(call add_json_list, ApexBootJars,                            $(PRODUCT_APEX_BOOT_JARS))
   $(call add_json_list, ArtApexJars,                             $(filter $(PRODUCT_BOOT_JARS),$(ART_APEX_JARS)))
+  $(call add_json_list, TestOnlyArtBootImageJars,                $(PRODUCT_TEST_ONLY_ART_BOOT_IMAGE_JARS))
   $(call add_json_list, SystemServerJars,                        $(PRODUCT_SYSTEM_SERVER_JARS))
   $(call add_json_list, SystemServerApps,                        $(PRODUCT_SYSTEM_SERVER_APPS))
   $(call add_json_list, ApexSystemServerJars,                    $(PRODUCT_APEX_SYSTEM_SERVER_JARS))
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index 288f81f..54a57d1 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -468,8 +468,6 @@
 	rsync --checksum $(PRIVATE_STAGING) $@
 
   my_dexpreopt_script := $(intermediates)/dexpreopt.sh
-  my_dexpreopt_zip := $(intermediates)/dexpreopt.zip
-  DEXPREOPT.$(LOCAL_MODULE).POST_INSTALLED_DEXPREOPT_ZIP := $(my_dexpreopt_zip)
   .KATI_RESTAT: $(my_dexpreopt_script)
   $(my_dexpreopt_script): PRIVATE_MODULE := $(LOCAL_MODULE)
   $(my_dexpreopt_script): PRIVATE_GLOBAL_SOONG_CONFIG := $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE)
@@ -499,38 +497,71 @@
     my_dexpreopt_deps += $(intermediates)/enforce_uses_libraries.status
   endif
 
+  # We need to add all the installed files to ALL_MODULES.$(my_register_name).INSTALLED in order
+  # for the build system to properly track installed files. (for sbom, installclean, etc)
+  # We install all the files in a zip file generated at execution time, which means we have to guess
+  # what's going to be in that zip file before it's created. We then check at executation time that
+  # our guess is correct.
+  # _system_other corresponds to OdexOnSystemOtherByName() in soong.
+  # The other paths correspond to dexpreoptCommand()
+  _dexlocation := $(patsubst $(PRODUCT_OUT)/%,%,$(LOCAL_INSTALLED_MODULE))
+  _dexname := $(basename $(notdir $(_dexlocation)))
+  _system_other := $(strip $(if $(strip $(BOARD_USES_SYSTEM_OTHER_ODEX)), \
+    $(if $(strip $(SANITIZE_LITE)),, \
+      $(if $(filter $(_dexname),$(PRODUCT_DEXPREOPT_SPEED_APPS))$(filter $(_dexname),$(PRODUCT_SYSTEM_SERVER_APPS)),, \
+        $(if $(strip $(foreach myfilter,$(SYSTEM_OTHER_ODEX_FILTER),$(filter system/$(myfilter),$(_dexlocation)))), \
+          system_other/)))))
+  # _dexdir has a trailing /
+  _dexdir := $(_system_other)$(dir $(_dexlocation))
+  my_dexpreopt_zip_contents := $(sort \
+    $(foreach arch,$(my_dexpreopt_archs), \
+      $(_dexdir)oat/$(arch)/$(_dexname).odex \
+      $(_dexdir)oat/$(arch)/$(_dexname).vdex \
+      $(if $(filter false,$(LOCAL_DEX_PREOPT_APP_IMAGE)),, \
+        $(if $(my_process_profile)$(filter true,$(LOCAL_DEX_PREOPT_APP_IMAGE)), \
+          $(_dexdir)oat/$(arch)/$(_dexname).art))) \
+    $(if $(my_process_profile),$(_dexlocation).prof))
+  _dexlocation :=
+  _dexdir :=
+  _dexname :=
+  _system_other :=
+
+  my_dexpreopt_zip := $(intermediates)/dexpreopt.zip
   $(my_dexpreopt_zip): PRIVATE_MODULE := $(LOCAL_MODULE)
   $(my_dexpreopt_zip): $(my_dexpreopt_deps)
   $(my_dexpreopt_zip): | $(DEXPREOPT_GEN_DEPS)
   $(my_dexpreopt_zip): .KATI_DEPFILE := $(my_dexpreopt_zip).d
   $(my_dexpreopt_zip): PRIVATE_DEX := $(my_dex_jar)
   $(my_dexpreopt_zip): PRIVATE_SCRIPT := $(my_dexpreopt_script)
+  $(my_dexpreopt_zip): PRIVATE_ZIP_CONTENTS := $(my_dexpreopt_zip_contents)
   $(my_dexpreopt_zip): $(my_dexpreopt_script)
 	@echo "$(PRIVATE_MODULE) dexpreopt"
+	rm -f $@
+	echo -n > $@.contents
+	$(foreach f,$(PRIVATE_ZIP_CONTENTS),echo "$(f)" >> $@.contents$(newline))
 	bash $(PRIVATE_SCRIPT) $(PRIVATE_DEX) $@
+	if ! diff <(zipinfo -1 $@ | sort) $@.contents >&2; then \
+	  echo "Contents of $@ did not match what make was expecting." >&2 && exit 1; \
+	fi
 
-  ifdef LOCAL_POST_INSTALL_CMD
-    # Add a shell command separator
-    LOCAL_POST_INSTALL_CMD += &&
-  endif
+  $(foreach installed_dex_file,$(my_dexpreopt_zip_contents),\
+    $(eval $(PRODUCT_OUT)/$(installed_dex_file): $(my_dexpreopt_zip) \
+$(newline)	unzip -qoDD -d $(PRODUCT_OUT) $(my_dexpreopt_zip) $(installed_dex_file)))
 
-  LOCAL_POST_INSTALL_CMD += \
-    for i in $$(zipinfo -1 $(my_dexpreopt_zip)); \
-      do mkdir -p $(PRODUCT_OUT)/$$(dirname $$i); \
-    done && \
-    ( unzip -qoDD -d $(PRODUCT_OUT) $(my_dexpreopt_zip) 2>&1 | grep -v "zipfile is empty"; exit $${PIPESTATUS[0]} ) || \
-      ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )
+  ALL_MODULES.$(my_register_name).INSTALLED += $(addprefix $(PRODUCT_OUT)/,$(my_dexpreopt_zip_contents))
 
-  $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD)
-  $(LOCAL_INSTALLED_MODULE): $(my_dexpreopt_zip)
-
-  $(my_all_targets): $(my_dexpreopt_zip)
+  # Normally this happens in sbom.mk, which is included from base_rules.mk. But since
+  # dex_preopt_odex_install.mk is included after base_rules.mk, it misses these odex files.
+  $(foreach installed_file,$(addprefix $(PRODUCT_OUT)/,$(my_dexpreopt_zip_contents)), \
+    $(eval ALL_INSTALLED_FILES.$(installed_file) := $(my_register_name)))
 
   my_dexpreopt_config :=
+  my_dexpreopt_config_for_postprocessing :=
+  my_dexpreopt_jar_copy :=
   my_dexpreopt_product_packages :=
   my_dexpreopt_script :=
   my_dexpreopt_zip :=
-  my_dexpreopt_config_for_postprocessing :=
+  my_dexpreopt_zip_contents :=
 endif # LOCAL_DEX_PREOPT
 endif # my_create_dexpreopt_config
 
diff --git a/core/dupcheck.sh b/core/dupcheck.sh
new file mode 100755
index 0000000..13ab782
--- /dev/null
+++ b/core/dupcheck.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+# Find duplicate shared libraries by md5 checksum and possible duplicates by size.
+# Results will be available in the out directory of the build.
+# Usage:
+# ./dupcheck.sh <out_dir> <image>
+
+OUT_DIR="$1"
+IMG="$2"
+TMP_MD5="${OUT_DIR}/_dup_md5"
+TMP_SIZE="${OUT_DIR}/_dup_size"
+TMP_CHECK="${OUT_DIR}/_dup_tmp_check"
+TMP_SIZE_REAL="${OUT_DIR}/_dup_size_real"
+TMP_FILE1="${OUT_DIR}/_dup_f1"
+TMP_FILE2="${OUT_DIR}/_dup_f2"
+MD5_DUPLICATES="${OUT_DIR}/duplicate-libs-md5-${IMG}.txt"
+SIZE_DUPLICATES="${OUT_DIR}/duplicate-libs-size-${IMG}.txt"
+
+# Check arguments
+if [ "$#" -ne 2 ]; then
+	echo "Usage: ./dupcheck.sh <out_dir> <image>"
+	exit 1
+fi
+
+# Check host and toolchain version
+CHECK_HOST=$(uname)
+if [ "${CHECK_HOST}" == "Linux" ]; then
+	ARCH="linux-x86"
+else
+	ARCH="darwin-x86"
+fi
+BINUTILS_PATH="./prebuilts/clang/host/${ARCH}/llvm-binutils-stable"
+
+# Remove any old files if they exist.
+if [ -f "${MD5_DUPLICATES}" ]; then
+	rm "${MD5_DUPLICATES}"
+fi
+
+if [ -f "${SIZE_DUPLICATES}" ]; then
+	rm "${SIZE_DUPLICATES}"
+fi
+
+# Find all .so files and calculate their md5.
+find ./"${OUT_DIR}"/${IMG}/ -name "lib*.so" -type f -print0 | xargs -0 md5sum | sed -e "s# .*/# #" | sort | uniq -c | sort -g | sed "/^.*1 /d" | sed "s/^. *[0-9] //" > "${TMP_MD5}" 2>&1
+
+if [ -s "${TMP_MD5}" ]; then
+	while read -r list; do
+		checksum=$(echo "${list}" | cut -f1 -d ' ')
+		filename=$(echo "${list}" | cut -f2 -d ' ')
+		# For each md5, list the file paths that match.
+		{
+			echo "MD5: ${checksum}";											                \
+			find ./"${OUT_DIR}"/${IMG}/ -name "${filename}" -type f -print0 | xargs -0 md5sum | grep "${checksum}" | sed 's/^.* //';	\
+			echo "";													                \
+		} >> "${MD5_DUPLICATES}"
+	done <"${TMP_MD5}"
+else
+	echo "No duplicate files by md5 found." >> "${MD5_DUPLICATES}"
+fi
+
+# Cleanup
+rm "${TMP_MD5}"
+
+# Find possible duplicate .so files by size.
+find ./"${OUT_DIR}"/${IMG}/ -name "*.so" -type f -print0 | xargs -0 stat --format="%s %n" 2>/dev/null | sed -e "s# .*/# #" | sort | uniq -c | sort -g | sed "/^.*1 /d" > "${TMP_SIZE}" 2>&1
+if [ -s "${TMP_SIZE}" ]; then
+	while read -r list; do
+		size=$(echo "${list}" | cut -f2 -d ' ')
+		filename=$(echo "${list}" | cut -f3 -d ' ')
+		# Check if the files are not in the md5sum list and do nothing if that is the case.
+		find ./"${OUT_DIR}"/${IMG}/ -name "${filename}" -type f -print0 | xargs -0 stat --format="%s %n" 2>/dev/null | grep "${size}" | sed "s/^.* //" | sort > "${TMP_CHECK}" 2>&1
+		while read -r filepath; do
+			found=$(grep -F "${filepath}" "${MD5_DUPLICATES}")
+			if [ -z "${found}" ]; then
+				echo "${filepath}" >> "${TMP_SIZE_REAL}"
+			fi
+		done<"${TMP_CHECK}"
+		# For every duplication found, diff the .note and .text sections.
+		if [ -s "${TMP_SIZE_REAL}" ]; then
+			{
+				echo "File: ${filename}, Size: ${size}";	\
+				cat "${TMP_SIZE_REAL}";				\
+				echo "";					\
+			} >> "${SIZE_DUPLICATES}"
+			count=$(wc -l "${TMP_SIZE_REAL}" | cut -f1 -d ' ')
+			# Limitation: this only works for file pairs. If more than two possible duplications are found, the user need to check manually
+			# all the possible combinations using the llvm-readelf and llvm-objdump commands below.
+			if [ "${count}" = 2 ]; then
+				file1=$(head -n 1 "${TMP_SIZE_REAL}")
+				file2=$(tail -n 1 "${TMP_SIZE_REAL}")
+				# Check .note section
+				${BINUTILS_PATH}/llvm-readelf --wide --notes "${file1}" > "${TMP_FILE1}" 2>&1
+				${BINUTILS_PATH}/llvm-readelf --wide --notes "${file2}" > "${TMP_FILE2}" 2>&1
+				{
+					diff -u "${TMP_FILE1}" "${TMP_FILE2}" | sed "1d;2d;3d";	\
+					echo "";
+				} >> "${SIZE_DUPLICATES}"
+				# Check .text section
+				${BINUTILS_PATH}/llvm-objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn --section=.text "${file1}" | sed "1d;2d"> "${TMP_FILE1}" 2>&1
+				${BINUTILS_PATH}/llvm-objdump --line-numbers --disassemble --demangle --reloc --no-show-raw-insn --section=.text "${file2}" | sed "1d;2d"> "${TMP_FILE2}" 2>&1
+				{
+					diff -u "${TMP_FILE1}" "${TMP_FILE2}" | sed "1d;2d;3d";	\
+					echo "";
+				} >> "${SIZE_DUPLICATES}"
+				# Cleanup
+				rm "${TMP_FILE1}" "${TMP_FILE2}"
+			else
+				echo "*Note: more than one duplicate. Manually verify all possible combinations." >> "${SIZE_DUPLICATES}"
+			fi
+			rm "${TMP_SIZE_REAL}"
+			echo "" >> "${SIZE_DUPLICATES}"
+		fi
+	done <"${TMP_SIZE}"
+	# Cleanup
+	rm "${TMP_SIZE}" "${TMP_CHECK}"
+else
+	echo "No duplicate files by size found." >> "${SIZE_DUPLICATES}"
+fi
diff --git a/core/main.mk b/core/main.mk
index 5a591f9..d42c8ad 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -191,6 +191,13 @@
 ADDITIONAL_PRODUCT_PROPERTIES += dalvik.vm.systemservercompilerfilter=$(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER)
 endif
 
+# Add the 16K developer option if it is defined for the product.
+ifeq ($(PRODUCT_16K_DEVELOPER_OPTION),true)
+ADDITIONAL_PRODUCT_PROPERTIES += ro.product.build.16k_page.enabled=true
+else
+ADDITIONAL_PRODUCT_PROPERTIES += ro.product.build.16k_page.enabled=false
+endif
+
 # Enable core platform API violation warnings on userdebug and eng builds.
 ifneq ($(TARGET_BUILD_VARIANT),user)
 ADDITIONAL_SYSTEM_PROPERTIES += persist.debug.dalvik.vm.core_platform_api_policy=just-warn
@@ -213,11 +220,19 @@
 # property_overrides_split_enabled is true. Otherwise it will be installed in
 # /system/build.prop
 ifdef BOARD_VNDK_VERSION
+  ifneq ($(KEEP_VNDK),false)
   ifeq ($(BOARD_VNDK_VERSION),current)
     ADDITIONAL_VENDOR_PROPERTIES := ro.vndk.version=$(PLATFORM_VNDK_VERSION)
   else
     ADDITIONAL_VENDOR_PROPERTIES := ro.vndk.version=$(BOARD_VNDK_VERSION)
   endif
+  endif
+
+  # TODO(b/290159430): ro.vndk.deprecate is a temporal variable for deprecating VNDK.
+  # This variable will be removed once ro.vndk.version can be removed.
+  ifneq ($(KEEP_VNDK),true)
+    ADDITIONAL_SYSTEM_PROPERTIES += ro.vndk.deprecate=true
+  endif
 endif
 
 # Add cpu properties for bionic and ART.
@@ -324,12 +339,14 @@
 # modules. It uses the version in PRODUCT_PRODUCT_VNDK_VERSION. If the value
 # is "current", use PLATFORM_VNDK_VERSION.
 ifdef PRODUCT_PRODUCT_VNDK_VERSION
+ifneq ($(KEEP_VNDK),false)
 ifeq ($(PRODUCT_PRODUCT_VNDK_VERSION),current)
 ADDITIONAL_PRODUCT_PROPERTIES += ro.product.vndk.version=$(PLATFORM_VNDK_VERSION)
 else
 ADDITIONAL_PRODUCT_PROPERTIES += ro.product.vndk.version=$(PRODUCT_PRODUCT_VNDK_VERSION)
 endif
 endif
+endif
 
 ADDITIONAL_PRODUCT_PROPERTIES += ro.build.characteristics=$(TARGET_AAPT_CHARACTERISTICS)
 
@@ -810,12 +827,14 @@
 
 # Sets up dependencies such that whenever a target module is installed,
 # any other target modules listed in $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET) will also be installed
+# This doesn't apply to ORDERONLY_INSTALLED items.
 define add-all-target-to-target-required-modules-deps
 $(foreach m,$(ALL_MODULES), \
   $(eval r := $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET)) \
   $(if $(r), \
     $(eval r := $(call module-installed-files,$(r))) \
     $(eval t_m := $(filter $(TARGET_OUT_ROOT)/%, $(ALL_MODULES.$(m).INSTALLED))) \
+    $(eval t_m := $(filter-out $(ALL_MODULES.$(m).ORDERONLY_INSTALLED), $(ALL_MODULES.$(m).INSTALLED))) \
     $(eval t_r := $(filter $(TARGET_OUT_ROOT)/%, $(r))) \
     $(eval t_r := $(filter-out $(t_m), $(t_r))) \
     $(if $(t_m), $(eval $(call add-required-deps, $(t_m),$(t_r)))) \
@@ -1224,9 +1243,7 @@
 
 endef
 
-# Lists most of the files a particular product installs, including:
-# - PRODUCT_PACKAGES, and their LOCAL_REQUIRED_MODULES
-# - PRODUCT_COPY_FILES
+# Lists the modules particular product installs.
 # The base list of modules to build for this product is specified
 # by the appropriate product definition file, which was included
 # by product_config.mk.
@@ -1238,8 +1255,7 @@
 # Name resolution for LOCAL_REQUIRED_MODULES:
 #   See the select-bitness-of-required-modules definition.
 # $(1): product makefile
-
-define product-installed-files
+define product-installed-modules
   $(eval _pif_modules := \
     $(call get-product-var,$(1),PRODUCT_PACKAGES) \
     $(if $(filter eng,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ENG)) \
@@ -1256,7 +1272,14 @@
   $(eval ### Resolve the :32 :64 module name) \
   $(eval _pif_modules := $(sort $(call resolve-bitness-for-modules,TARGET,$(_pif_modules)))) \
   $(call expand-required-modules,_pif_modules,$(_pif_modules),$(_pif_overrides)) \
-  $(filter-out $(HOST_OUT_ROOT)/%,$(call module-installed-files, $(_pif_modules))) \
+  $(_pif_modules)
+endef
+
+# Lists most of the files a particular product installs.
+# It gives all the installed files for all modules returned by product-installed-modules,
+# and also includes PRODUCT_COPY_FILES.
+define product-installed-files
+  $(filter-out $(HOST_OUT_ROOT)/%,$(call module-installed-files, $(call product-installed-modules,$(1)))) \
   $(call resolve-product-relative-paths,\
     $(foreach cf,$(call get-product-var,$(1),PRODUCT_COPY_FILES),$(call word-colon,2,$(cf))))
 endef
@@ -1356,6 +1379,7 @@
   product_host_FILES := $(call host-installed-files,$(INTERNAL_PRODUCT))
   product_target_FILES := $(call product-installed-files, $(INTERNAL_PRODUCT))
   # WARNING: The product_MODULES variable is depended on by external files.
+  # It contains the list of register names that will be installed on the device
   product_MODULES := $(_pif_modules)
 
   # Verify the artifact path requirements made by included products.
@@ -1428,6 +1452,16 @@
       $(warning $(ALL_MODULES.$(m).MAKEFILE): Module '$(m)' in PRODUCT_PACKAGES_TESTS has nothing to install!)))
 endif
 
+ifneq ($(TARGET_BUILD_APPS),)
+  # If this build is just for apps, only build apps and not the full system by default.
+  ifneq ($(filter all,$(TARGET_BUILD_APPS)),)
+    # If they used the magic goal "all" then build all apps in the source tree.
+    unbundled_build_modules := $(foreach m,$(sort $(ALL_MODULES)),$(if $(filter APPS,$(ALL_MODULES.$(m).CLASS)),$(m)))
+  else
+    unbundled_build_modules := $(sort $(TARGET_BUILD_APPS))
+  endif
+endif
+
 # build/make/core/Makefile contains extra stuff that we don't want to pollute this
 # top-level makefile with.  It expects that ALL_DEFAULT_INSTALLED_MODULES
 # contains everything that's built during the current make, but it also further
@@ -1703,16 +1737,10 @@
 else ifneq ($(TARGET_BUILD_APPS),)
   # If this build is just for apps, only build apps and not the full system by default.
 
-  unbundled_build_modules :=
-  ifneq ($(filter all,$(TARGET_BUILD_APPS)),)
-    # If they used the magic goal "all" then build all apps in the source tree.
-    unbundled_build_modules := $(foreach m,$(sort $(ALL_MODULES)),$(if $(filter APPS,$(ALL_MODULES.$(m).CLASS)),$(m)))
-  else
-    unbundled_build_modules := $(TARGET_BUILD_APPS)
-  endif
-
-  # Dist the installed files if they exist.
-  apps_only_installed_files := $(foreach m,$(unbundled_build_modules),$(ALL_MODULES.$(m).INSTALLED))
+  # Dist the installed files if they exist, except the installed symlinks. dist-for-goals emits
+  # `cp src dest` commands, which will fail to copy dangling symlinks.
+  apps_only_installed_files := $(foreach m,$(unbundled_build_modules),\
+    $(filter-out $(ALL_MODULES.$(m).INSTALLED_SYMLINKS),$(ALL_MODULES.$(m).INSTALLED)))
   $(call dist-for-goals,apps_only, $(apps_only_installed_files))
 
   # Dist the bundle files if they exist.
@@ -2159,10 +2187,7 @@
 
 metadata_list := $(OUT_DIR)/.module_paths/METADATA.list
 metadata_files := $(subst $(newline),$(space),$(file <$(metadata_list)))
-# (TODO: b/272358583 find another way of always rebuilding this target)
-# Remove the sbom-metadata.csv whenever makefile is evaluated
-$(shell rm $(PRODUCT_OUT)/sbom-metadata.csv >/dev/null 2>&1)
-$(PRODUCT_OUT)/sbom-metadata.csv: $(installed_files) $(metadata_list) $(metadata_files)
+$(PRODUCT_OUT)/sbom-metadata.csv:
 	rm -f $@
 	echo installed_file,module_path,soong_module_type,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,build_output_path,static_libraries,whole_static_libraries,is_static_lib >> $@
 	$(eval _all_static_libs :=)
@@ -2173,7 +2198,6 @@
 	  $(eval _module_path := $(strip $(sort $(ALL_MODULES.$(_module_name).PATH)))) \
 	  $(eval _soong_module_type := $(strip $(sort $(ALL_MODULES.$(_module_name).SOONG_MODULE_TYPE)))) \
 	  $(eval _is_prebuilt_make_module := $(ALL_MODULES.$(_module_name).IS_PREBUILT_MAKE_MODULE)) \
-	  $(eval _post_installed_dexpreopt_zip := $(DEXPREOPT.$(_module_name).POST_INSTALLED_DEXPREOPT_ZIP)) \
 	  $(eval _product_copy_files := $(sort $(filter %:$(_path_on_device),$(product_copy_files_without_owner)))) \
 	  $(eval _kernel_module_copy_files := $(sort $(filter %$(_path_on_device),$(KERNEL_MODULE_COPY_FILES)))) \
 	  $(eval _is_build_prop := $(call is-build-prop,$f)) \
@@ -2186,15 +2210,14 @@
 	  $(eval _is_fsverity_build_manifest_apk := $(if $(findstring $f,$(ALL_FSVERITY_BUILD_MANIFEST_APK)),Y)) \
 	  $(eval _is_linker_config := $(if $(findstring $f,$(SYSTEM_LINKER_CONFIG) $(vendor_linker_config_file)),Y)) \
 	  $(eval _is_partition_compat_symlink := $(if $(findstring $f,$(PARTITION_COMPAT_SYMLINKS)),Y)) \
-	  $(eval _is_platform_generated := $(_is_build_prop)$(_is_notice_file)$(_is_dexpreopt_image_profile)$(_is_product_system_other_avbkey)$(_is_event_log_tags_file)$(_is_system_other_odex_marker)$(_is_kernel_modules_blocklist)$(_is_fsverity_build_manifest_apk)$(_is_linker_config)$(_is_partition_compat_symlink)) \
+	  $(eval _is_flags_file := $(if $(findstring $f, $(ALL_FLAGS_FILES)),Y)) \
+	  $(eval _is_rootdir_symlink := $(if $(findstring $f, $(ALL_ROOTDIR_SYMLINKS)),Y)) \
+	  $(eval _is_platform_generated := $(_is_build_prop)$(_is_notice_file)$(_is_dexpreopt_image_profile)$(_is_product_system_other_avbkey)$(_is_event_log_tags_file)$(_is_system_other_odex_marker)$(_is_kernel_modules_blocklist)$(_is_fsverity_build_manifest_apk)$(_is_linker_config)$(_is_partition_compat_symlink)$(_is_flags_file)$(_is_rootdir_symlink)) \
 	  $(eval _static_libs := $(ALL_INSTALLED_FILES.$f.STATIC_LIBRARIES)) \
 	  $(eval _whole_static_libs := $(ALL_INSTALLED_FILES.$f.WHOLE_STATIC_LIBRARIES)) \
 	  $(foreach l,$(_static_libs),$(eval _all_static_libs += $l:$(strip $(sort $(ALL_MODULES.$l.PATH))):$(strip $(sort $(ALL_MODULES.$l.SOONG_MODULE_TYPE))):$(ALL_STATIC_LIBRARIES.$l.BUILT_FILE))) \
 	  $(foreach l,$(_whole_static_libs),$(eval _all_static_libs += $l:$(strip $(sort $(ALL_MODULES.$l.PATH))):$(strip $(sort $(ALL_MODULES.$l.SOONG_MODULE_TYPE))):$(ALL_STATIC_LIBRARIES.$l.BUILT_FILE))) \
 	  echo /$(_path_on_device),$(_module_path),$(_soong_module_type),$(_is_prebuilt_make_module),$(_product_copy_files),$(_kernel_module_copy_files),$(_is_platform_generated),$(_build_output_path),$(_static_libs),$(_whole_static_libs), >> $@; \
-	  $(if $(_post_installed_dexpreopt_zip), \
-	  for i in $$(zipinfo -1 $(_post_installed_dexpreopt_zip)); do echo /$$i$(comma)$(_module_path)$(comma)$(_soong_module_type)$(comma)$(_is_prebuilt_make_module)$(comma)$(_product_copy_files)$(comma)$(_kernel_module_copy_files)$(comma)$(_is_platform_generated)$(comma)$(PRODUCT_OUT)/$$i$(comma)$(_static_libs)$(comma)$(_whole_static_libs)$(comma) >> $@ ; done ; \
-	  ) \
 	)
 	$(foreach l,$(sort $(_all_static_libs)), \
 	  $(eval _lib_stem := $(call word-colon,1,$l)) \
@@ -2207,11 +2230,17 @@
 	  echo $(_lib_stem).a,$(_module_path),$(_soong_module_type),,,,,$(_built_file),$(_static_libs),$(_whole_static_libs),$(_is_static_lib) >> $@; \
 	)
 
+# (TODO: b/272358583 find another way of always rebuilding sbom.spdx)
+# Remove the always_dirty_file.txt whenever the makefile is evaluated
+$(shell rm -f $(PRODUCT_OUT)/always_dirty_file.txt)
+$(PRODUCT_OUT)/always_dirty_file.txt:
+	touch $@
+
 .PHONY: sbom
 ifeq ($(TARGET_BUILD_APPS),)
 sbom: $(PRODUCT_OUT)/sbom.spdx.json
 $(PRODUCT_OUT)/sbom.spdx.json: $(PRODUCT_OUT)/sbom.spdx
-$(PRODUCT_OUT)/sbom.spdx: $(PRODUCT_OUT)/sbom-metadata.csv $(GEN_SBOM)
+$(PRODUCT_OUT)/sbom.spdx: $(PRODUCT_OUT)/sbom-metadata.csv $(GEN_SBOM) $(installed_files) $(metadata_list) $(metadata_files) $(PRODUCT_OUT)/always_dirty_file.txt
 	rm -rf $@
 	$(GEN_SBOM) --output_file $@ --metadata $(PRODUCT_OUT)/sbom-metadata.csv --build_version $(BUILD_FINGERPRINT_FROM_FILE) --product_mfr "$(PRODUCT_MANUFACTURER)" --json
 
@@ -2230,7 +2259,7 @@
 $(eval _dep_modules := $(filter %.$(_module_name),$(ALL_MODULES)) $(filter %.$(_module_name)$(TARGET_2ND_ARCH_MODULE_SUFFIX),$(ALL_MODULES)))
 $(eval _is_apex := $(filter %.apex,$(3)))
 
-$(4): $(3) $(metadata_list) $(metadata_files)
+$(4):
 	rm -rf $$@
 	echo installed_file,module_path,soong_module_type,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,build_output_path,static_libraries,whole_static_libraries,is_static_lib >> $$@
 	echo /$(_path_on_device),$(_module_path),$(_soong_module_type),,,,,$(3),,, >> $$@
@@ -2239,7 +2268,7 @@
 	    echo $(patsubst $(PRODUCT_OUT)/apex/$(_module_name)/%,%,$(ALL_MODULES.$m.INSTALLED)),$(sort $(ALL_MODULES.$m.PATH)),$(sort $(ALL_MODULES.$m.SOONG_MODULE_TYPE)),,,,,$(strip $(ALL_MODULES.$m.BUILT)),,, >> $$@;))
 
 $(2): $(1)
-$(1): $(4) $(GEN_SBOM)
+$(1): $(4) $(3) $(GEN_SBOM) $(installed_files) $(metadata_list) $(metadata_files)
 	rm -rf $$@
 	$(GEN_SBOM) --output_file $$@ --metadata $(4) --build_version $$(BUILD_FINGERPRINT_FROM_FILE) --product_mfr "$(PRODUCT_MANUFACTURER)" --json $(if $(filter %.apk,$(3)),--unbundled_apk,--unbundled_apex)
 endef
diff --git a/core/package_internal.mk b/core/package_internal.mk
index 7cfab5b..a03a62b 100644
--- a/core/package_internal.mk
+++ b/core/package_internal.mk
@@ -531,7 +531,7 @@
 $(LOCAL_BUILT_MODULE): PRIVATE_RES_PACKAGE := $(my_res_package)
 $(LOCAL_BUILT_MODULE) : $(my_res_package) $(AAPT2)
 ifdef LOCAL_COMPRESSED_MODULE
-$(LOCAL_BUILT_MODULE) : $(MINIGZIP)
+$(LOCAL_BUILT_MODULE) : $(GZIP)
 endif
 ifeq (true, $(LOCAL_UNCOMPRESS_DEX))
 $(LOCAL_BUILT_MODULE) : $(ZIP2ZIP)
diff --git a/core/packaging/flags.mk b/core/packaging/flags.mk
new file mode 100644
index 0000000..697fe24
--- /dev/null
+++ b/core/packaging/flags.mk
@@ -0,0 +1,123 @@
+# Copyright (C) 2023 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 file is included by build/make/core/Makefile, and contains the logic for
+# the combined flags files.
+#
+
+# TODO: Should we do all of the images in $(IMAGES_TO_BUILD)?
+_FLAG_PARTITIONS := product system system_ext vendor
+
+
+# -----------------------------------------------------------------
+# Release Config Flags
+
+# Create a summary file of build flags for each partition
+# $(1): built build flags json file
+# $(2): installed build flags json file
+# $(3): flag names
+define generate-partition-build-flag-file
+$(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))
+$(eval $(strip $(1)): PRIVATE_FLAG_NAMES := $(strip $(3)))
+$(strip $(1)):
+	mkdir -p $$(dir $$(PRIVATE_OUT))
+	echo '{' > $$(PRIVATE_OUT)
+	echo '"flags": [' >> $$(PRIVATE_OUT)
+	$$(foreach flag, $$(PRIVATE_FLAG_NAMES), \
+		( \
+			printf '  { "name": "%s", "value": "%s", ' \
+					'$$(flag)' \
+					'$$(_ALL_RELEASE_FLAGS.$$(flag).VALUE)' \
+					; \
+			printf '"set": "%s", "default": "%s", "declared": "%s" }' \
+					'$$(_ALL_RELEASE_FLAGS.$$(flag).SET_IN)' \
+					'$$(_ALL_RELEASE_FLAGS.$$(flag).DEFAULT)' \
+					'$$(_ALL_RELEASE_FLAGS.$$(flag).DECLARED_IN)' \
+					; \
+			printf '$$(if $$(filter $$(lastword $$(PRIVATE_FLAG_NAMES)),$$(flag)),,$$(comma))\n' ; \
+		) >> $$(PRIVATE_OUT) ; \
+	)
+	echo "]" >> $$(PRIVATE_OUT)
+	echo "}" >> $$(PRIVATE_OUT)
+$(call copy-one-file, $(1), $(2))
+endef
+
+$(foreach partition, $(_FLAG_PARTITIONS), \
+	$(eval build_flag_summaries.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/build_flags.json) \
+	$(eval $(call generate-partition-build-flag-file, \
+				$(TARGET_OUT_FLAGS)/$(partition)/build_flags.json, \
+				$(build_flag_summaries.$(partition)), \
+				$(_ALL_RELEASE_FLAGS.PARTITIONS.$(partition)) \
+			) \
+	) \
+)
+
+
+# -----------------------------------------------------------------
+# Aconfig Flags
+
+# Create a summary file of build flags for each partition
+# $(1): built aconfig flags textprot file (out)
+# $(2): installed aconfig flags textprot file (out)
+# $(3): input aconfig files for the partition (in)
+define generate-partition-aconfig-flag-file
+$(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))
+$(eval $(strip $(1)): PRIVATE_IN := $(strip $(3)))
+$(strip $(1)): $(ACONFIG) $(strip $(3))
+	mkdir -p $$(dir $$(PRIVATE_OUT))
+	$$(if $$(PRIVATE_IN), \
+		$$(ACONFIG) dump --format textproto --out $$(PRIVATE_OUT) \
+			$$(addprefix --cache ,$$(PRIVATE_IN)), \
+		echo "# No aconfig flags" > $$(PRIVATE_OUT) \
+	)
+$(call copy-one-file, $(1), $(2))
+endef
+
+
+$(foreach partition, $(_FLAG_PARTITIONS), \
+	$(eval aconfig_flag_summaries.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig_flags.textproto) \
+	$(eval $(call generate-partition-aconfig-flag-file, \
+				$(TARGET_OUT_FLAGS)/$(partition)/aconfig_flags.textproto, \
+				$(aconfig_flag_summaries.$(partition)), \
+				$(sort $(foreach m,$(call register-names-for-partition, $(partition)), \
+					$(ALL_MODULES.$(m).ACONFIG_FILES) \
+				)) \
+	)) \
+)
+
+
+# -----------------------------------------------------------------
+# Install the ones we need for the configured product
+required_flags_files := \
+		$(sort $(foreach partition, $(filter $(IMAGES_TO_BUILD), $(_FLAG_PARTITIONS)), \
+			$(build_flag_summaries.$(partition)) \
+			$(aconfig_flag_summaries.$(partition)) \
+		))
+
+ALL_DEFAULT_INSTALLED_MODULES += $(required_flags_files)
+ALL_FLAGS_FILES := $(required_flags_files)
+
+# TODO: Remove
+.PHONY: flag-files
+flag-files: $(required_flags_files)
+
+
+# Clean up
+required_flags_files:=
+$(foreach partition, $(_FLAG_PARTITIONS), \
+	$(eval build_flag_summaries.$(partition):=) \
+	$(eval aconfig_flag_summaries.$(partition):=) \
+)
+
diff --git a/core/product.mk b/core/product.mk
index 8f4db38..54e3695 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -33,6 +33,10 @@
 # 4096, 16384 and 65536.
 _product_single_value_vars += PRODUCT_MAX_PAGE_SIZE_SUPPORTED
 
+# Indicates that AOSP can use a kernel configured with 4k/16k/64k page sizes.
+# The possible values are true or false.
+_product_single_value_vars += PRODUCT_PAGE_SIZE_AGNOSTIC
+
 # The resource configuration options to use for this product.
 _product_list_vars += PRODUCT_LOCALES
 _product_list_vars += PRODUCT_AAPT_CONFIG
@@ -43,6 +47,13 @@
 _product_list_vars += PRODUCT_PACKAGES_DEBUG
 _product_list_vars += PRODUCT_PACKAGES_DEBUG_ASAN
 _product_list_vars += PRODUCT_PACKAGES_ARM64
+
+# packages that are added to PRODUCT_PACKAGES based on the PRODUCT_SHIPPING_API_LEVEL
+# These are only added if the shipping API level is that level or lower
+_product_list_vars += PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29
+_product_list_vars += PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33
+_product_list_vars += PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34
+
 # Packages included only for eng/userdebug builds, when building with EMMA_INSTRUMENT=true
 _product_list_vars += PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE
 _product_list_vars += PRODUCT_PACKAGES_ENG
@@ -146,6 +157,9 @@
 # PRODUCT_BOOT_JARS, so that device-specific jars go after common jars.
 _product_list_vars += PRODUCT_BOOT_JARS_EXTRA
 
+# List of jars to be included in the ART boot image for testing.
+_product_list_vars += PRODUCT_TEST_ONLY_ART_BOOT_IMAGE_JARS
+
 _product_single_value_vars += PRODUCT_SUPPORTS_VBOOT
 _product_list_vars += PRODUCT_SYSTEM_SERVER_APPS
 # List of system_server classpath jars on the platform.
@@ -317,6 +331,10 @@
 _product_list_vars += PRODUCT_PACKAGE_NAME_OVERRIDES
 _product_list_vars += PRODUCT_CERTIFICATE_OVERRIDES
 
+# Overrides the (apex, jar) pairs above when determining the on-device location. The format is:
+# <old_apex>:<old_jar>:<new_apex>:<new_jar>
+_product_list_vars += PRODUCT_CONFIGURED_JAR_LOCATION_OVERRIDES
+
 # Controls for whether different partitions are built for the current product.
 _product_single_value_vars += PRODUCT_BUILD_SYSTEM_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_SYSTEM_OTHER_IMAGE
@@ -419,6 +437,9 @@
 # specified we default to COW version 2 in update_engine for backwards compatibility
 _product_single_value_vars += PRODUCT_VIRTUAL_AB_COW_VERSION
 
+# If set, determines whether the build system checks vendor seapp contexts violations.
+_product_single_value_vars += PRODUCT_CHECK_VENDOR_SEAPP_VIOLATIONS
+
 _product_list_vars += PRODUCT_AFDO_PROFILES
 
 .KATI_READONLY := _product_single_value_vars _product_list_vars
diff --git a/core/product_config.mk b/core/product_config.mk
index 3f9eb24..9f0cf25 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -500,6 +500,9 @@
   ifneq (,$(call math_gt_or_eq,33,$(PRODUCT_SHIPPING_API_LEVEL)))
     PRODUCT_PACKAGES += $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_33)
   endif
+  ifneq (,$(call math_gt_or_eq,34,$(PRODUCT_SHIPPING_API_LEVEL)))
+    PRODUCT_PACKAGES += $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34)
+  endif
 endif
 
 # If build command defines OVERRIDE_PRODUCT_EXTRA_VNDK_VERSIONS,
@@ -572,6 +575,32 @@
       $(PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS))
 endif
 
+# Get the board API level.
+board_api_level := $(PLATFORM_SDK_VERSION)
+ifdef BOARD_API_LEVEL
+  board_api_level := $(BOARD_API_LEVEL)
+else ifdef BOARD_SHIPPING_API_LEVEL
+  # Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level.
+  board_api_level := $(BOARD_SHIPPING_API_LEVEL)
+endif
+
+# Calculate the VSR vendor API level.
+VSR_VENDOR_API_LEVEL := $(board_api_level)
+
+ifdef PRODUCT_SHIPPING_API_LEVEL
+  VSR_VENDOR_API_LEVEL := $(call math_min,$(PRODUCT_SHIPPING_API_LEVEL),$(board_api_level))
+endif
+.KATI_READONLY := VSR_VENDOR_API_LEVEL
+
+# Boolean variable determining if vendor seapp contexts is enforced
+CHECK_VENDOR_SEAPP_VIOLATIONS := false
+ifneq ($(call math_gt,$(VSR_VENDOR_API_LEVEL),34),)
+  CHECK_VENDOR_SEAPP_VIOLATIONS := true
+else ifneq ($(PRODUCT_CHECK_VENDOR_SEAPP_VIOLATIONS),)
+  CHECK_VENDOR_SEAPP_VIOLATIONS := $(PRODUCT_CHECK_VENDOR_SEAPP_VIOLATIONS)
+endif
+.KATI_READONLY := CHECK_VENDOR_SEAPP_VIOLATIONS
+
 define product-overrides-config
 $$(foreach rule,$$(PRODUCT_$(1)_OVERRIDES),\
     $$(if $$(filter 2,$$(words $$(subst :,$$(space),$$(rule)))),,\
diff --git a/core/proguard_basic_keeps.flags b/core/proguard_basic_keeps.flags
index b59527a..f6b34b8 100644
--- a/core/proguard_basic_keeps.flags
+++ b/core/proguard_basic_keeps.flags
@@ -10,6 +10,12 @@
 # and RuntimeVisibleTypeAnnotations, as well as associated defaults.
 -keepattributes RuntimeVisible*Annotation*,AnnotationDefault
 
+# With R8 full mode, certain attributes are only kept when matched with an
+# explicit keep rule for that target, even with a global -keepattributes rule.
+# As such, we can add the global keep rule here with minimal cost while
+# simplifying incremental development.
+-keepattributes Exceptions
+
 # For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
 -keepclassmembers enum * {
     public static **[] values();
diff --git a/core/release_config.mk b/core/release_config.mk
index b72ee89..4f9daaf 100644
--- a/core/release_config.mk
+++ b/core/release_config.mk
@@ -76,6 +76,12 @@
 ifeq ($(filter $(_all_release_configs), $(TARGET_RELEASE)),)
     $(error No release config found for TARGET_RELEASE: $(TARGET_RELEASE). Available releases are: $(_all_release_configs))
 else
+# TODO(b/294161396): Remove this logic
+ifeq ($(TARGET_RELEASE),next)
+ifndef TESTING_TARGET_RELEASE_NEXT
+   $(error "next" releases are not supported yet.  b/294161396)
+endif
+endif
     # Choose flag files
     # Don't sort this, use it in the order they gave us.
     flag_value_files := $(_all_release_configs.$(TARGET_RELEASE).FILES)
diff --git a/core/rust_device_benchmark_config_template.xml b/core/rust_device_benchmark_config_template.xml
index 2055df2..541630c 100644
--- a/core/rust_device_benchmark_config_template.xml
+++ b/core/rust_device_benchmark_config_template.xml
@@ -16,7 +16,7 @@
 <!-- This test config file is auto-generated. -->
 <configuration description="Config to run {MODULE} rust benchmark tests.">
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="false" />
+        <option name="cleanup" value="true" />
         <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
     </target_preparer>
 
diff --git a/core/soong_app_prebuilt.mk b/core/soong_app_prebuilt.mk
index ccc5449..3aa244c 100644
--- a/core/soong_app_prebuilt.mk
+++ b/core/soong_app_prebuilt.mk
@@ -73,12 +73,14 @@
 # We skip it for unbundled app builds where we cannot build veridex.
 module_run_appcompat :=
 ifeq (true,$(non_system_module))
-ifeq (,$(TARGET_BUILD_APPS))  # ! unbundled app build
+ifeq (,$(TARGET_BUILD_APPS))  # not unbundled app build
+ifeq (,$(filter sdk,$(MAKECMDGOALS))) # not sdk build (which is another form of unbundled build)
 ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
   module_run_appcompat := true
 endif
 endif
 endif
+endif
 
 ifeq ($(module_run_appcompat),true)
   $(LOCAL_BUILT_MODULE): $(appcompat-files)
@@ -100,31 +102,24 @@
 endif
 
 ifdef LOCAL_SOONG_PROGUARD_DICT
-  my_proguard_dictionary_directory := $(local-proguard-dictionary-directory)
-  my_proguard_dictionary_mapping_directory := $(local-proguard-dictionary-mapping-directory)
-  $(eval $(call copy-one-file,$(LOCAL_SOONG_PROGUARD_DICT),\
-    $(intermediates.COMMON)/proguard_dictionary))
   $(eval $(call copy-r8-dictionary-file-with-mapping,\
     $(LOCAL_SOONG_PROGUARD_DICT),\
-    $(my_proguard_dictionary_directory)/proguard_dictionary,\
-    $(my_proguard_dictionary_mapping_directory)/proguard_dictionary.textproto))
-  $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),\
-    $(my_proguard_dictionary_directory)/classes.jar))
-  $(call add-dependency,$(LOCAL_BUILT_MODULE),\
-    $(intermediates.COMMON)/proguard_dictionary)
-  $(call add-dependency,$(LOCAL_BUILT_MODULE),\
-    $(my_proguard_dictionary_directory)/proguard_dictionary)
-  $(call add-dependency,$(LOCAL_BUILT_MODULE),\
-    $(my_proguard_dictionary_mapping_directory)/proguard_dictionary.textproto)
-  $(call add-dependency,$(LOCAL_BUILT_MODULE),\
-    $(my_proguard_dictionary_directory)/classes.jar)
+    $(intermediates.COMMON)/proguard_dictionary,\
+    $(intermediates.COMMON)/proguard_dictionary.textproto))
+
+  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_FILES := \
+    $(intermediates.COMMON)/proguard_dictionary \
+    $(LOCAL_SOONG_CLASSES_JAR)
+  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_SOONG_ZIP_ARGUMENTS := \
+    -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/proguard_dictionary \
+    -f $(intermediates.COMMON)/proguard_dictionary \
+    -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/classes.jar \
+    -f $(LOCAL_SOONG_CLASSES_JAR)
+  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_MAPPING := $(intermediates.COMMON)/proguard_dictionary.textproto
 endif
 
 ifdef LOCAL_SOONG_PROGUARD_USAGE_ZIP
-  $(eval $(call copy-one-file,$(LOCAL_SOONG_PROGUARD_USAGE_ZIP),\
-    $(call local-packaging-dir,proguard_usage)/proguard_usage.zip))
-  $(call add-dependency,$(LOCAL_BUILT_MODULE),\
-    $(call local-packaging-dir,proguard_usage)/proguard_usage.zip)
+  ALL_MODULES.$(my_register_name).PROGUARD_USAGE_ZIP := $(LOCAL_SOONG_PROGUARD_USAGE_ZIP)
 endif
 
 ifdef LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 0d5799c..a4dbffa 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -40,6 +40,7 @@
 $(call add_json_str,  Platform_base_os,                  $(PLATFORM_BASE_OS))
 $(call add_json_str,  Platform_version_last_stable,      $(PLATFORM_VERSION_LAST_STABLE))
 $(call add_json_str,  Platform_version_known_codenames,  $(PLATFORM_VERSION_KNOWN_CODENAMES))
+$(call add_json_bool, Release_aidl_use_unfrozen,         $(RELEASE_AIDL_USE_UNFROZEN))
 
 $(call add_json_str,  Platform_min_supported_target_sdk_version, $(PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION))
 
@@ -150,6 +151,7 @@
 $(call add_json_bool, Malloc_pattern_fill_contents,      $(MALLOC_PATTERN_FILL_CONTENTS))
 $(call add_json_str,  Override_rs_driver,                $(OVERRIDE_RS_DRIVER))
 $(call add_json_str,  DeviceMaxPageSizeSupported,        $(TARGET_MAX_PAGE_SIZE_SUPPORTED))
+$(call add_json_bool, DevicePageSizeAgnostic,            $(filter true,$(TARGET_PAGE_SIZE_AGNOSTIC)))
 
 $(call add_json_bool, UncompressPrivAppDex,              $(call invert_bool,$(filter true,$(DONT_UNCOMPRESS_PRIV_APPS_DEXS))))
 $(call add_json_list, ModulesLoadedByPrivilegedModules,  $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES))
@@ -204,17 +206,8 @@
 
 $(call add_json_list, PgoAdditionalProfileDirs,          $(PGO_ADDITIONAL_PROFILE_DIRS))
 
-$(call add_json_list, BoardPlatVendorPolicy,             $(BOARD_PLAT_VENDOR_POLICY))
-$(call add_json_list, BoardReqdMaskPolicy,               $(BOARD_REQD_MASK_POLICY))
-$(call add_json_list, BoardSystemExtPublicPrebuiltDirs,  $(BOARD_SYSTEM_EXT_PUBLIC_PREBUILT_DIRS))
-$(call add_json_list, BoardSystemExtPrivatePrebuiltDirs, $(BOARD_SYSTEM_EXT_PRIVATE_PREBUILT_DIRS))
-$(call add_json_list, BoardProductPublicPrebuiltDirs,    $(BOARD_PRODUCT_PUBLIC_PREBUILT_DIRS))
-$(call add_json_list, BoardProductPrivatePrebuiltDirs,   $(BOARD_PRODUCT_PRIVATE_PREBUILT_DIRS))
 $(call add_json_list, BoardVendorSepolicyDirs,           $(BOARD_VENDOR_SEPOLICY_DIRS) $(BOARD_SEPOLICY_DIRS))
 $(call add_json_list, BoardOdmSepolicyDirs,              $(BOARD_ODM_SEPOLICY_DIRS))
-$(call add_json_list, BoardVendorDlkmSepolicyDirs,       $(BOARD_VENDOR_DLKM_SEPOLICY_DIRS))
-$(call add_json_list, BoardOdmDlkmSepolicyDirs,          $(BOARD_ODM_DLKM_SEPOLICY_DIRS))
-$(call add_json_list, BoardSystemDlkmSepolicyDirs,       $(BOARD_SYSTEM_DLKM_SEPOLICY_DIRS))
 $(call add_json_list, SystemExtPublicSepolicyDirs,       $(SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS))
 $(call add_json_list, SystemExtPrivateSepolicyDirs,      $(SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS))
 $(call add_json_list, BoardSepolicyM4Defs,               $(BOARD_SEPOLICY_M4DEFS))
@@ -235,6 +228,7 @@
 $(call add_json_list, ManifestPackageNameOverrides,      $(PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES))
 $(call add_json_list, PackageNameOverrides,              $(PRODUCT_PACKAGE_NAME_OVERRIDES))
 $(call add_json_list, CertificateOverrides,              $(PRODUCT_CERTIFICATE_OVERRIDES))
+$(call add_json_list, ConfiguredJarLocationOverrides,    $(PRODUCT_CONFIGURED_JAR_LOCATION_OVERRIDES))
 
 $(call add_json_str, ApexGlobalMinSdkVersionOverride,    $(APEX_GLOBAL_MIN_SDK_VERSION_OVERRIDE))
 
@@ -317,6 +311,11 @@
 
 $(call add_json_str, ReleaseVersion,    $(_RELEASE_VERSION))
 $(call add_json_list, ReleaseAconfigValueSets,    $(RELEASE_ACONFIG_VALUE_SETS))
+$(call add_json_str, ReleaseAconfigFlagDefaultPermission,    $(RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION))
+
+$(call add_json_bool, KeepVndk, $(filter true,$(KEEP_VNDK)))
+
+$(call add_json_bool, CheckVendorSeappViolations, $(filter true,$(CHECK_VENDOR_SEAPP_VIOLATIONS)))
 
 $(call json_end)
 
diff --git a/core/soong_java_prebuilt.mk b/core/soong_java_prebuilt.mk
index a8f475f..9744abf 100644
--- a/core/soong_java_prebuilt.mk
+++ b/core/soong_java_prebuilt.mk
@@ -62,31 +62,24 @@
 endif
 
 ifdef LOCAL_SOONG_PROGUARD_DICT
-  my_proguard_dictionary_directory := $(local-proguard-dictionary-directory)
-  my_proguard_dictionary_mapping_directory := $(local-proguard-dictionary-mapping-directory)
-  $(eval $(call copy-one-file,$(LOCAL_SOONG_PROGUARD_DICT),\
-    $(intermediates.COMMON)/proguard_dictionary))
   $(eval $(call copy-r8-dictionary-file-with-mapping,\
     $(LOCAL_SOONG_PROGUARD_DICT),\
-    $(my_proguard_dictionary_directory)/proguard_dictionary,\
-    $(my_proguard_dictionary_mapping_directory)/proguard_dictionary.textproto))
-  $(eval $(call copy-one-file,$(LOCAL_SOONG_CLASSES_JAR),\
-    $(my_proguard_dictionary_directory)/classes.jar))
-  $(call add-dependency,$(common_javalib.jar),\
-    $(intermediates.COMMON)/proguard_dictionary)
-  $(call add-dependency,$(common_javalib.jar),\
-    $(my_proguard_dictionary_directory)/proguard_dictionary)
-  $(call add-dependency,$(common_javalib.jar),\
-    $(my_proguard_dictionary_mapping_directory)/proguard_dictionary.textproto)
-  $(call add-dependency,$(common_javalib.jar),\
-    $(my_proguard_dictionary_directory)/classes.jar)
+    $(intermediates.COMMON)/proguard_dictionary,\
+    $(intermediates.COMMON)/proguard_dictionary.textproto))
+
+  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_FILES := \
+    $(intermediates.COMMON)/proguard_dictionary \
+    $(LOCAL_SOONG_CLASSES_JAR)
+  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_SOONG_ZIP_ARGUMENTS := \
+    -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/proguard_dictionary \
+    -f $(intermediates.COMMON)/proguard_dictionary \
+    -e out/target/common/obj/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/classes.jar \
+    -f $(LOCAL_SOONG_CLASSES_JAR)
+  ALL_MODULES.$(my_register_name).PROGUARD_DICTIONARY_MAPPING := $(intermediates.COMMON)/proguard_dictionary.textproto
 endif
 
 ifdef LOCAL_SOONG_PROGUARD_USAGE_ZIP
-  $(eval $(call copy-one-file,$(LOCAL_SOONG_PROGUARD_USAGE_ZIP),\
-    $(call local-packaging-dir,proguard_usage)/proguard_usage.zip))
-  $(call add-dependency,$(common_javalib.jar),\
-    $(call local-packaging-dir,proguard_usage)/proguard_usage.zip)
+  ALL_MODULES.$(my_register_name).PROGUARD_USAGE_ZIP := $(LOCAL_SOONG_PROGUARD_USAGE_ZIP)
 endif
 
 
@@ -99,6 +92,17 @@
 
   $(call add-dependency,$(LOCAL_BUILT_MODULE),$(my_res_package))
 
+  my_transitive_res_packages := $(intermediates.COMMON)/transitive-res-packages
+  $(my_transitive_res_packages): PRIVATE_TRANSITIVE_RES_PACKAGES := $(filter-out $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE),$(LOCAL_SOONG_TRANSITIVE_RES_PACKAGES))
+  $(my_transitive_res_packages):
+	@echo Write transitive resource package list $@
+	rm -f $@
+	touch $@
+	$(foreach f,$(PRIVATE_TRANSITIVE_RES_PACKAGES),\
+	  echo "$f" >> $@; )
+
+  $(call add-dependency,$(my_res_package),$(my_transitive_res_packages))
+
   my_proguard_flags := $(intermediates.COMMON)/export_proguard_flags
   $(my_proguard_flags): $(LOCAL_SOONG_EXPORT_PROGUARD_FLAGS)
 	@echo "Export proguard flags: $@"
diff --git a/core/static_java_library.mk b/core/static_java_library.mk
index 4053985..4a72a1f 100644
--- a/core/static_java_library.mk
+++ b/core/static_java_library.mk
@@ -127,6 +127,12 @@
 endif
 endif
 
+# transitive-res-packages is only populated for Soong modules for now, but needs
+# to exist so that other Make modules can depend on it.  Create an empty file.
+my_transitive_res_packages := $(intermediates.COMMON)/transitive-res-packages
+$(my_transitive_res_packages):
+	touch $@
+
 import_proguard_flag_files := $(strip $(foreach l,$(LOCAL_STATIC_ANDROID_LIBRARIES) $(LOCAL_STATIC_JAVA_AAR_LIBRARIES),\
     $(call intermediates-dir-for,JAVA_LIBRARIES,$(l),,COMMON)/export_proguard_flags))
 $(intermediates.COMMON)/export_proguard_flags: $(import_proguard_flag_files) $(addprefix $(LOCAL_PATH)/,$(LOCAL_EXPORT_PROGUARD_FLAG_FILES))
diff --git a/core/sysprop.mk b/core/sysprop.mk
index a2296a8..4536e5f 100644
--- a/core/sysprop.mk
+++ b/core/sysprop.mk
@@ -47,9 +47,21 @@
         echo "ro.product.$(1).model=$(PRODUCT_MODEL)" >> $(2);\
         echo "ro.product.$(1).name=$(TARGET_PRODUCT)" >> $(2);\
         # Attestation specific properties for AOSP/GSI build running on device.
-        echo "ro.product.model_for_attestation=$(PRODUCT_MODEL_FOR_ATTESTATION)" >> $(2);\
-        echo "ro.product.brand_for_attestation=$(PRODUCT_BRAND_FOR_ATTESTATION)" >> $(2);\
-        echo "ro.product.name_for_attestation=$(PRODUCT_NAME_FOR_ATTESTATION)" >> $(2);\
+        if [ -n "$(strip $(PRODUCT_MODEL_FOR_ATTESTATION))" ]; then \
+            echo "ro.product.model_for_attestation=$(PRODUCT_MODEL_FOR_ATTESTATION)" >> $(2);\
+        fi; \
+        if [ -n "$(strip $(PRODUCT_BRAND_FOR_ATTESTATION))" ]; then \
+            echo "ro.product.brand_for_attestation=$(PRODUCT_BRAND_FOR_ATTESTATION)" >> $(2);\
+        fi; \
+        if [ -n "$(strip $(PRODUCT_NAME_FOR_ATTESTATION))" ]; then \
+            echo "ro.product.name_for_attestation=$(PRODUCT_NAME_FOR_ATTESTATION)" >> $(2);\
+        fi; \
+        if [ -n "$(strip $(PRODUCT_DEVICE_FOR_ATTESTATION))" ]; then \
+            echo "ro.product.device_for_attestation=$(PRODUCT_DEVICE_FOR_ATTESTATION)" >> $(2);\
+        fi; \
+        if [ -n "$(strip $(PRODUCT_MANUFACTURER_FOR_ATTESTATION))" ]; then \
+            echo "ro.product.manufacturer_for_attestation=$(PRODUCT_MANUFACTURER_FOR_ATTESTATION)" >> $(2);\
+        fi; \
     )\
     $(if $(filter true,$(ZYGOTE_FORCE_64)),\
         $(if $(filter vendor,$(1)),\
@@ -66,9 +78,12 @@
     )\
     echo "ro.$(1).build.date=`$(DATE_FROM_FILE)`" >> $(2);\
     echo "ro.$(1).build.date.utc=`$(DATE_FROM_FILE) +%s`" >> $(2);\
-    echo "ro.$(1).build.fingerprint=$(BUILD_FINGERPRINT_FROM_FILE)" >> $(2);\
-    echo "ro.$(1).build.id=$(BUILD_ID)" >> $(2);\
-    echo "ro.$(1).build.tags=$(BUILD_VERSION_TAGS)" >> $(2);\
+    # Allow optional assignments for ARC forward-declarations (b/249168657)
+    # TODO: Remove any tag-related inconsistencies once the goals from
+    # go/arc-android-sigprop-changes have been achieved.
+    echo "ro.$(1).build.fingerprint?=$(BUILD_FINGERPRINT_FROM_FILE)" >> $(2);\
+    echo "ro.$(1).build.id?=$(BUILD_ID)" >> $(2);\
+    echo "ro.$(1).build.tags?=$(BUILD_VERSION_TAGS)" >> $(2);\
     echo "ro.$(1).build.type=$(TARGET_BUILD_VARIANT)" >> $(2);\
     echo "ro.$(1).build.version.incremental=$(BUILD_NUMBER_FROM_FILE)" >> $(2);\
     echo "ro.$(1).build.version.release=$(PLATFORM_VERSION_LAST_STABLE)" >> $(2);\
@@ -565,4 +580,4 @@
 # $1 installed file path, e.g. out/target/product/vsoc_x86_64/system/build.prop
 define is-build-prop
 $(if $(findstring $1,$(ALL_INSTALLED_BUILD_PROP_FILES)),Y)
-endef
\ No newline at end of file
+endef
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index c8b1183..593b7b6 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -37,16 +37,18 @@
   cts_platform_release_path := cts/tests/tests/os/assets/platform_releases.txt
   cts_platform_release_string := $(shell cat $(cts_platform_release_path))
 
-  ifeq (,$(findstring $(PLATFORM_VERSION),$(cts_platform_version_string)))
-    define error_msg
-      ============================================================
-      Could not find version "$(PLATFORM_VERSION)" in CTS platform version file:
-      $(cts_platform_version_path)
-      Most likely PLATFORM_VERSION in build/core/version_defaults.mk
-      has changed and a new version must be added to this CTS file.
-      ============================================================
-    endef
-    $(error $(error_msg))
+  ifeq ($(RELEASE_PLATFORM_VERSION_CODENAME_REL),)
+    ifeq (,$(findstring $(PLATFORM_VERSION),$(cts_platform_version_string)))
+      define error_msg
+        ============================================================
+        Could not find version "$(PLATFORM_VERSION)" in CTS platform version file:
+        $(cts_platform_version_path)
+        Most likely PLATFORM_VERSION in build/core/version_defaults.mk
+        has changed and a new version must be added to this CTS file.
+        ============================================================
+      endef
+      $(error $(error_msg))
+    endif
   endif
   ifeq (,$(findstring $(PLATFORM_VERSION_LAST_STABLE),$(cts_platform_release_string)))
     define error_msg
diff --git a/core/tasks/module-info.mk b/core/tasks/module-info.mk
index 66ba8f1..9668b53 100644
--- a/core/tasks/module-info.mk
+++ b/core/tasks/module-info.mk
@@ -2,40 +2,40 @@
 # Currently runtime_dependencies only include the runtime libs information for cc binaries.
 
 MODULE_INFO_JSON := $(PRODUCT_OUT)/module-info.json
+COMMA := ,
+_NEWLINE := '\n'
 
 $(MODULE_INFO_JSON):
 	@echo Generating $@
 	$(hide) echo -ne '{\n ' > $@
-	$(hide) echo -ne $(foreach m, $(sort $(ALL_MODULES)), \
-		' "$(m)": {' \
-			'"class": [$(foreach w,$(sort $(ALL_MODULES.$(m).CLASS)),"$(w)", )], ' \
-			'"path": [$(foreach w,$(sort $(ALL_MODULES.$(m).PATH)),"$(w)", )], ' \
-			'"tags": [$(foreach w,$(sort $(ALL_MODULES.$(m).TAGS)),"$(w)", )], ' \
-			'"installed": [$(foreach w,$(sort $(ALL_MODULES.$(m).INSTALLED)),"$(w)", )], ' \
-			'"compatibility_suites": [$(foreach w,$(sort $(ALL_MODULES.$(m).COMPATIBILITY_SUITES)),"$(w)", )], ' \
-			'"auto_test_config": [$(ALL_MODULES.$(m).auto_test_config)], ' \
-			'"module_name": "$(ALL_MODULES.$(m).MODULE_NAME)", ' \
-			'"test_config": [$(foreach w,$(strip $(ALL_MODULES.$(m).TEST_CONFIG) $(ALL_MODULES.$(m).EXTRA_TEST_CONFIGS)),"$(w)", )], ' \
-			'"dependencies": [$(foreach w,$(sort $(ALL_DEPS.$(m).ALL_DEPS)),"$(w)", )], ' \
-			'"shared_libs": [$(foreach w,$(sort $(ALL_MODULES.$(m).SHARED_LIBS)),"$(w)", )], ' \
-			'"static_libs": [$(foreach w,$(sort $(ALL_MODULES.$(m).STATIC_LIBS)),"$(w)", )], ' \
-			'"system_shared_libs": [$(foreach w,$(sort $(ALL_MODULES.$(m).SYSTEM_SHARED_LIBS)),"$(w)", )], ' \
-			'"srcs": [$(foreach w,$(sort $(ALL_MODULES.$(m).SRCS)),"$(w)", )], ' \
-			'"srcjars": [$(foreach w,$(sort $(ALL_MODULES.$(m).SRCJARS)),"$(w)", )], ' \
-			'"classes_jar": [$(foreach w,$(sort $(ALL_MODULES.$(m).CLASSES_JAR)),"$(w)", )], ' \
-			'"test_mainline_modules": [$(foreach w,$(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES)),"$(w)", )], ' \
-			'"is_unit_test": "$(ALL_MODULES.$(m).IS_UNIT_TEST)", ' \
-			'"test_options_tags": [$(foreach w,$(sort $(ALL_MODULES.$(m).TEST_OPTIONS_TAGS)),"$(w)", )], ' \
-			'"data": [$(foreach w,$(sort $(ALL_MODULES.$(m).TEST_DATA)),"$(w)", )], ' \
-			'"runtime_dependencies": [$(foreach w,$(sort $(ALL_MODULES.$(m).LOCAL_RUNTIME_LIBRARIES)),"$(w)", )], ' \
-			'"static_dependencies": [$(foreach w,$(sort $(ALL_MODULES.$(m).LOCAL_STATIC_LIBRARIES)),"$(w)", )], ' \
-			'"data_dependencies": [$(foreach w,$(sort $(ALL_MODULES.$(m).TEST_DATA_BINS)),"$(w)", )], ' \
-			'"supported_variants": [$(foreach w,$(sort $(ALL_MODULES.$(m).SUPPORTED_VARIANTS)),"$(w)", )], ' \
-			'"host_dependencies": [$(foreach w,$(sort $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET)),"$(w)", )], ' \
-			'"target_dependencies": [$(foreach w,$(sort $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST)),"$(w)", )], ' \
-			'},\n' \
-	 ) | sed -e 's/, *\]/]/g' -e 's/, *\}/ }/g' -e '$$s/,$$//' >> $@
-	$(hide) echo '}' >> $@
+	$(hide) echo -ne $(KATI_foreach_sep m,$(COMMA)$(_NEWLINE), $(sort $(ALL_MODULES)),\
+		'"$(m)": {' \
+			'"class": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).CLASS)),"$(w)")],' \
+			'"path": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).PATH)),"$(w)")],' \
+			'"tags": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).TAGS)),"$(w)")],' \
+			'"installed": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).INSTALLED)),"$(w)")],' \
+			'"compatibility_suites": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).COMPATIBILITY_SUITES)),"$(w)")],' \
+			'"auto_test_config": [$(ALL_MODULES.$(m).auto_test_config)],' \
+			'"module_name": "$(ALL_MODULES.$(m).MODULE_NAME)"$(COMMA)' \
+			'"test_config": [$(KATI_foreach_sep w,$(COMMA) ,$(strip $(ALL_MODULES.$(m).TEST_CONFIG) $(ALL_MODULES.$(m).EXTRA_TEST_CONFIGS)),"$(w)")],' \
+			'"dependencies": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_DEPS.$(m).ALL_DEPS)),"$(w)")],' \
+			'"shared_libs": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).SHARED_LIBS)),"$(w)")],' \
+			'"static_libs": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).STATIC_LIBS)),"$(w)")],' \
+			'"system_shared_libs": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).SYSTEM_SHARED_LIBS)),"$(w)")],' \
+			'"srcs": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).SRCS)),"$(w)")],' \
+			'"srcjars": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).SRCJARS)),"$(w)")],' \
+			'"classes_jar": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).CLASSES_JAR)),"$(w)")],' \
+			'"test_mainline_modules": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES)),"$(w)")],' \
+			'"is_unit_test": "$(ALL_MODULES.$(m).IS_UNIT_TEST)"$(COMMA)' \
+			'"test_options_tags": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).TEST_OPTIONS_TAGS)),"$(w)")],' \
+			'"data": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).TEST_DATA)),"$(w)")],' \
+			'"runtime_dependencies": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).LOCAL_RUNTIME_LIBRARIES)),"$(w)")],' \
+			'"static_dependencies": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).LOCAL_STATIC_LIBRARIES)),"$(w)")],' \
+			'"data_dependencies": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).TEST_DATA_BINS)),"$(w)")],' \
+			'"supported_variants": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).SUPPORTED_VARIANTS)),"$(w)")],' \
+			'"host_dependencies": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET)),"$(w)")],' \
+			'"target_dependencies": [$(KATI_foreach_sep w,$(COMMA) ,$(sort $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST)),"$(w)")]' \
+			'}')'\n}\n' >> $@
 
 
 droidcore-unbundled: $(MODULE_INFO_JSON)
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index c107254..99d69d1 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -52,6 +52,8 @@
 
 # These are the current development codenames, if the build is not a final
 # release build.  If this is a final release build, it is simply "REL".
+# Note that this may be overridden by RELEASE_VERSION_CODENAME_REL in
+# version_util.mk.
 PLATFORM_VERSION_CODENAME.UP1A := UpsideDownCake
 PLATFORM_VERSION_CODENAME.VP1A := VanillaIceCream
 
@@ -103,7 +105,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 := 2023-06-05
+    PLATFORM_SECURITY_PATCH := 2023-07-05
 endif
 
 include $(BUILD_SYSTEM)/version_util.mk
diff --git a/core/version_util.mk b/core/version_util.mk
index d3fcdc2..457f0f7 100644
--- a/core/version_util.mk
+++ b/core/version_util.mk
@@ -56,6 +56,12 @@
 # unreleased API level targetable by this branch, not just those that are valid
 # lunch targets for this branch.
 
+# Release config flag to override the current version to REL.  Note that the
+# codename can also be locked to REL by setting it in versino_defaults.mk.
+ifneq ($(RELEASE_PLATFORM_VERSION_CODENAME_REL),)
+  PLATFORM_VERSION_CODENAME.$(TARGET_PLATFORM_VERSION) := REL
+endif
+
 PLATFORM_VERSION_CODENAME := $(PLATFORM_VERSION_CODENAME.$(TARGET_PLATFORM_VERSION))
 ifndef PLATFORM_VERSION_CODENAME
   # PLATFORM_VERSION_CODENAME falls back to TARGET_PLATFORM_VERSION
diff --git a/envsetup.sh b/envsetup.sh
index f4755dd..0a90460 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -1075,6 +1075,10 @@
     echo "can't find Android.mk"
 }
 
+function adb() {
+   command adb "${@}"
+}
+
 # simplified version of ps; output in the form
 # <pid> <procname>
 function qpid() {
@@ -1358,53 +1362,6 @@
     get_abs_build_var ANDROID_PREBUILTS
 }
 
-function tracedmdump()
-{
-    local T=$(gettop)
-    if [ ! "$T" ]; then
-        echo "Couldn't locate the top of the tree.  Try setting TOP."
-        return
-    fi
-    local prebuiltdir=$(getprebuilt)
-    local arch=$(gettargetarch)
-    local KERNEL=$T/prebuilts/qemu-kernel/$arch/vmlinux-qemu
-
-    local TRACE=$1
-    if [ ! "$TRACE" ] ; then
-        echo "usage:  tracedmdump  tracename"
-        return
-    fi
-
-    if [ ! -r "$KERNEL" ] ; then
-        echo "Error: cannot find kernel: '$KERNEL'"
-        return
-    fi
-
-    local BASETRACE=$(basename $TRACE)
-    if [ "$BASETRACE" = "$TRACE" ] ; then
-        TRACE=$ANDROID_PRODUCT_OUT/traces/$TRACE
-    fi
-
-    echo "post-processing traces..."
-    rm -f $TRACE/qtrace.dexlist
-    post_trace $TRACE
-    if [ $? -ne 0 ]; then
-        echo "***"
-        echo "*** Error: malformed trace.  Did you remember to exit the emulator?"
-        echo "***"
-        return
-    fi
-    echo "generating dexlist output..."
-    /bin/ls $ANDROID_PRODUCT_OUT/system/framework/*.jar $ANDROID_PRODUCT_OUT/system/app/*.apk $ANDROID_PRODUCT_OUT/data/app/*.apk 2>/dev/null | xargs dexlist > $TRACE/qtrace.dexlist
-    echo "generating dmtrace data..."
-    q2dm -r $ANDROID_PRODUCT_OUT/symbols $TRACE $KERNEL $TRACE/dmtrace || return
-    echo "generating html file..."
-    dmtracedump -h $TRACE/dmtrace >| $TRACE/dmtrace.html || return
-    echo "done, see $TRACE/dmtrace.html for details"
-    echo "or run:"
-    echo "    traceview $TRACE/dmtrace"
-}
-
 # communicate with a running device or emulator, set up necessary state,
 # and run the hat command.
 function runhat()
diff --git a/target/board/generic/BoardConfig.mk b/target/board/generic/BoardConfig.mk
index 87c16da..6720ddb 100644
--- a/target/board/generic/BoardConfig.mk
+++ b/target/board/generic/BoardConfig.mk
@@ -30,20 +30,3 @@
 TARGET_CPU_ABI2 := armeabi
 
 include build/make/target/board/BoardConfigGsiCommon.mk
-
-ifndef BUILDING_GSI
-include build/make/target/board/BoardConfigEmuCommon.mk
-
-BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800
-
-# Wifi.
-BOARD_WLAN_DEVICE           := emulator
-BOARD_HOSTAPD_DRIVER        := NL80211
-BOARD_WPA_SUPPLICANT_DRIVER := NL80211
-BOARD_HOSTAPD_PRIVATE_LIB   := lib_driver_cmd_simulated
-BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated
-WPA_SUPPLICANT_VERSION      := VER_0_8_X
-WIFI_DRIVER_FW_PATH_PARAM   := "/dev/null"
-WIFI_DRIVER_FW_PATH_STA     := "/dev/null"
-WIFI_DRIVER_FW_PATH_AP      := "/dev/null"
-endif
diff --git a/target/board/generic/device.mk b/target/board/generic/device.mk
index 76242c9..76edf6b 100644
--- a/target/board/generic/device.mk
+++ b/target/board/generic/device.mk
@@ -14,5 +14,3 @@
 # limitations under the License.
 #
 
-PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu
-PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps.
diff --git a/target/board/generic_64bitonly_x86_64/BoardConfig.mk b/target/board/generic_64bitonly_x86_64/BoardConfig.mk
index a240eab..a129ea0 100644
--- a/target/board/generic_64bitonly_x86_64/BoardConfig.mk
+++ b/target/board/generic_64bitonly_x86_64/BoardConfig.mk
@@ -28,23 +28,3 @@
 TARGET_PRELINK_MODULE := false
 
 include build/make/target/board/BoardConfigGsiCommon.mk
-
-ifndef BUILDING_GSI
-include build/make/target/board/BoardConfigEmuCommon.mk
-
-BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800
-
-BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86
-
-# Wifi.
-BOARD_WLAN_DEVICE           := emulator
-BOARD_HOSTAPD_DRIVER        := NL80211
-BOARD_WPA_SUPPLICANT_DRIVER := NL80211
-BOARD_HOSTAPD_PRIVATE_LIB   := lib_driver_cmd_simulated
-BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated
-WPA_SUPPLICANT_VERSION      := VER_0_8_X
-WIFI_DRIVER_FW_PATH_PARAM   := "/dev/null"
-WIFI_DRIVER_FW_PATH_STA     := "/dev/null"
-WIFI_DRIVER_FW_PATH_AP      := "/dev/null"
-
-endif # !BUILDING_GSI
diff --git a/target/board/generic_x86/BoardConfig.mk b/target/board/generic_x86/BoardConfig.mk
index 47fd384..26bede8 100644
--- a/target/board/generic_x86/BoardConfig.mk
+++ b/target/board/generic_x86/BoardConfig.mk
@@ -19,23 +19,3 @@
 TARGET_ARCH_VARIANT := x86
 
 include build/make/target/board/BoardConfigGsiCommon.mk
-
-ifndef BUILDING_GSI
-include build/make/target/board/BoardConfigEmuCommon.mk
-
-# Resize to 4G to accomodate ASAN and CTS
-BOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296
-
-BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86
-
-# Wifi.
-BOARD_WLAN_DEVICE           := emulator
-BOARD_HOSTAPD_DRIVER        := NL80211
-BOARD_WPA_SUPPLICANT_DRIVER := NL80211
-BOARD_HOSTAPD_PRIVATE_LIB   := lib_driver_cmd_simulated
-BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated
-WPA_SUPPLICANT_VERSION      := VER_0_8_X
-WIFI_DRIVER_FW_PATH_PARAM   := "/dev/null"
-WIFI_DRIVER_FW_PATH_STA     := "/dev/null"
-WIFI_DRIVER_FW_PATH_AP      := "/dev/null"
-endif
diff --git a/target/board/generic_x86/device.mk b/target/board/generic_x86/device.mk
index 5ad008f..60f0cc3 100644
--- a/target/board/generic_x86/device.mk
+++ b/target/board/generic_x86/device.mk
@@ -14,9 +14,6 @@
 # limitations under the License.
 #
 
-PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu
-PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps.
-
 ifdef NET_ETH0_STARTONBOOT
   PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1
 endif
diff --git a/target/board/generic_x86_64/BoardConfig.mk b/target/board/generic_x86_64/BoardConfig.mk
old mode 100755
new mode 100644
index 36136f4..64da578
--- a/target/board/generic_x86_64/BoardConfig.mk
+++ b/target/board/generic_x86_64/BoardConfig.mk
@@ -28,23 +28,3 @@
 TARGET_DYNAMIC_64_32_DRMSERVER := true
 
 include build/make/target/board/BoardConfigGsiCommon.mk
-
-ifndef BUILDING_GSI
-include build/make/target/board/BoardConfigEmuCommon.mk
-
-BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800
-
-BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86
-
-# Wifi.
-BOARD_WLAN_DEVICE           := emulator
-BOARD_HOSTAPD_DRIVER        := NL80211
-BOARD_WPA_SUPPLICANT_DRIVER := NL80211
-BOARD_HOSTAPD_PRIVATE_LIB   := lib_driver_cmd_simulated
-BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated
-WPA_SUPPLICANT_VERSION      := VER_0_8_X
-WIFI_DRIVER_FW_PATH_PARAM   := "/dev/null"
-WIFI_DRIVER_FW_PATH_STA     := "/dev/null"
-WIFI_DRIVER_FW_PATH_AP      := "/dev/null"
-
-endif # !BUILDING_GSI
diff --git a/target/board/generic_x86_64_arm64/BoardConfig.mk b/target/board/generic_x86_64_arm64/BoardConfig.mk
old mode 100755
new mode 100644
index f528294..818ec44
--- a/target/board/generic_x86_64_arm64/BoardConfig.mk
+++ b/target/board/generic_x86_64_arm64/BoardConfig.mk
@@ -13,7 +13,6 @@
 # limitations under the License.
 #
 
-# x86_64 emulator specific definitions
 TARGET_CPU_ABI := x86_64
 TARGET_ARCH := x86_64
 TARGET_ARCH_VARIANT := x86_64
@@ -37,23 +36,9 @@
 TARGET_PRELINK_MODULE := false
 
 include build/make/target/board/BoardConfigMainlineCommon.mk
-include build/make/target/board/BoardConfigEmuCommon.mk
 
 # the settings differ from BoardConfigMainlineCommon.mk
 BOARD_USES_SYSTEM_OTHER_ODEX :=
 
 # Resize to 4G to accommodate ASAN and CTS
 BOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296
-
-BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86
-
-# Wifi.
-BOARD_WLAN_DEVICE           := emulator
-BOARD_HOSTAPD_DRIVER        := NL80211
-BOARD_WPA_SUPPLICANT_DRIVER := NL80211
-BOARD_HOSTAPD_PRIVATE_LIB   := lib_driver_cmd_simulated
-BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated
-WPA_SUPPLICANT_VERSION      := VER_0_8_X
-WIFI_DRIVER_FW_PATH_PARAM   := "/dev/null"
-WIFI_DRIVER_FW_PATH_STA     := "/dev/null"
-WIFI_DRIVER_FW_PATH_AP      := "/dev/null"
diff --git a/target/board/generic_x86_64_arm64/device.mk b/target/board/generic_x86_64_arm64/device.mk
old mode 100755
new mode 100644
index 76242c9..76edf6b
--- a/target/board/generic_x86_64_arm64/device.mk
+++ b/target/board/generic_x86_64_arm64/device.mk
@@ -14,5 +14,3 @@
 # limitations under the License.
 #
 
-PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu
-PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps.
diff --git a/target/board/generic_x86_arm/BoardConfig.mk b/target/board/generic_x86_arm/BoardConfig.mk
index f6589b0..62bb5eb 100644
--- a/target/board/generic_x86_arm/BoardConfig.mk
+++ b/target/board/generic_x86_arm/BoardConfig.mk
@@ -13,7 +13,6 @@
 # limitations under the License.
 #
 
-# x86 emulator specific definitions
 TARGET_CPU_ABI := x86
 TARGET_ARCH := x86
 TARGET_ARCH_VARIANT := x86
@@ -30,23 +29,9 @@
 # The settings in latter makefiles overwrite those in the former.
 #
 include build/make/target/board/BoardConfigMainlineCommon.mk
-include build/make/target/board/BoardConfigEmuCommon.mk
 
 # the settings differ from BoardConfigMainlineCommon.mk
 BOARD_USES_SYSTEM_OTHER_ODEX :=
 
 # Resize to 4G to accomodate ASAN and CTS
 BOARD_USERDATAIMAGE_PARTITION_SIZE := 4294967296
-
-BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86
-
-# Wifi.
-BOARD_WLAN_DEVICE           := emulator
-BOARD_HOSTAPD_DRIVER        := NL80211
-BOARD_WPA_SUPPLICANT_DRIVER := NL80211
-BOARD_HOSTAPD_PRIVATE_LIB   := lib_driver_cmd_simulated
-BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated
-WPA_SUPPLICANT_VERSION      := VER_0_8_X
-WIFI_DRIVER_FW_PATH_PARAM   := "/dev/null"
-WIFI_DRIVER_FW_PATH_STA     := "/dev/null"
-WIFI_DRIVER_FW_PATH_AP      := "/dev/null"
diff --git a/target/board/generic_x86_arm/device.mk b/target/board/generic_x86_arm/device.mk
index 76242c9..76edf6b 100644
--- a/target/board/generic_x86_arm/device.mk
+++ b/target/board/generic_x86_arm/device.mk
@@ -14,5 +14,3 @@
 # limitations under the License.
 #
 
-PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu
-PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps.
diff --git a/target/product/aosp_64bitonly_x86_64.mk b/target/product/aosp_64bitonly_x86_64.mk
index 75fd3c8..cf812a2 100644
--- a/target/product/aosp_64bitonly_x86_64.mk
+++ b/target/product/aosp_64bitonly_x86_64.mk
@@ -51,7 +51,6 @@
 #
 # All components inherited here go to vendor image
 #
-$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk)
 
 #
diff --git a/target/product/aosp_arm.mk b/target/product/aosp_arm.mk
index 61c1316..d9c362e 100644
--- a/target/product/aosp_arm.mk
+++ b/target/product/aosp_arm.mk
@@ -50,7 +50,6 @@
 # All components inherited here go to vendor image
 #
 $(call inherit-product-if-exists, build/make/target/product/ramdisk_stub.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk)
 
 #
diff --git a/target/product/aosp_arm64.mk b/target/product/aosp_arm64.mk
index 6c907db..d3514a5 100644
--- a/target/product/aosp_arm64.mk
+++ b/target/product/aosp_arm64.mk
@@ -54,7 +54,6 @@
 #
 # All components inherited here go to vendor or vendor_boot image
 #
-$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_arm64/device.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk)
 
diff --git a/target/product/aosp_riscv64.mk b/target/product/aosp_riscv64.mk
index 270a989..fa503ff 100644
--- a/target/product/aosp_riscv64.mk
+++ b/target/product/aosp_riscv64.mk
@@ -46,7 +46,6 @@
 #
 # All components inherited here go to vendor image
 #
-$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_riscv64/device.mk)
 
 #
diff --git a/target/product/aosp_x86.mk b/target/product/aosp_x86.mk
index a2f0390..c26a8bf 100644
--- a/target/product/aosp_x86.mk
+++ b/target/product/aosp_x86.mk
@@ -47,7 +47,6 @@
 #
 # All components inherited here go to vendor image
 #
-$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk)
 
 
diff --git a/target/product/aosp_x86_64.mk b/target/product/aosp_x86_64.mk
index 535ee3f..3040dd3 100644
--- a/target/product/aosp_x86_64.mk
+++ b/target/product/aosp_x86_64.mk
@@ -56,7 +56,6 @@
 #
 # All components inherited here go to vendor image
 #
-$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk)
 
diff --git a/target/product/aosp_x86_arm.mk b/target/product/aosp_x86_arm.mk
index 39ad0d8..a103b1a 100644
--- a/target/product/aosp_x86_arm.mk
+++ b/target/product/aosp_x86_arm.mk
@@ -45,7 +45,6 @@
 #
 # All components inherited here go to vendor image
 #
-$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_arm/device.mk)
 
 
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 7fc33b0..c74d0a3 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -19,7 +19,6 @@
     abx \
     adbd_system_api \
     am \
-    android.hidl.allocator@1.0-service \
     android.hidl.base-V1.0-java \
     android.hidl.manager-V1.0-java \
     android.hidl.memory@1.0-impl \
@@ -70,7 +69,6 @@
     com.android.scheduling \
     com.android.sdkext \
     com.android.tethering \
-    com.android.threadnetwork \
     com.android.tzdata \
     com.android.uwb \
     com.android.virt \
@@ -89,7 +87,6 @@
     dump.erofs \
     dumpstate \
     dumpsys \
-    DynamicSystemInstallationService \
     e2fsck \
     ExtShared \
     flags_health_check \
@@ -110,7 +107,6 @@
     gatekeeperd \
     gpuservice \
     hid \
-    hwservicemanager \
     idmap2 \
     idmap2d \
     ime \
@@ -298,11 +294,26 @@
 
 endif
 
+# Product does not support Dynamic System Update
+ifneq ($(PRODUCT_NO_DYNAMIC_SYSTEM_UPDATE),true)
+    PRODUCT_PACKAGES += \
+        DynamicSystemInstallationService \
+
+endif
+
 # VINTF data for system image
 PRODUCT_PACKAGES += \
     system_manifest.xml \
     system_compatibility_matrix.xml \
 
+HIDL_SUPPORT_SERVICES := \
+    hwservicemanager \
+    android.hidl.allocator@1.0-service \
+
+# Base modules when shipping api level is less than or equal to 34
+PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 += \
+    $(HIDL_SUPPORT_SERVICES) \
+
 PRODUCT_PACKAGES_ARM64 := libclang_rt.hwasan \
  libclang_rt.hwasan.bootstrap \
  libc_hwasan \
@@ -333,6 +344,7 @@
 PRODUCT_HOST_PACKAGES += \
     BugReport \
     adb \
+    adevice \
     art-tools \
     atest \
     bcc \
@@ -348,7 +360,6 @@
     incident_report \
     ld.mc \
     lpdump \
-    minigzip \
     mke2fs \
     mkfs.erofs \
     resize2fs \
@@ -381,6 +392,7 @@
 # Packages included only for eng or userdebug builds, previously debug tagged
 PRODUCT_PACKAGES_DEBUG := \
     adb_keys \
+    adevice_fingerprint \
     arping \
     dmuserd \
     idlcli \
@@ -427,3 +439,6 @@
     frameworks/base/config/dirty-image-objects:system/etc/dirty-image-objects)
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/runtime_libart.mk)
+
+# Use "image" APEXes always.
+$(call inherit-product,$(SRC_TARGET_DIR)/product/updatable_apex.mk)
diff --git a/target/product/cfi-common.mk b/target/product/cfi-common.mk
index 5cc7ae5..559963c 100644
--- a/target/product/cfi-common.mk
+++ b/target/product/cfi-common.mk
@@ -33,7 +33,7 @@
     hardware/qcom/wlan/wcn6740/qcwcn/wpa_supplicant_8_lib \
     hardware/interfaces/keymaster \
     hardware/interfaces/security \
-    packages/modules/Bluetooth \
+    packages/modules/Bluetooth/system \
     system/chre \
     system/core/libnetutils \
     system/libziparchive \
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index 1e28c80..3ca4187 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -38,6 +38,14 @@
 PRODUCT_BOOT_JARS := \
     $(ART_APEX_JARS)
 
+# List of jars to be included in the ART boot image for testing.
+# DO NOT reorder this list. The order must match the one described above.
+# Note: We use the host variant of "core-icu4j" and "conscrypt" for testing.
+PRODUCT_TEST_ONLY_ART_BOOT_IMAGE_JARS := \
+    $(ART_APEX_JARS) \
+    platform:core-icu4j-host \
+    platform:conscrypt-host \
+
 # /system and /system_ext boot jars.
 PRODUCT_BOOT_JARS += \
     framework-minus-apex \
@@ -106,6 +114,13 @@
     com.android.uwb:service-uwb \
     com.android.wifi:service-wifi \
 
+# Overrides the (apex, jar) pairs above when determining the on-device location. The format is:
+# <old_apex>:<old_jar>:<new_apex>:<new_jar>
+PRODUCT_CONFIGURED_JAR_LOCATION_OVERRIDES := \
+    platform:framework-minus-apex:platform:framework \
+    platform:core-icu4j-host:com.android.i18n:core-icu4j \
+    platform:conscrypt-host:com.android.conscrypt:conscrypt \
+
 # Minimal configuration for running dex2oat (default argument values).
 # PRODUCT_USES_DEFAULT_ART_CONFIG must be true to enable boot image compilation.
 PRODUCT_USES_DEFAULT_ART_CONFIG := true
diff --git a/target/product/full.manifest.xml b/target/product/full.manifest.xml
new file mode 100644
index 0000000..b8b0d37
--- /dev/null
+++ b/target/product/full.manifest.xml
@@ -0,0 +1,2 @@
+<manifest version="1.0" type="device" target-level="7">
+</manifest>
diff --git a/target/product/full.mk b/target/product/full.mk
index 945957f..da04f49 100644
--- a/target/product/full.mk
+++ b/target/product/full.mk
@@ -20,10 +20,11 @@
 # entirely appropriate to inherit from for on-device configurations.
 
 $(call inherit-product-if-exists, build/make/target/product/ramdisk_stub.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic/device.mk)
 
+DEVICE_MANIFEST_FILE += build/make/target/product/full.manifest.xml
+
 # Enable dynamic partition size
 PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true
 
diff --git a/target/product/full_x86.mk b/target/product/full_x86.mk
index 0f3be91..07f6472 100644
--- a/target/product/full_x86.mk
+++ b/target/product/full_x86.mk
@@ -23,10 +23,11 @@
 # that isn't a wifi connection. This will instruct init.rc to enable the
 # network connection so that you can use it with ADB
 
-$(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk)
 
+DEVICE_MANIFEST_FILE += build/make/target/product/full.manifest.xml
+
 ifdef NET_ETH0_STARTONBOOT
   PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1
 endif
diff --git a/target/product/generic.mk b/target/product/generic.mk
index fb5b727..fd3b3fb 100644
--- a/target/product/generic.mk
+++ b/target/product/generic.mk
@@ -14,9 +14,6 @@
 # limitations under the License.
 #
 
-PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu
-PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps.
-
 # This is a generic phone product that isn't specialized for a specific device.
 # It includes the base Android platform.
 
diff --git a/target/product/generic_system.mk b/target/product/generic_system.mk
index 1a639ef..f194d8b 100644
--- a/target/product/generic_system.mk
+++ b/target/product/generic_system.mk
@@ -102,6 +102,11 @@
     libaudiopolicyengineconfigurable \
     libpolicy-subsystem
 
+
+ifneq ($(KEEP_VNDK),true)
+PRODUCT_PACKAGES += llndk.libraries.txt
+endif
+
 # Include all zygote init scripts. "ro.zygote" will select one of them.
 PRODUCT_COPY_FILES += \
     system/core/rootdir/init.zygote32.rc:system/etc/init/hw/init.zygote32.rc \
diff --git a/target/product/gsi/Android.mk b/target/product/gsi/Android.mk
index 86d4622..adeb159 100644
--- a/target/product/gsi/Android.mk
+++ b/target/product/gsi/Android.mk
@@ -195,7 +195,10 @@
 include $(CLEAR_VARS)
 _vndk_versions :=
 ifeq ($(filter com.android.vndk.current.on_vendor, $(PRODUCT_PACKAGES)),)
-	_vndk_versions += $(PRODUCT_EXTRA_VNDK_VERSIONS)
+	_vndk_versions += $(if $(call math_is_number,$(PLATFORM_VNDK_VERSION)),\
+		$(foreach vndk_ver,$(PRODUCT_EXTRA_VNDK_VERSIONS),\
+			$(if $(call math_lt,$(vndk_ver),$(PLATFORM_VNDK_VERSION)),$(vndk_ver))),\
+		$(PRODUCT_EXTRA_VNDK_VERSIONS))
 endif
 ifneq ($(BOARD_VNDK_VERSION),current)
 	_vndk_versions += $(BOARD_VNDK_VERSION)
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index e39af92..bd85b9f 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -28,10 +28,15 @@
 
 BUILDING_GSI := true
 
-# Exclude all files under system/product and system/system_ext
+# Exclude all files under system/product and system/system_ext,
+# and the vndk apex's compat symlinks
 PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
     system/product/% \
-    system/system_ext/%
+    system/system_ext/% \
+    system/lib/vndk-29 \
+    system/lib/vndk-sp-29 \
+    system/lib64/vndk-29 \
+    system/lib64/vndk-sp-29
 
 # GSI should always support up-to-date platform features.
 # Keep this value at the latest API level to ensure latest build system
diff --git a/target/product/sdk.mk b/target/product/sdk.mk
index fa7e1ad..e4cb7ff 100644
--- a/target/product/sdk.mk
+++ b/target/product/sdk.mk
@@ -14,11 +14,8 @@
 # limitations under the License.
 #
 
-# This is a simple product that uses configures the minimum amount
-# needed to build the SDK (without the emulator).
+# Don't modify this file - It's just an alias!
 
-$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_x86.mk)
 
 PRODUCT_NAME := sdk
-PRODUCT_BRAND := Android
-PRODUCT_DEVICE := mainline_x86
diff --git a/target/product/sdk_arm64.mk b/target/product/sdk_arm64.mk
index 8bb38f4..497f673 100644
--- a/target/product/sdk_arm64.mk
+++ b/target/product/sdk_arm64.mk
@@ -14,8 +14,11 @@
 # limitations under the License.
 #
 
-# Don't modify this file - It's just an alias!
+$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk)
 
-$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_phone_arm64.mk)
+TARGET_SUPPORTS_32_BIT_APPS := true
+TARGET_SUPPORTS_64_BIT_APPS := true
 
 PRODUCT_NAME := sdk_arm64
+PRODUCT_BRAND := Android
+PRODUCT_DEVICE := mainline_arm64
diff --git a/target/product/sdk_x86.mk b/target/product/sdk_x86.mk
index 13ee57d..843321f 100644
--- a/target/product/sdk_x86.mk
+++ b/target/product/sdk_x86.mk
@@ -14,8 +14,11 @@
 # limitations under the License.
 #
 
-# Don't modify this file - It's just an alias!
+# This is a simple product that uses configures the minimum amount
+# needed to build the SDK (without the emulator).
 
-$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_phone_x86.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk)
 
-PRODUCT_NAME := sdk_x86
+PRODUCT_NAME := sdk_x86_64
+PRODUCT_BRAND := Android
+PRODUCT_DEVICE := mainline_x86
diff --git a/target/product/sdk_x86_64.mk b/target/product/sdk_x86_64.mk
index 5f6553e..c1caf7e 100644
--- a/target/product/sdk_x86_64.mk
+++ b/target/product/sdk_x86_64.mk
@@ -14,8 +14,14 @@
 # limitations under the License.
 #
 
-# Don't modify this file - It's just an alias!
+# This is a simple product that uses configures the minimum amount
+# needed to build the SDK (without the emulator).
 
-$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_phone_x86_64.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk)
+
+TARGET_SUPPORTS_32_BIT_APPS := true
+TARGET_SUPPORTS_64_BIT_APPS := true
 
 PRODUCT_NAME := sdk_x86_64
+PRODUCT_BRAND := Android
+PRODUCT_DEVICE := mainline_x86_64
diff --git a/target/product/updatable_apex.mk b/target/product/updatable_apex.mk
index c19982b..8357fdf 100644
--- a/target/product/updatable_apex.mk
+++ b/target/product/updatable_apex.mk
@@ -14,17 +14,13 @@
 # limitations under the License.
 #
 
-# Inherit this when the target needs to support updating APEXes
+# com.android.apex.cts.shim.v1_prebuilt overrides CtsShimPrebuilt
+# and CtsShimPrivPrebuilt since they are packaged inside the APEX.
+PRODUCT_PACKAGES += com.android.apex.cts.shim.v1_prebuilt
+PRODUCT_SYSTEM_PROPERTIES := ro.apex.updatable=true
 
-ifneq ($(OVERRIDE_TARGET_FLATTEN_APEX),true)
-  # com.android.apex.cts.shim.v1_prebuilt overrides CtsShimPrebuilt
-  # and CtsShimPrivPrebuilt since they are packaged inside the APEX.
-  PRODUCT_PACKAGES += com.android.apex.cts.shim.v1_prebuilt
-  PRODUCT_SYSTEM_PROPERTIES := ro.apex.updatable=true
-  TARGET_FLATTEN_APEX := false
-  # Use compressed apexes in pre-installed partitions.
-  # Note: this doesn't mean that all pre-installed apexes will be compressed.
-  #  Whether an apex is compressed or not is controlled at apex Soong module
-  #  via compresible property.
-  PRODUCT_COMPRESSED_APEX := true
-endif
+# Use compressed apexes in pre-installed partitions.
+# Note: this doesn't mean that all pre-installed apexes will be compressed.
+#  Whether an apex is compressed or not is controlled at apex Soong module
+#  via compresible property.
+PRODUCT_COMPRESSED_APEX := true
diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel
index 0de178b..2dbb585 100644
--- a/tools/BUILD.bazel
+++ b/tools/BUILD.bazel
@@ -1,6 +1,7 @@
 py_library(
     name = "event_log_tags",
     srcs = ["event_log_tags.py"],
+    imports = ["."],
 )
 
 py_binary(
diff --git a/tools/aconfig/Android.bp b/tools/aconfig/Android.bp
index c349907..28bf8a5 100644
--- a/tools/aconfig/Android.bp
+++ b/tools/aconfig/Android.bp
@@ -24,6 +24,14 @@
     },
 }
 
+python_library_host {
+    name: "libaconfig_python_proto",
+    srcs: ["protos/aconfig.proto"],
+    proto: {
+        canonical_path_from_root: false,
+    },
+}
+
 // host binary: aconfig
 
 rust_protobuf_host {
@@ -67,7 +75,7 @@
     ],
 }
 
-// integration tests: java
+// integration tests: general
 
 aconfig_declarations {
     name: "aconfig.test.flags",
@@ -91,22 +99,97 @@
     ],
 }
 
+// integration tests: java
+
 java_aconfig_library {
-    name: "aconfig_test_java",
+    name: "aconfig_test_java_library",
     aconfig_declarations: "aconfig.test.flags",
 }
 
 android_test {
     name: "aconfig.test.java",
     srcs: [
-        "tests/**/*.java",
+        "tests/AconfigTest.java",
     ],
     manifest: "tests/AndroidManifest.xml",
     certificate: "platform",
     static_libs: [
+        "aconfig_test_java_library",
         "androidx.test.rules",
         "testng",
-        "aconfig_test_java",
     ],
     test_suites: ["device-tests"],
 }
+
+java_aconfig_library {
+    name: "aconfig_host_test_java_library",
+    aconfig_declarations: "aconfig.test.flags",
+    host_supported: true,
+    test: true,
+}
+
+java_test_host {
+    name: "AconfigJavaHostTest",
+    srcs: [
+        "tests/AconfigHostTest.java",
+    ],
+    static_libs: [
+        "aconfig_host_test_java_library",
+        "junit",
+    ],
+    test_suites: ["general-tests"],
+}
+
+// integration tests: C++
+
+cc_aconfig_library {
+    name: "aconfig_test_cpp_library",
+    aconfig_declarations: "aconfig.test.flags",
+}
+
+cc_test {
+    name: "aconfig.test.cpp",
+    srcs: [
+        "tests/aconfig_test.cpp",
+    ],
+    static_libs: [
+        "aconfig_test_cpp_library",
+        "libgmock",
+    ],
+    shared_libs: [
+        "server_configurable_flags",
+    ],
+}
+
+rust_aconfig_library {
+    name: "libaconfig_test_rust_library",
+    crate_name: "aconfig_test_rust_library",
+    aconfig_declarations: "aconfig.test.flags",
+}
+
+rust_test {
+    name: "aconfig.prod_mode.test.rust",
+    srcs: [
+        "tests/aconfig_prod_mode_test.rs"
+    ],
+    rustlibs: [
+        "libaconfig_test_rust_library",
+    ],
+}
+
+rust_aconfig_library {
+    name: "libaconfig_test_rust_library_with_test_mode",
+    crate_name: "aconfig_test_rust_library",
+    aconfig_declarations: "aconfig.test.flags",
+    test: true,
+}
+
+rust_test {
+    name: "aconfig.test_mode.test.rust",
+    srcs: [
+        "tests/aconfig_test_mode_test.rs"
+    ],
+    rustlibs: [
+        "libaconfig_test_rust_library_with_test_mode",
+    ],
+}
\ No newline at end of file
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index 86124dd..74ac5ec 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -10,6 +10,12 @@
           "include-filter": "android.cts.flags.tests.FlagAnnotationTest"
         }
       ]
+    },
+    {
+      // Ensure changes on aconfig auto generated library is compatible with
+      // test testing filtering logic. Breakage on this test means all tests
+      // that using the flag macros to do filtering will get affected.
+      "name": "FlagMacrosTests"
     }
   ]
 }
diff --git a/tools/aconfig/fake_device_config/Android.bp b/tools/aconfig/fake_device_config/Android.bp
new file mode 100644
index 0000000..5f62ae9
--- /dev/null
+++ b/tools/aconfig/fake_device_config/Android.bp
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 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.
+
+java_library {
+	name: "fake_device_config",
+	srcs: ["src/**/*.java"],
+	sdk_version: "core_platform",
+    host_supported: true,
+}
+
diff --git a/tools/aconfig/fake_device_config/src/android/provider/DeviceConfig.java b/tools/aconfig/fake_device_config/src/android/provider/DeviceConfig.java
new file mode 100644
index 0000000..50b6289
--- /dev/null
+++ b/tools/aconfig/fake_device_config/src/android/provider/DeviceConfig.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+package android.provider;
+
+/*
+ * This class allows generated aconfig code to compile independently of the framework.
+ */
+public class DeviceConfig {
+	private DeviceConfig() {
+	}
+
+	public static boolean getBoolean(String ns, String name, boolean def) {
+		return false;
+	}
+}
diff --git a/tools/aconfig/protos/aconfig.proto b/tools/aconfig/protos/aconfig.proto
index 4cad69a..d5e2868 100644
--- a/tools/aconfig/protos/aconfig.proto
+++ b/tools/aconfig/protos/aconfig.proto
@@ -39,6 +39,7 @@
   optional string namespace = 2;
   optional string description = 3;
   repeated string bug = 4;
+  optional bool is_fixed_read_only = 5;
 };
 
 message flag_declarations {
@@ -75,6 +76,7 @@
   optional flag_state state = 6;
   optional flag_permission permission = 7;
   repeated tracepoint trace = 8;
+  optional bool is_fixed_read_only = 9;
 }
 
 message parsed_flags {
diff --git a/tools/aconfig/src/codegen_cpp.rs b/tools/aconfig/src/codegen_cpp.rs
index a802725..8c2d7ba 100644
--- a/tools/aconfig/src/codegen_cpp.rs
+++ b/tools/aconfig/src/codegen_cpp.rs
@@ -42,7 +42,7 @@
         cpp_namespace,
         package: package.to_string(),
         readwrite,
-        for_prod: codegen_mode == CodegenMode::Production,
+        for_test: codegen_mode == CodegenMode::Test,
         class_elements,
     };
 
@@ -57,16 +57,6 @@
             template: include_str!("../templates/cpp_source_file.template"),
             dir: "",
         },
-        FileSpec {
-            name: &format!("{}_flag_provider.h", header),
-            template: match codegen_mode {
-                CodegenMode::Production => {
-                    include_str!("../templates/cpp_prod_flag_provider.template")
-                }
-                CodegenMode::Test => include_str!("../templates/cpp_test_flag_provider.template"),
-            },
-            dir: "",
-        },
     ];
     files.iter().map(|file| generate_file(file, &context)).collect()
 }
@@ -92,7 +82,7 @@
     pub cpp_namespace: String,
     pub package: String,
     pub readwrite: bool,
-    pub for_prod: bool,
+    pub for_test: bool,
     pub class_elements: Vec<ClassElement>,
 }
 
@@ -101,7 +91,6 @@
     pub readwrite: bool,
     pub default_value: String,
     pub flag_name: String,
-    pub uppercase_flag_name: String,
     pub device_config_namespace: String,
     pub device_config_flag: String,
 }
@@ -115,7 +104,6 @@
             "false".to_string()
         },
         flag_name: pf.name().to_string(),
-        uppercase_flag_name: pf.name().to_string().to_ascii_uppercase(),
         device_config_namespace: pf.namespace().to_string(),
         device_config_flag: codegen::create_device_config_ident(package, pf.name())
             .expect("values checked at flag parse time"),
@@ -128,40 +116,30 @@
     use std::collections::HashMap;
 
     const EXPORTED_PROD_HEADER_EXPECTED: &str = r#"
-#ifndef com_android_aconfig_test_HEADER_H
-#define com_android_aconfig_test_HEADER_H
+#pragma once
 
-#include <string>
+#ifdef __cplusplus
+
 #include <memory>
-#include <server_configurable_flags/get_flags.h>
-using namespace server_configurable_flags;
 
 namespace com::android::aconfig::test {
 class flag_provider_interface {
 public:
-
     virtual ~flag_provider_interface() = default;
 
     virtual bool disabled_ro() = 0;
 
     virtual bool disabled_rw() = 0;
 
+    virtual bool enabled_fixed_ro() = 0;
+
     virtual bool enabled_ro() = 0;
 
     virtual bool enabled_rw() = 0;
-
-    virtual void override_flag(std::string const&, bool) {}
-
-    virtual void reset_overrides() {}
 };
 
 extern std::unique_ptr<flag_provider_interface> provider_;
 
-extern std::string const DISABLED_RO;
-extern std::string const DISABLED_RW;
-extern std::string const ENABLED_RO;
-extern std::string const ENABLED_RW;
-
 inline bool disabled_ro() {
     return false;
 }
@@ -170,6 +148,10 @@
     return provider_->disabled_rw();
 }
 
+inline bool enabled_fixed_ro() {
+    return true;
+}
+
 inline bool enabled_ro() {
     return true;
 }
@@ -178,28 +160,35 @@
     return provider_->enabled_rw();
 }
 
-inline void override_flag(std::string const& name, bool val) {
-    return provider_->override_flag(name, val);
 }
 
-inline void reset_overrides() {
-    return provider_->reset_overrides();
-}
+extern "C" {
+#endif // __cplusplus
 
-}
+bool com_android_aconfig_test_disabled_ro();
+
+bool com_android_aconfig_test_disabled_rw();
+
+bool com_android_aconfig_test_enabled_fixed_ro();
+
+bool com_android_aconfig_test_enabled_ro();
+
+bool com_android_aconfig_test_enabled_rw();
+
+#ifdef __cplusplus
+} // extern "C"
 #endif
 "#;
 
     const EXPORTED_TEST_HEADER_EXPECTED: &str = r#"
-#ifndef com_android_aconfig_test_HEADER_H
-#define com_android_aconfig_test_HEADER_H
+#pragma once
 
-#include <string>
+#ifdef __cplusplus
+
 #include <memory>
-#include <server_configurable_flags/get_flags.h>
-using namespace server_configurable_flags;
 
 namespace com::android::aconfig::test {
+
 class flag_provider_interface {
 public:
 
@@ -207,184 +196,316 @@
 
     virtual bool disabled_ro() = 0;
 
+    virtual void disabled_ro(bool val) = 0;
+
     virtual bool disabled_rw() = 0;
 
+    virtual void disabled_rw(bool val) = 0;
+
+    virtual bool enabled_fixed_ro() = 0;
+
+    virtual void enabled_fixed_ro(bool val) = 0;
+
     virtual bool enabled_ro() = 0;
 
+    virtual void enabled_ro(bool val) = 0;
+
     virtual bool enabled_rw() = 0;
 
-    virtual void override_flag(std::string const&, bool) {}
+    virtual void enabled_rw(bool val) = 0;
 
-    virtual void reset_overrides() {}
+    virtual void reset_flags() {}
 };
 
 extern std::unique_ptr<flag_provider_interface> provider_;
 
-extern std::string const DISABLED_RO;
-extern std::string const DISABLED_RW;
-extern std::string const ENABLED_RO;
-extern std::string const ENABLED_RW;
-
 inline bool disabled_ro() {
     return provider_->disabled_ro();
 }
 
+inline void disabled_ro(bool val) {
+    provider_->disabled_ro(val);
+}
+
 inline bool disabled_rw() {
     return provider_->disabled_rw();
 }
 
+inline void disabled_rw(bool val) {
+    provider_->disabled_rw(val);
+}
+
+inline bool enabled_fixed_ro() {
+    return provider_->enabled_fixed_ro();
+}
+
+inline void enabled_fixed_ro(bool val) {
+    provider_->enabled_fixed_ro(val);
+}
+
 inline bool enabled_ro() {
     return provider_->enabled_ro();
 }
 
+inline void enabled_ro(bool val) {
+    provider_->enabled_ro(val);
+}
+
 inline bool enabled_rw() {
     return provider_->enabled_rw();
 }
 
-inline void override_flag(std::string const& name, bool val) {
-    return provider_->override_flag(name, val);
+inline void enabled_rw(bool val) {
+    provider_->enabled_rw(val);
 }
 
-inline void reset_overrides() {
-    return provider_->reset_overrides();
+inline void reset_flags() {
+    return provider_->reset_flags();
 }
 
 }
+
+extern "C" {
+#endif // __cplusplus
+
+bool com_android_aconfig_test_disabled_ro();
+
+void set_com_android_aconfig_test_disabled_ro(bool val);
+
+bool com_android_aconfig_test_disabled_rw();
+
+void set_com_android_aconfig_test_disabled_rw(bool val);
+
+bool com_android_aconfig_test_enabled_fixed_ro();
+
+void set_com_android_aconfig_test_enabled_fixed_ro(bool val);
+
+bool com_android_aconfig_test_enabled_ro();
+
+void set_com_android_aconfig_test_enabled_ro(bool val);
+
+bool com_android_aconfig_test_enabled_rw();
+
+void set_com_android_aconfig_test_enabled_rw(bool val);
+
+void com_android_aconfig_test_reset_flags();
+
+
+#ifdef __cplusplus
+} // extern "C"
 #endif
+
+
 "#;
 
-    const PROD_FLAG_PROVIDER_HEADER_EXPECTED: &str = r#"
-#ifndef com_android_aconfig_test_flag_provider_HEADER_H
-#define com_android_aconfig_test_flag_provider_HEADER_H
-
+    const PROD_SOURCE_FILE_EXPECTED: &str = r#"
 #include "com_android_aconfig_test.h"
-
-namespace com::android::aconfig::test {
-class flag_provider : public flag_provider_interface {
-public:
-
-    virtual bool disabled_ro() override {
-        return false;
-    }
-
-    virtual bool disabled_rw() override {
-        return GetServerConfigurableFlag(
-            "aconfig_test",
-            "com.android.aconfig.test.disabled_rw",
-            "false") == "true";
-    }
-
-    virtual bool enabled_ro() override {
-        return true;
-    }
-
-    virtual bool enabled_rw() override {
-        return GetServerConfigurableFlag(
-            "aconfig_test",
-            "com.android.aconfig.test.enabled_rw",
-            "true") == "true";
-    }
-};
-}
-#endif
-"#;
-
-    const TEST_FLAG_PROVIDER_HEADER_EXPECTED: &str = r#"
-#ifndef com_android_aconfig_test_flag_provider_HEADER_H
-#define com_android_aconfig_test_flag_provider_HEADER_H
-
-#include "com_android_aconfig_test.h"
-
-#include <unordered_map>
-#include <unordered_set>
-#include <cassert>
-
-namespace com::android::aconfig::test {
-class flag_provider : public flag_provider_interface {
-private:
-    std::unordered_map<std::string, bool> overrides_;
-    std::unordered_set<std::string> flag_names_;
-
-public:
-
-    flag_provider()
-        : overrides_(),
-          flag_names_() {
-        flag_names_.insert(DISABLED_RO);
-        flag_names_.insert(DISABLED_RW);
-        flag_names_.insert(ENABLED_RO);
-        flag_names_.insert(ENABLED_RW);
-    }
-
-    virtual bool disabled_ro() override {
-        auto it = overrides_.find(DISABLED_RO);
-        if (it != overrides_.end()) {
-            return it->second;
-        } else {
-            return false;
-        }
-    }
-
-    virtual bool disabled_rw() override {
-        auto it = overrides_.find(DISABLED_RW);
-        if (it != overrides_.end()) {
-            return it->second;
-        } else {
-            return GetServerConfigurableFlag(
-                "aconfig_test",
-                "com.android.aconfig.test.disabled_rw",
-                "false") == "true";
-        }
-    }
-
-    virtual bool enabled_ro() override {
-        auto it = overrides_.find(ENABLED_RO);
-        if (it != overrides_.end()) {
-            return it->second;
-        } else {
-            return true;
-        }
-    }
-
-    virtual bool enabled_rw() override {
-        auto it = overrides_.find(ENABLED_RW);
-        if (it != overrides_.end()) {
-            return it->second;
-        } else {
-            return GetServerConfigurableFlag(
-                "aconfig_test",
-                "com.android.aconfig.test.enabled_rw",
-                "true") == "true";
-        }
-    }
-
-    virtual void override_flag(std::string const& flag, bool val) override {
-        assert(flag_names_.count(flag));
-        overrides_[flag] = val;
-    }
-
-    virtual void reset_overrides() override {
-        overrides_.clear();
-    }
-};
-}
-#endif
-"#;
-
-    const SOURCE_FILE_EXPECTED: &str = r#"
-#include "com_android_aconfig_test.h"
-#include "com_android_aconfig_test_flag_provider.h"
+#include <server_configurable_flags/get_flags.h>
 
 namespace com::android::aconfig::test {
 
-    std::string const DISABLED_RO = "com.android.aconfig.test.disabled_ro";
-    std::string const DISABLED_RW = "com.android.aconfig.test.disabled_rw";
-    std::string const ENABLED_RO = "com.android.aconfig.test.enabled_ro";
-    std::string const ENABLED_RW = "com.android.aconfig.test.enabled_rw";
+    class flag_provider : public flag_provider_interface {
+        public:
+
+            virtual bool disabled_ro() override {
+                return false;
+            }
+
+            virtual bool disabled_rw() override {
+                return server_configurable_flags::GetServerConfigurableFlag(
+                    "aconfig_flags.aconfig_test",
+                    "com.android.aconfig.test.disabled_rw",
+                    "false") == "true";
+            }
+
+            virtual bool enabled_fixed_ro() override {
+                return true;
+            }
+
+            virtual bool enabled_ro() override {
+                return true;
+            }
+
+            virtual bool enabled_rw() override {
+                return server_configurable_flags::GetServerConfigurableFlag(
+                    "aconfig_flags.aconfig_test",
+                    "com.android.aconfig.test.enabled_rw",
+                    "true") == "true";
+            }
+
+    };
 
     std::unique_ptr<flag_provider_interface> provider_ =
         std::make_unique<flag_provider>();
 }
+
+bool com_android_aconfig_test_disabled_ro() {
+    return false;
+}
+
+bool com_android_aconfig_test_disabled_rw() {
+    return com::android::aconfig::test::disabled_rw();
+}
+
+bool com_android_aconfig_test_enabled_fixed_ro() {
+    return true;
+}
+
+bool com_android_aconfig_test_enabled_ro() {
+    return true;
+}
+
+bool com_android_aconfig_test_enabled_rw() {
+    return com::android::aconfig::test::enabled_rw();
+}
+
+"#;
+
+    const TEST_SOURCE_FILE_EXPECTED: &str = r#"
+#include "com_android_aconfig_test.h"
+#include <server_configurable_flags/get_flags.h>
+
+namespace com::android::aconfig::test {
+
+    class flag_provider : public flag_provider_interface {
+        private:
+            std::unordered_map<std::string, bool> overrides_;
+
+        public:
+            flag_provider()
+                : overrides_()
+            {}
+
+            virtual bool disabled_ro() override {
+                auto it = overrides_.find("disabled_ro");
+                  if (it != overrides_.end()) {
+                      return it->second;
+                } else {
+                  return false;
+                }
+            }
+
+            virtual void disabled_ro(bool val) override {
+                overrides_["disabled_ro"] = val;
+            }
+
+            virtual bool disabled_rw() override {
+                auto it = overrides_.find("disabled_rw");
+                  if (it != overrides_.end()) {
+                      return it->second;
+                } else {
+                  return server_configurable_flags::GetServerConfigurableFlag(
+                      "aconfig_flags.aconfig_test",
+                      "com.android.aconfig.test.disabled_rw",
+                      "false") == "true";
+                }
+            }
+
+            virtual void disabled_rw(bool val) override {
+                overrides_["disabled_rw"] = val;
+            }
+
+            virtual bool enabled_fixed_ro() override {
+                auto it = overrides_.find("enabled_fixed_ro");
+                  if (it != overrides_.end()) {
+                      return it->second;
+                } else {
+                  return true;
+                }
+            }
+
+            virtual void enabled_fixed_ro(bool val) override {
+                overrides_["enabled_fixed_ro"] = val;
+            }
+
+            virtual bool enabled_ro() override {
+                auto it = overrides_.find("enabled_ro");
+                  if (it != overrides_.end()) {
+                      return it->second;
+                } else {
+                  return true;
+                }
+            }
+
+            virtual void enabled_ro(bool val) override {
+                overrides_["enabled_ro"] = val;
+            }
+
+            virtual bool enabled_rw() override {
+                auto it = overrides_.find("enabled_rw");
+                  if (it != overrides_.end()) {
+                      return it->second;
+                } else {
+                  return server_configurable_flags::GetServerConfigurableFlag(
+                      "aconfig_flags.aconfig_test",
+                      "com.android.aconfig.test.enabled_rw",
+                      "true") == "true";
+                }
+            }
+
+            virtual void enabled_rw(bool val) override {
+                overrides_["enabled_rw"] = val;
+            }
+
+            virtual void reset_flags() override {
+                overrides_.clear();
+            }
+    };
+
+    std::unique_ptr<flag_provider_interface> provider_ =
+        std::make_unique<flag_provider>();
+}
+
+bool com_android_aconfig_test_disabled_ro() {
+    return com::android::aconfig::test::disabled_ro();
+}
+
+
+void set_com_android_aconfig_test_disabled_ro(bool val) {
+    com::android::aconfig::test::disabled_ro(val);
+}
+
+bool com_android_aconfig_test_disabled_rw() {
+    return com::android::aconfig::test::disabled_rw();
+}
+
+
+void set_com_android_aconfig_test_disabled_rw(bool val) {
+    com::android::aconfig::test::disabled_rw(val);
+}
+
+
+bool com_android_aconfig_test_enabled_fixed_ro() {
+    return com::android::aconfig::test::enabled_fixed_ro();
+}
+
+void set_com_android_aconfig_test_enabled_fixed_ro(bool val) {
+    com::android::aconfig::test::enabled_fixed_ro(val);
+}
+
+
+bool com_android_aconfig_test_enabled_ro() {
+    return com::android::aconfig::test::enabled_ro();
+}
+
+
+void set_com_android_aconfig_test_enabled_ro(bool val) {
+    com::android::aconfig::test::enabled_ro(val);
+}
+
+bool com_android_aconfig_test_enabled_rw() {
+    return com::android::aconfig::test::enabled_rw();
+}
+
+
+void set_com_android_aconfig_test_enabled_rw(bool val) {
+    com::android::aconfig::test::enabled_rw(val);
+}
+
+void com_android_aconfig_test_reset_flags() {
+     com::android::aconfig::test::reset_flags();
+}
+
 "#;
 
     fn test_generate_cpp_code(mode: CodegenMode) {
@@ -413,25 +534,15 @@
             )
         );
 
-        target_file_path = String::from("com_android_aconfig_test_flag_provider.h");
-        assert!(generated_files_map.contains_key(&target_file_path));
-        assert_eq!(
-            None,
-            crate::test::first_significant_code_diff(
-                match mode {
-                    CodegenMode::Production => PROD_FLAG_PROVIDER_HEADER_EXPECTED,
-                    CodegenMode::Test => TEST_FLAG_PROVIDER_HEADER_EXPECTED,
-                },
-                generated_files_map.get(&target_file_path).unwrap()
-            )
-        );
-
         target_file_path = String::from("com_android_aconfig_test.cc");
         assert!(generated_files_map.contains_key(&target_file_path));
         assert_eq!(
             None,
             crate::test::first_significant_code_diff(
-                SOURCE_FILE_EXPECTED,
+                match mode {
+                    CodegenMode::Production => PROD_SOURCE_FILE_EXPECTED,
+                    CodegenMode::Test => TEST_SOURCE_FILE_EXPECTED,
+                },
                 generated_files_map.get(&target_file_path).unwrap()
             )
         );
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index 8ab6ffa..7cdf486 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -47,9 +47,13 @@
         "FeatureFlags.java",
         include_str!("../templates/FeatureFlags.java.template"),
     )?;
+    template.add_template(
+        "FakeFeatureFlagsImpl.java",
+        include_str!("../templates/FakeFeatureFlagsImpl.java.template"),
+    )?;
 
     let path: PathBuf = package.split('.').collect();
-    ["Flags.java", "FeatureFlagsImpl.java", "FeatureFlags.java"]
+    ["Flags.java", "FeatureFlags.java", "FeatureFlagsImpl.java", "FakeFeatureFlagsImpl.java"]
         .iter()
         .map(|file| {
             Ok(OutputFile {
@@ -112,20 +116,23 @@
     use super::*;
     use std::collections::HashMap;
 
-    const EXPECTED_FEATUREFLAGS_CONTENT: &str = r#"
+    const EXPECTED_FEATUREFLAGS_COMMON_CONTENT: &str = r#"
     package com.android.aconfig.test;
     public interface FeatureFlags {
         boolean disabledRo();
         boolean disabledRw();
+        boolean enabledFixedRo();
         boolean enabledRo();
         boolean enabledRw();
-    }"#;
+    }
+    "#;
 
     const EXPECTED_FLAG_COMMON_CONTENT: &str = r#"
     package com.android.aconfig.test;
     public final class Flags {
         public static final String FLAG_DISABLED_RO = "com.android.aconfig.test.disabled_ro";
         public static final String FLAG_DISABLED_RW = "com.android.aconfig.test.disabled_rw";
+        public static final String FLAG_ENABLED_FIXED_RO = "com.android.aconfig.test.enabled_fixed_ro";
         public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro";
         public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
 
@@ -135,6 +142,9 @@
         public static boolean disabledRw() {
             return FEATURE_FLAGS.disabledRw();
         }
+        public static boolean enabledFixedRo() {
+            return FEATURE_FLAGS.enabledFixedRo();
+        }
         public static boolean enabledRo() {
             return FEATURE_FLAGS.enabledRo();
         }
@@ -143,6 +153,64 @@
         }
     "#;
 
+    const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#"
+    package com.android.aconfig.test;
+    import java.util.HashMap;
+    import java.util.Map;
+    public class FakeFeatureFlagsImpl implements FeatureFlags {
+        public FakeFeatureFlagsImpl() {
+            resetAll();
+        }
+        @Override
+        public boolean disabledRo() {
+            return getFlag(Flags.FLAG_DISABLED_RO);
+        }
+        @Override
+        public boolean disabledRw() {
+            return getFlag(Flags.FLAG_DISABLED_RW);
+        }
+        @Override
+        public boolean enabledFixedRo() {
+            return getFlag(Flags.FLAG_ENABLED_FIXED_RO);
+        }
+        @Override
+        public boolean enabledRo() {
+            return getFlag(Flags.FLAG_ENABLED_RO);
+        }
+        @Override
+        public boolean enabledRw() {
+            return getFlag(Flags.FLAG_ENABLED_RW);
+        }
+        public void setFlag(String flagName, boolean value) {
+            if (!this.mFlagMap.containsKey(flagName)) {
+                throw new IllegalArgumentException("no such flag " + flagName);
+            }
+            this.mFlagMap.put(flagName, value);
+        }
+        public void resetAll() {
+            for (Map.Entry entry : mFlagMap.entrySet()) {
+                entry.setValue(null);
+            }
+        }
+        private boolean getFlag(String flagName) {
+            Boolean value = this.mFlagMap.get(flagName);
+            if (value == null) {
+                throw new IllegalArgumentException(flagName + " is not set");
+            }
+            return value;
+        }
+        private Map<String, Boolean> mFlagMap = new HashMap<>(
+            Map.of(
+                Flags.FLAG_DISABLED_RO, false,
+                Flags.FLAG_DISABLED_RW, false,
+                Flags.FLAG_ENABLED_FIXED_RO, false,
+                Flags.FLAG_ENABLED_RO, false,
+                Flags.FLAG_ENABLED_RW, false
+            )
+        );
+    }
+    "#;
+
     #[test]
     fn test_generate_java_code_production() {
         let parsed_flags = crate::test::parse_test_flags();
@@ -156,7 +224,8 @@
             + r#"
             private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
         }"#;
-        let expected_featureflagsimpl_content = r#"
+
+        let expect_featureflagsimpl_content = r#"
         package com.android.aconfig.test;
         import android.provider.DeviceConfig;
         public final class FeatureFlagsImpl implements FeatureFlags {
@@ -173,6 +242,10 @@
                 );
             }
             @Override
+            public boolean enabledFixedRo() {
+                return true;
+            }
+            @Override
             public boolean enabledRo() {
                 return true;
             }
@@ -188,8 +261,12 @@
         "#;
         let mut file_set = HashMap::from([
             ("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()),
-            ("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content),
-            ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_CONTENT),
+            ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content),
+            ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT),
+            (
+                "com/android/aconfig/test/FakeFeatureFlagsImpl.java",
+                EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
+            ),
         ]);
 
         for file in generated_files {
@@ -219,75 +296,57 @@
             CodegenMode::Test,
         )
         .unwrap();
+
         let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
             + r#"
-            public static void setFeatureFlagsImpl(FeatureFlags featureFlags) {
+            public static void setFeatureFlags(FeatureFlags featureFlags) {
                 Flags.FEATURE_FLAGS = featureFlags;
             }
-            public static void unsetFeatureFlagsImpl() {
+            public static void unsetFeatureFlags() {
                 Flags.FEATURE_FLAGS = null;
             }
             private static FeatureFlags FEATURE_FLAGS;
         }
         "#;
-        let expected_featureflagsimpl_content = r#"
+        let expect_featureflagsimpl_content = r#"
         package com.android.aconfig.test;
-        import static java.util.stream.Collectors.toMap;
-        import java.util.HashMap;
-        import java.util.Map;
-        import java.util.stream.Stream;
         public final class FeatureFlagsImpl implements FeatureFlags {
             @Override
             public boolean disabledRo() {
-                return getFlag(Flags.FLAG_DISABLED_RO);
+                throw new UnsupportedOperationException(
+                    "Method is not implemented.");
             }
             @Override
             public boolean disabledRw() {
-                return getFlag(Flags.FLAG_DISABLED_RW);
+                throw new UnsupportedOperationException(
+                    "Method is not implemented.");
+            }
+            @Override
+            public boolean enabledFixedRo() {
+                throw new UnsupportedOperationException(
+                    "Method is not implemented.");
             }
             @Override
             public boolean enabledRo() {
-                return getFlag(Flags.FLAG_ENABLED_RO);
+                throw new UnsupportedOperationException(
+                    "Method is not implemented.");
             }
             @Override
             public boolean enabledRw() {
-                return getFlag(Flags.FLAG_ENABLED_RW);
+                throw new UnsupportedOperationException(
+                    "Method is not implemented.");
             }
-            public void setFlag(String flagName, boolean value) {
-                if (!this.mFlagMap.containsKey(flagName)) {
-                    throw new IllegalArgumentException("no such flag" + flagName);
-                }
-                this.mFlagMap.put(flagName, value);
-            }
-            public void resetAll() {
-                for (Map.Entry entry : mFlagMap.entrySet()) {
-                    entry.setValue(null);
-                }
-            }
-            private boolean getFlag(String flagName) {
-                Boolean value = this.mFlagMap.get(flagName);
-                if (value == null) {
-                    throw new IllegalArgumentException(flagName + " is not set");
-                }
-                return value;
-            }
-            private HashMap<String, Boolean> mFlagMap = Stream.of(
-                    Flags.FLAG_DISABLED_RO,
-                    Flags.FLAG_DISABLED_RW,
-                    Flags.FLAG_ENABLED_RO,
-                    Flags.FLAG_ENABLED_RW
-                )
-                .collect(
-                    HashMap::new,
-                    (map, elem) -> map.put(elem, null),
-                    HashMap::putAll
-                );
         }
         "#;
+
         let mut file_set = HashMap::from([
             ("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()),
-            ("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content),
-            ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_CONTENT),
+            ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT),
+            ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content),
+            (
+                "com/android/aconfig/test/FakeFeatureFlagsImpl.java",
+                EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
+            ),
         ]);
 
         for file in generated_files {
diff --git a/tools/aconfig/src/codegen_rust.rs b/tools/aconfig/src/codegen_rust.rs
index f931418..4e4c7dd 100644
--- a/tools/aconfig/src/codegen_rust.rs
+++ b/tools/aconfig/src/codegen_rust.rs
@@ -19,10 +19,14 @@
 use tinytemplate::TinyTemplate;
 
 use crate::codegen;
-use crate::commands::OutputFile;
+use crate::commands::{CodegenMode, OutputFile};
 use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
 
-pub fn generate_rust_code<'a, I>(package: &str, parsed_flags_iter: I) -> Result<OutputFile>
+pub fn generate_rust_code<'a, I>(
+    package: &str,
+    parsed_flags_iter: I,
+    codegen_mode: CodegenMode,
+) -> Result<OutputFile>
 where
     I: Iterator<Item = &'a ProtoParsedFlag>,
 {
@@ -34,7 +38,13 @@
         modules: package.split('.').map(|s| s.to_string()).collect::<Vec<_>>(),
     };
     let mut template = TinyTemplate::new();
-    template.add_template("rust_code_gen", include_str!("../templates/rust.template"))?;
+    template.add_template(
+        "rust_code_gen",
+        match codegen_mode {
+            CodegenMode::Production => include_str!("../templates/rust_prod.template"),
+            CodegenMode::Test => include_str!("../templates/rust_test.template"),
+        },
+    )?;
     let contents = template.render("rust_code_gen", &context)?;
     let path = ["src", "lib.rs"].iter().collect();
     Ok(OutputFile { contents: contents.into(), path })
@@ -49,41 +59,27 @@
 
 #[derive(Serialize)]
 struct TemplateParsedFlag {
+    pub readwrite: bool,
+    pub default_value: String,
     pub name: String,
     pub device_config_namespace: String,
     pub device_config_flag: String,
-
-    // TinyTemplate's conditionals are limited to single <bool> expressions; list all options here
-    // Invariant: exactly one of these fields will be true
-    pub is_read_only_enabled: bool,
-    pub is_read_only_disabled: bool,
-    pub is_read_write: bool,
 }
 
 impl TemplateParsedFlag {
     #[allow(clippy::nonminimal_bool)]
     fn new(package: &str, pf: &ProtoParsedFlag) -> Self {
         let template = TemplateParsedFlag {
+            readwrite: pf.permission() == ProtoFlagPermission::READ_WRITE,
+            default_value: match pf.state() {
+                ProtoFlagState::ENABLED => "true".to_string(),
+                ProtoFlagState::DISABLED => "false".to_string(),
+            },
             name: pf.name().to_string(),
             device_config_namespace: pf.namespace().to_string(),
             device_config_flag: codegen::create_device_config_ident(package, pf.name())
                 .expect("values checked at flag parse time"),
-            is_read_only_enabled: pf.permission() == ProtoFlagPermission::READ_ONLY
-                && pf.state() == ProtoFlagState::ENABLED,
-            is_read_only_disabled: pf.permission() == ProtoFlagPermission::READ_ONLY
-                && pf.state() == ProtoFlagState::DISABLED,
-            is_read_write: pf.permission() == ProtoFlagPermission::READ_WRITE,
         };
-        #[rustfmt::skip]
-        debug_assert!(
-            (template.is_read_only_enabled && !template.is_read_only_disabled && !template.is_read_write) ||
-            (!template.is_read_only_enabled && template.is_read_only_disabled && !template.is_read_write) ||
-            (!template.is_read_only_enabled && !template.is_read_only_disabled && template.is_read_write),
-            "TemplateParsedFlag invariant failed: {} {} {}",
-            template.is_read_only_enabled,
-            template.is_read_only_disabled,
-            template.is_read_write,
-        );
         template
     }
 }
@@ -92,48 +88,259 @@
 mod tests {
     use super::*;
 
-    #[test]
-    fn test_generate_rust_code() {
-        let parsed_flags = crate::test::parse_test_flags();
-        let generated =
-            generate_rust_code(crate::test::TEST_PACKAGE, parsed_flags.parsed_flag.iter()).unwrap();
-        assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
-        let expected = r#"
-pub mod com {
-pub mod android {
-pub mod aconfig {
-pub mod test {
+    const PROD_EXPECTED: &str = r#"
+//! codegenerated rust flag lib
+
+/// flag provider
+pub struct FlagProvider;
+
+impl FlagProvider {
+    /// query flag disabled_ro
+    pub fn disabled_ro(&self) -> bool {
+        false
+    }
+
+    /// query flag disabled_rw
+    pub fn disabled_rw(&self) -> bool {
+        flags_rust::GetServerConfigurableFlag(
+            "aconfig_flags.aconfig_test",
+            "com.android.aconfig.test.disabled_rw",
+            "false") == "true"
+    }
+
+    /// query flag enabled_fixed_ro
+    pub fn enabled_fixed_ro(&self) -> bool {
+        true
+    }
+
+    /// query flag enabled_ro
+    pub fn enabled_ro(&self) -> bool {
+        true
+    }
+
+    /// query flag enabled_rw
+    pub fn enabled_rw(&self) -> bool {
+        flags_rust::GetServerConfigurableFlag(
+            "aconfig_flags.aconfig_test",
+            "com.android.aconfig.test.enabled_rw",
+            "true") == "true"
+    }
+}
+
+/// flag provider
+pub static PROVIDER: FlagProvider = FlagProvider;
+
+/// query flag disabled_ro
 #[inline(always)]
-pub const fn r#disabled_ro() -> bool {
+pub fn disabled_ro() -> bool {
     false
 }
 
+/// query flag disabled_rw
 #[inline(always)]
-pub fn r#disabled_rw() -> bool {
-    flags_rust::GetServerConfigurableFlag("aconfig_test", "com.android.aconfig.test.disabled_rw", "false") == "true"
+pub fn disabled_rw() -> bool {
+    PROVIDER.disabled_rw()
 }
 
+/// query flag enabled_fixed_ro
 #[inline(always)]
-pub const fn r#enabled_ro() -> bool {
+pub fn enabled_fixed_ro() -> bool {
     true
 }
 
+/// query flag enabled_ro
 #[inline(always)]
-pub fn r#enabled_rw() -> bool {
-    flags_rust::GetServerConfigurableFlag("aconfig_test", "com.android.aconfig.test.enabled_rw", "false") == "true"
+pub fn enabled_ro() -> bool {
+    true
 }
 
-}
-}
-}
+/// query flag enabled_rw
+#[inline(always)]
+pub fn enabled_rw() -> bool {
+    PROVIDER.enabled_rw()
 }
 "#;
+
+    const TEST_EXPECTED: &str = r#"
+//! codegenerated rust flag lib
+
+use std::collections::BTreeMap;
+use std::sync::Mutex;
+
+/// flag provider
+pub struct FlagProvider {
+    overrides: BTreeMap<&'static str, bool>,
+}
+
+impl FlagProvider {
+    /// query flag disabled_ro
+    pub fn disabled_ro(&self) -> bool {
+        self.overrides.get("disabled_ro").copied().unwrap_or(
+            false
+        )
+    }
+
+    /// set flag disabled_ro
+    pub fn set_disabled_ro(&mut self, val: bool) {
+        self.overrides.insert("disabled_ro", val);
+    }
+
+    /// query flag disabled_rw
+    pub fn disabled_rw(&self) -> bool {
+        self.overrides.get("disabled_rw").copied().unwrap_or(
+            flags_rust::GetServerConfigurableFlag(
+                "aconfig_flags.aconfig_test",
+                "com.android.aconfig.test.disabled_rw",
+                "false") == "true"
+        )
+    }
+
+    /// set flag disabled_rw
+    pub fn set_disabled_rw(&mut self, val: bool) {
+        self.overrides.insert("disabled_rw", val);
+    }
+
+    /// query flag enabled_fixed_ro
+    pub fn enabled_fixed_ro(&self) -> bool {
+        self.overrides.get("enabled_fixed_ro").copied().unwrap_or(
+            true
+        )
+    }
+
+    /// set flag enabled_fixed_ro
+    pub fn set_enabled_fixed_ro(&mut self, val: bool) {
+        self.overrides.insert("enabled_fixed_ro", val);
+    }
+
+    /// query flag enabled_ro
+    pub fn enabled_ro(&self) -> bool {
+        self.overrides.get("enabled_ro").copied().unwrap_or(
+            true
+        )
+    }
+
+    /// set flag enabled_ro
+    pub fn set_enabled_ro(&mut self, val: bool) {
+        self.overrides.insert("enabled_ro", val);
+    }
+
+    /// query flag enabled_rw
+    pub fn enabled_rw(&self) -> bool {
+        self.overrides.get("enabled_rw").copied().unwrap_or(
+            flags_rust::GetServerConfigurableFlag(
+                "aconfig_flags.aconfig_test",
+                "com.android.aconfig.test.enabled_rw",
+                "true") == "true"
+        )
+    }
+
+    /// set flag enabled_rw
+    pub fn set_enabled_rw(&mut self, val: bool) {
+        self.overrides.insert("enabled_rw", val);
+    }
+
+    /// clear all flag overrides
+    pub fn reset_flags(&mut self) {
+        self.overrides.clear();
+    }
+}
+
+/// flag provider
+pub static PROVIDER: Mutex<FlagProvider> = Mutex::new(
+    FlagProvider {overrides: BTreeMap::new()}
+);
+
+/// query flag disabled_ro
+#[inline(always)]
+pub fn disabled_ro() -> bool {
+    PROVIDER.lock().unwrap().disabled_ro()
+}
+
+/// set flag disabled_ro
+#[inline(always)]
+pub fn set_disabled_ro(val: bool) {
+    PROVIDER.lock().unwrap().set_disabled_ro(val);
+}
+
+/// query flag disabled_rw
+#[inline(always)]
+pub fn disabled_rw() -> bool {
+    PROVIDER.lock().unwrap().disabled_rw()
+}
+
+/// set flag disabled_rw
+#[inline(always)]
+pub fn set_disabled_rw(val: bool) {
+    PROVIDER.lock().unwrap().set_disabled_rw(val);
+}
+
+/// query flag enabled_fixed_ro
+#[inline(always)]
+pub fn enabled_fixed_ro() -> bool {
+    PROVIDER.lock().unwrap().enabled_fixed_ro()
+}
+
+/// set flag enabled_fixed_ro
+#[inline(always)]
+pub fn set_enabled_fixed_ro(val: bool) {
+    PROVIDER.lock().unwrap().set_enabled_fixed_ro(val);
+}
+
+/// query flag enabled_ro
+#[inline(always)]
+pub fn enabled_ro() -> bool {
+    PROVIDER.lock().unwrap().enabled_ro()
+}
+
+/// set flag enabled_ro
+#[inline(always)]
+pub fn set_enabled_ro(val: bool) {
+    PROVIDER.lock().unwrap().set_enabled_ro(val);
+}
+
+/// query flag enabled_rw
+#[inline(always)]
+pub fn enabled_rw() -> bool {
+    PROVIDER.lock().unwrap().enabled_rw()
+}
+
+/// set flag enabled_rw
+#[inline(always)]
+pub fn set_enabled_rw(val: bool) {
+    PROVIDER.lock().unwrap().set_enabled_rw(val);
+}
+
+/// clear all flag override
+pub fn reset_flags() {
+    PROVIDER.lock().unwrap().reset_flags()
+}
+"#;
+
+    fn test_generate_rust_code(mode: CodegenMode) {
+        let parsed_flags = crate::test::parse_test_flags();
+        let generated =
+            generate_rust_code(crate::test::TEST_PACKAGE, parsed_flags.parsed_flag.iter(), mode)
+                .unwrap();
+        assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
         assert_eq!(
             None,
             crate::test::first_significant_code_diff(
-                expected,
+                match mode {
+                    CodegenMode::Production => PROD_EXPECTED,
+                    CodegenMode::Test => TEST_EXPECTED,
+                },
                 &String::from_utf8(generated.contents).unwrap()
             )
         );
     }
+
+    #[test]
+    fn test_generate_rust_code_for_prod() {
+        test_generate_rust_code(CodegenMode::Production);
+    }
+
+    #[test]
+    fn test_generate_rust_code_for_test() {
+        test_generate_rust_code(CodegenMode::Test);
+    }
 }
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index 687f319..e4baa82 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -35,8 +35,15 @@
 impl Input {
     fn try_parse_flags(&mut self) -> Result<ProtoParsedFlags> {
         let mut buffer = Vec::new();
-        self.reader.read_to_end(&mut buffer)?;
+        self.reader
+            .read_to_end(&mut buffer)
+            .with_context(|| format!("failed to read {}", self.source))?;
         crate::protos::parsed_flags::try_from_binary_proto(&buffer)
+            .with_context(|| self.error_context())
+    }
+
+    fn error_context(&self) -> String {
+        format!("failed to parse {}", self.source)
     }
 }
 
@@ -45,28 +52,36 @@
     pub contents: Vec<u8>,
 }
 
-const DEFAULT_FLAG_STATE: ProtoFlagState = ProtoFlagState::DISABLED;
-const DEFAULT_FLAG_PERMISSION: ProtoFlagPermission = ProtoFlagPermission::READ_WRITE;
+pub const DEFAULT_FLAG_STATE: ProtoFlagState = ProtoFlagState::DISABLED;
+pub const DEFAULT_FLAG_PERMISSION: ProtoFlagPermission = ProtoFlagPermission::READ_WRITE;
 
-pub fn parse_flags(package: &str, declarations: Vec<Input>, values: Vec<Input>) -> Result<Vec<u8>> {
+pub fn parse_flags(
+    package: &str,
+    declarations: Vec<Input>,
+    values: Vec<Input>,
+    default_permission: ProtoFlagPermission,
+) -> Result<Vec<u8>> {
     let mut parsed_flags = ProtoParsedFlags::new();
 
     for mut input in declarations {
         let mut contents = String::new();
-        input.reader.read_to_string(&mut contents)?;
+        input
+            .reader
+            .read_to_string(&mut contents)
+            .with_context(|| format!("failed to read {}", input.source))?;
 
         let flag_declarations = crate::protos::flag_declarations::try_from_text_proto(&contents)
-            .with_context(|| format!("Failed to parse {}", input.source))?;
+            .with_context(|| input.error_context())?;
         ensure!(
             package == flag_declarations.package(),
-            "Failed to parse {}: expected package {}, got {}",
+            "failed to parse {}: expected package {}, got {}",
             input.source,
             package,
             flag_declarations.package()
         );
         for mut flag_declaration in flag_declarations.flag.into_iter() {
             crate::protos::flag_declaration::verify_fields(&flag_declaration)
-                .with_context(|| format!("Failed to parse {}", input.source))?;
+                .with_context(|| input.error_context())?;
 
             // create ParsedFlag using FlagDeclaration and default values
             let mut parsed_flag = ProtoParsedFlag::new();
@@ -76,11 +91,17 @@
             parsed_flag.set_description(flag_declaration.take_description());
             parsed_flag.bug.append(&mut flag_declaration.bug);
             parsed_flag.set_state(DEFAULT_FLAG_STATE);
-            parsed_flag.set_permission(DEFAULT_FLAG_PERMISSION);
+            let flag_permission = if flag_declaration.is_fixed_read_only() {
+                ProtoFlagPermission::READ_ONLY
+            } else {
+                default_permission
+            };
+            parsed_flag.set_permission(flag_permission);
+            parsed_flag.set_is_fixed_read_only(flag_declaration.is_fixed_read_only());
             let mut tracepoint = ProtoTracepoint::new();
             tracepoint.set_source(input.source.clone());
             tracepoint.set_state(DEFAULT_FLAG_STATE);
-            tracepoint.set_permission(DEFAULT_FLAG_PERMISSION);
+            tracepoint.set_permission(flag_permission);
             parsed_flag.trace.push(tracepoint);
 
             // verify ParsedFlag looks reasonable
@@ -101,18 +122,32 @@
 
     for mut input in values {
         let mut contents = String::new();
-        input.reader.read_to_string(&mut contents)?;
+        input
+            .reader
+            .read_to_string(&mut contents)
+            .with_context(|| format!("failed to read {}", input.source))?;
         let flag_values = crate::protos::flag_values::try_from_text_proto(&contents)
-            .with_context(|| format!("Failed to parse {}", input.source))?;
+            .with_context(|| input.error_context())?;
         for flag_value in flag_values.flag_value.into_iter() {
             crate::protos::flag_value::verify_fields(&flag_value)
-                .with_context(|| format!("Failed to parse {}", input.source))?;
+                .with_context(|| input.error_context())?;
 
-            let Some(parsed_flag) = parsed_flags.parsed_flag.iter_mut().find(|pf| pf.package() == flag_value.package() && pf.name() == flag_value.name()) else {
+            let Some(parsed_flag) = parsed_flags
+                .parsed_flag
+                .iter_mut()
+                .find(|pf| pf.package() == flag_value.package() && pf.name() == flag_value.name())
+            else {
                 // (silently) skip unknown flags
                 continue;
             };
 
+            ensure!(
+                !parsed_flag.is_fixed_read_only()
+                    || flag_value.permission() == ProtoFlagPermission::READ_ONLY,
+                "failed to set permission of flag {}, since this flag is fixed read only flag",
+                flag_value.name()
+            );
+
             parsed_flag.set_state(flag_value.state());
             parsed_flag.set_permission(flag_value.permission());
             let mut tracepoint = ProtoTracepoint::new();
@@ -123,6 +158,8 @@
         }
     }
 
+    // Create a sorted parsed_flags
+    crate::protos::parsed_flags::sort_parsed_flags(&mut parsed_flags);
     crate::protos::parsed_flags::verify_fields(&parsed_flags)?;
     let mut output = Vec::new();
     parsed_flags.write_to_vec(&mut output)?;
@@ -151,12 +188,12 @@
     generate_cpp_code(package, parsed_flags.parsed_flag.iter(), codegen_mode)
 }
 
-pub fn create_rust_lib(mut input: Input) -> Result<OutputFile> {
+pub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<OutputFile> {
     let parsed_flags = input.try_parse_flags()?;
     let Some(package) = find_unique_package(&parsed_flags) else {
         bail!("no parsed flags, or the parsed flags use different packages");
     };
-    generate_rust_code(package, parsed_flags.parsed_flag.iter())
+    generate_rust_code(package, parsed_flags.parsed_flag.iter(), codegen_mode)
 }
 
 pub fn create_device_config_defaults(mut input: Input) -> Result<Vec<u8>> {
@@ -207,7 +244,7 @@
 #[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
 pub enum DumpFormat {
     Text,
-    Debug,
+    Verbose,
     Protobuf,
     Textproto,
 }
@@ -223,18 +260,27 @@
         DumpFormat::Text => {
             for parsed_flag in parsed_flags.parsed_flag.into_iter() {
                 let line = format!(
-                    "{}/{}: {:?} {:?}\n",
+                    "{}/{}: {:?} + {:?}\n",
                     parsed_flag.package(),
                     parsed_flag.name(),
-                    parsed_flag.state(),
-                    parsed_flag.permission()
+                    parsed_flag.permission(),
+                    parsed_flag.state()
                 );
                 output.extend_from_slice(line.as_bytes());
             }
         }
-        DumpFormat::Debug => {
+        DumpFormat::Verbose => {
             for parsed_flag in parsed_flags.parsed_flag.into_iter() {
-                let line = format!("{:#?}\n", parsed_flag);
+                let sources: Vec<_> =
+                    parsed_flag.trace.iter().map(|tracepoint| tracepoint.source()).collect();
+                let line = format!(
+                    "{}/{}: {:?} + {:?} ({})\n",
+                    parsed_flag.package(),
+                    parsed_flag.name(),
+                    parsed_flag.permission(),
+                    parsed_flag.state(),
+                    sources.join(", ")
+                );
                 output.extend_from_slice(line.as_bytes());
             }
         }
@@ -277,6 +323,7 @@
         assert_eq!(ProtoFlagState::ENABLED, enabled_ro.state());
         assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.permission());
         assert_eq!(3, enabled_ro.trace.len());
+        assert!(!enabled_ro.is_fixed_read_only());
         assert_eq!("tests/test.aconfig", enabled_ro.trace[0].source());
         assert_eq!(ProtoFlagState::DISABLED, enabled_ro.trace[0].state());
         assert_eq!(ProtoFlagPermission::READ_WRITE, enabled_ro.trace[0].permission());
@@ -287,8 +334,11 @@
         assert_eq!(ProtoFlagState::ENABLED, enabled_ro.trace[2].state());
         assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.trace[2].permission());
 
-        assert_eq!(4, parsed_flags.parsed_flag.len());
+        assert_eq!(5, parsed_flags.parsed_flag.len());
         for pf in parsed_flags.parsed_flag.iter() {
+            if pf.name() == "enabled_fixed_ro" {
+                continue;
+            }
             let first = pf.trace.first().unwrap();
             assert_eq!(DEFAULT_FLAG_STATE, first.state());
             assert_eq!(DEFAULT_FLAG_PERMISSION, first.permission());
@@ -297,6 +347,85 @@
             assert_eq!(pf.state(), last.state());
             assert_eq!(pf.permission(), last.permission());
         }
+
+        let enabled_fixed_ro =
+            parsed_flags.parsed_flag.iter().find(|pf| pf.name() == "enabled_fixed_ro").unwrap();
+        assert!(enabled_fixed_ro.is_fixed_read_only());
+        assert_eq!(ProtoFlagState::ENABLED, enabled_fixed_ro.state());
+        assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.permission());
+        assert_eq!(2, enabled_fixed_ro.trace.len());
+        assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.trace[0].permission());
+        assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.trace[1].permission());
+    }
+
+    #[test]
+    fn test_parse_flags_setting_default() {
+        let first_flag = r#"
+        package: "com.first"
+        flag {
+            name: "first"
+            namespace: "first_ns"
+            description: "This is the description of the first flag."
+            bug: "123"
+        }
+        "#;
+        let declaration =
+            vec![Input { source: "momery".to_string(), reader: Box::new(first_flag.as_bytes()) }];
+        let value: Vec<Input> = vec![];
+
+        let flags_bytes = crate::commands::parse_flags(
+            "com.first",
+            declaration,
+            value,
+            ProtoFlagPermission::READ_ONLY,
+        )
+        .unwrap();
+        let parsed_flags =
+            crate::protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();
+        assert_eq!(1, parsed_flags.parsed_flag.len());
+        let parsed_flag = parsed_flags.parsed_flag.first().unwrap();
+        assert_eq!(ProtoFlagState::DISABLED, parsed_flag.state());
+        assert_eq!(ProtoFlagPermission::READ_ONLY, parsed_flag.permission());
+    }
+
+    #[test]
+    fn test_parse_flags_override_fixed_read_only() {
+        let first_flag = r#"
+        package: "com.first"
+        flag {
+            name: "first"
+            namespace: "first_ns"
+            description: "This is the description of the first flag."
+            bug: "123"
+            is_fixed_read_only: true
+        }
+        "#;
+        let declaration =
+            vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }];
+
+        let first_flag_value = r#"
+        flag_value {
+            package: "com.first"
+            name: "first"
+            state: DISABLED
+            permission: READ_WRITE
+        }
+        "#;
+        let value = vec![Input {
+            source: "memory".to_string(),
+            reader: Box::new(first_flag_value.as_bytes()),
+        }];
+        let error = crate::commands::parse_flags(
+            "com.first",
+            declaration,
+            value,
+            ProtoFlagPermission::READ_WRITE,
+        )
+        .unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            "failed to set permission of flag first, since this flag is fixed read only flag"
+        );
     }
 
     #[test]
@@ -320,7 +449,7 @@
         let input = parse_test_flags_as_input();
         let bytes = dump_parsed_flags(vec![input], DumpFormat::Text).unwrap();
         let text = std::str::from_utf8(&bytes).unwrap();
-        assert!(text.contains("com.android.aconfig.test/disabled_ro: DISABLED READ_ONLY"));
+        assert!(text.contains("com.android.aconfig.test/disabled_ro: READ_ONLY + DISABLED"));
     }
 
     #[test]
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index 72feb94..84073f7 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -16,7 +16,7 @@
 
 //! `aconfig` is a build time tool to manage build time configurations, such as feature flags.
 
-use anyhow::{anyhow, bail, ensure, Result};
+use anyhow::{anyhow, bail, Context, Result};
 use clap::{builder::ArgAction, builder::EnumValueParser, Arg, ArgMatches, Command};
 use core::any::Any;
 use std::fs;
@@ -44,6 +44,14 @@
                 .arg(Arg::new("package").long("package").required(true))
                 .arg(Arg::new("declarations").long("declarations").action(ArgAction::Append))
                 .arg(Arg::new("values").long("values").action(ArgAction::Append))
+                .arg(
+                    Arg::new("default-permission")
+                        .long("default-permission")
+                        .value_parser(protos::flag_permission::parse_from_str)
+                        .default_value(protos::flag_permission::to_string(
+                            &commands::DEFAULT_FLAG_PERMISSION,
+                        )),
+                )
                 .arg(Arg::new("cache").long("cache").required(true)),
         )
         .subcommand(
@@ -71,7 +79,13 @@
         .subcommand(
             Command::new("create-rust-lib")
                 .arg(Arg::new("cache").long("cache").required(true))
-                .arg(Arg::new("out").long("out").required(true)),
+                .arg(Arg::new("out").long("out").required(true))
+                .arg(
+                    Arg::new("mode")
+                        .long("mode")
+                        .value_parser(EnumValueParser::<commands::CodegenMode>::new())
+                        .default_value("production"),
+                ),
         )
         .subcommand(
             Command::new("create-device-config-defaults")
@@ -123,26 +137,27 @@
 }
 
 fn write_output_file_realtive_to_dir(root: &Path, output_file: &OutputFile) -> Result<()> {
-    ensure!(
-        root.is_dir(),
-        "output directory {} does not exist or is not a directory",
-        root.display()
-    );
     let path = root.join(output_file.path.clone());
     let parent = path
         .parent()
         .ok_or(anyhow!("unable to locate parent of output file {}", path.display()))?;
-    fs::create_dir_all(parent)?;
-    let mut file = fs::File::create(path)?;
-    file.write_all(&output_file.contents)?;
+    fs::create_dir_all(parent)
+        .with_context(|| format!("failed to create directory {}", parent.display()))?;
+    let mut file = fs::File::create(path.clone())
+        .with_context(|| format!("failed to open {}", path.display()))?;
+    file.write_all(&output_file.contents)
+        .with_context(|| format!("failed to write to {}", path.display()))?;
     Ok(())
 }
 
 fn write_output_to_file_or_stdout(path: &str, data: &[u8]) -> Result<()> {
     if path == "-" {
-        io::stdout().write_all(data)?;
+        io::stdout().write_all(data).context("failed to write to stdout")?;
     } else {
-        fs::File::create(path)?.write_all(data)?;
+        fs::File::create(path)
+            .with_context(|| format!("failed to open {}", path))?
+            .write_all(data)
+            .with_context(|| format!("failed to write to {}", path))?;
     }
     Ok(())
 }
@@ -154,14 +169,18 @@
             let package = get_required_arg::<String>(sub_matches, "package")?;
             let declarations = open_zero_or_more_files(sub_matches, "declarations")?;
             let values = open_zero_or_more_files(sub_matches, "values")?;
-            let output = commands::parse_flags(package, declarations, values)?;
+            let default_permission =
+                get_required_arg::<protos::ProtoFlagPermission>(sub_matches, "default-permission")?;
+            let output = commands::parse_flags(package, declarations, values, *default_permission)
+                .context("failed to create cache")?;
             let path = get_required_arg::<String>(sub_matches, "cache")?;
             write_output_to_file_or_stdout(path, &output)?;
         }
         Some(("create-java-lib", sub_matches)) => {
             let cache = open_single_file(sub_matches, "cache")?;
             let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?;
-            let generated_files = commands::create_java_lib(cache, *mode)?;
+            let generated_files =
+                commands::create_java_lib(cache, *mode).context("failed to create java lib")?;
             let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
             generated_files
                 .iter()
@@ -170,7 +189,8 @@
         Some(("create-cpp-lib", sub_matches)) => {
             let cache = open_single_file(sub_matches, "cache")?;
             let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?;
-            let generated_files = commands::create_cpp_lib(cache, *mode)?;
+            let generated_files =
+                commands::create_cpp_lib(cache, *mode).context("failed to create cpp lib")?;
             let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
             generated_files
                 .iter()
@@ -178,25 +198,30 @@
         }
         Some(("create-rust-lib", sub_matches)) => {
             let cache = open_single_file(sub_matches, "cache")?;
-            let generated_file = commands::create_rust_lib(cache)?;
+            let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?;
+            let generated_file =
+                commands::create_rust_lib(cache, *mode).context("failed to create rust lib")?;
             let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
             write_output_file_realtive_to_dir(&dir, &generated_file)?;
         }
         Some(("create-device-config-defaults", sub_matches)) => {
             let cache = open_single_file(sub_matches, "cache")?;
-            let output = commands::create_device_config_defaults(cache)?;
+            let output = commands::create_device_config_defaults(cache)
+                .context("failed to create device config defaults")?;
             let path = get_required_arg::<String>(sub_matches, "out")?;
             write_output_to_file_or_stdout(path, &output)?;
         }
         Some(("create-device-config-sysprops", sub_matches)) => {
             let cache = open_single_file(sub_matches, "cache")?;
-            let output = commands::create_device_config_sysprops(cache)?;
+            let output = commands::create_device_config_sysprops(cache)
+                .context("failed to create device config sysprops")?;
             let path = get_required_arg::<String>(sub_matches, "out")?;
             write_output_to_file_or_stdout(path, &output)?;
         }
         Some(("dump", sub_matches)) => {
             let input = open_zero_or_more_files(sub_matches, "cache")?;
-            let format = get_required_arg::<DumpFormat>(sub_matches, "format")?;
+            let format = get_required_arg::<DumpFormat>(sub_matches, "format")
+                .context("failed to dump previously parsed flags")?;
             let output = commands::dump_parsed_flags(input, *format)?;
             let path = get_required_arg::<String>(sub_matches, "out")?;
             write_output_to_file_or_stdout(path, &output)?;
diff --git a/tools/aconfig/src/protos.rs b/tools/aconfig/src/protos.rs
index 17019be..b93abcc 100644
--- a/tools/aconfig/src/protos.rs
+++ b/tools/aconfig/src/protos.rs
@@ -92,8 +92,7 @@
         ensure!(codegen::is_valid_name_ident(pdf.name()), "bad flag declaration: bad name");
         ensure!(codegen::is_valid_name_ident(pdf.namespace()), "bad flag declaration: bad name");
         ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description");
-
-        // ProtoFlagDeclaration.bug: Vec<String>: may be empty, no checks needed
+        ensure!(pdf.bug.len() == 1, "bad flag declaration: exactly one bug required");
 
         Ok(())
     }
@@ -157,6 +156,26 @@
     }
 }
 
+pub mod flag_permission {
+    use super::*;
+    use anyhow::bail;
+
+    pub fn parse_from_str(permission: &str) -> Result<ProtoFlagPermission> {
+        match permission.to_ascii_lowercase().as_str() {
+            "read_write" => Ok(ProtoFlagPermission::READ_WRITE),
+            "read_only" => Ok(ProtoFlagPermission::READ_ONLY),
+            _ => bail!("Permission needs to be read_only or read_write."),
+        }
+    }
+
+    pub fn to_string(permission: &ProtoFlagPermission) -> &str {
+        match permission {
+            ProtoFlagPermission::READ_WRITE => "read_write",
+            ProtoFlagPermission::READ_ONLY => "read_only",
+        }
+    }
+}
+
 pub mod tracepoint {
     use super::*;
     use anyhow::ensure;
@@ -195,11 +214,15 @@
         for tp in pf.trace.iter() {
             super::tracepoint::verify_fields(tp)?;
         }
-
-        // ProtoParsedFlag.bug: Vec<String>: may be empty, no checks needed
+        ensure!(pf.bug.len() == 1, "bad flag declaration: exactly one bug required");
 
         Ok(())
     }
+
+    pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
+        debug_assert!(!pf.trace.is_empty());
+        pf.trace[0].source()
+    }
 }
 
 pub mod parsed_flags {
@@ -214,6 +237,8 @@
     }
 
     pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
+        use crate::protos::parsed_flag::path_to_declaration;
+
         let mut previous: Option<&ProtoParsedFlag> = None;
         for parsed_flag in pf.parsed_flag.iter() {
             if let Some(prev) = previous {
@@ -221,7 +246,12 @@
                 let b = create_sorting_key(parsed_flag);
                 match a.cmp(&b) {
                     Ordering::Less => {}
-                    Ordering::Equal => bail!("bad parsed flags: duplicate flag {}", a),
+                    Ordering::Equal => bail!(
+                        "bad parsed flags: duplicate flag {} (defined in {} and {})",
+                        a,
+                        path_to_declaration(prev),
+                        path_to_declaration(parsed_flag)
+                    ),
                     Ordering::Greater => {
                         bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
                     }
@@ -243,6 +273,10 @@
         Ok(merged)
     }
 
+    pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
+        pf.parsed_flag.sort_by_key(create_sorting_key);
+    }
+
     fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
         format!("{}.{}", pf.package(), pf.name())
     }
@@ -263,12 +297,13 @@
     namespace: "first_ns"
     description: "This is the description of the first flag."
     bug: "123"
-    bug: "abc"
 }
 flag {
     name: "second"
     namespace: "second_ns"
     description: "This is the description of the second flag."
+    bug: "abc"
+    is_fixed_read_only: true
 }
 "#,
         )
@@ -278,14 +313,14 @@
         assert_eq!(first.name(), "first");
         assert_eq!(first.namespace(), "first_ns");
         assert_eq!(first.description(), "This is the description of the first flag.");
-        assert_eq!(first.bug.len(), 2);
-        assert_eq!(first.bug[0], "123");
-        assert_eq!(first.bug[1], "abc");
+        assert_eq!(first.bug, vec!["123"]);
+        assert!(!first.is_fixed_read_only());
         let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
         assert_eq!(second.name(), "second");
         assert_eq!(second.namespace(), "second_ns");
         assert_eq!(second.description(), "This is the description of the second flag.");
-        assert_eq!(second.bug.len(), 0);
+        assert_eq!(second.bug, vec!["abc"]);
+        assert!(second.is_fixed_read_only());
 
         // bad input: missing package in flag declarations
         let error = flag_declarations::try_from_text_proto(
@@ -360,6 +395,36 @@
         )
         .unwrap_err();
         assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
+
+        // bad input: no bug entries in flag declaration
+        let error = flag_declarations::try_from_text_proto(
+            r#"
+package: "com.foo.bar"
+flag {
+    name: "first"
+    namespace: "first_ns"
+    description: "This is the description of the first flag."
+}
+"#,
+        )
+        .unwrap_err();
+        assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
+
+        // bad input: multiple bug entries in flag declaration
+        let error = flag_declarations::try_from_text_proto(
+            r#"
+package: "com.foo.bar"
+flag {
+    name: "first"
+    namespace: "first_ns"
+    description: "This is the description of the first flag."
+    bug: "123"
+    bug: "abc"
+}
+"#,
+        )
+        .unwrap_err();
+        assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
     }
 
     #[test]
@@ -466,6 +531,7 @@
     name: "first"
     namespace: "first_ns"
     description: "This is the description of the first flag."
+    bug: "SOME_BUG"
     state: DISABLED
     permission: READ_ONLY
     trace {
@@ -479,6 +545,7 @@
     name: "second"
     namespace: "second_ns"
     description: "This is the description of the second flag."
+    bug: "SOME_BUG"
     state: ENABLED
     permission: READ_WRITE
     trace {
@@ -491,6 +558,7 @@
         state: ENABLED
         permission: READ_WRITE
     }
+    is_fixed_read_only: true
 }
 "#;
         let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
@@ -500,6 +568,7 @@
         assert_eq!(second.name(), "second");
         assert_eq!(second.namespace(), "second_ns");
         assert_eq!(second.description(), "This is the description of the second flag.");
+        assert_eq!(second.bug, vec!["SOME_BUG"]);
         assert_eq!(second.state(), ProtoFlagState::ENABLED);
         assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
         assert_eq!(2, second.trace.len());
@@ -509,6 +578,7 @@
         assert_eq!(second.trace[1].source(), "flags.values");
         assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
         assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_WRITE);
+        assert!(second.is_fixed_read_only());
 
         // valid input: empty
         let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
@@ -553,6 +623,7 @@
     name: "first"
     namespace: "first_ns"
     description: "This is the description of the first flag."
+    bug: ""
     state: DISABLED
     permission: READ_ONLY
     trace {
@@ -566,6 +637,7 @@
     name: "second"
     namespace: "second_ns"
     description: "This is the description of the second flag."
+    bug: ""
     state: ENABLED
     permission: READ_WRITE
     trace {
@@ -588,6 +660,7 @@
     name: "bbb"
     namespace: "first_ns"
     description: "This is the description of the first flag."
+    bug: ""
     state: DISABLED
     permission: READ_ONLY
     trace {
@@ -601,6 +674,7 @@
     name: "aaa"
     namespace: "second_ns"
     description: "This is the description of the second flag."
+    bug: ""
     state: ENABLED
     permission: READ_WRITE
     trace {
@@ -623,6 +697,7 @@
     name: "bar"
     namespace: "first_ns"
     description: "This is the description of the first flag."
+    bug: ""
     state: DISABLED
     permission: READ_ONLY
     trace {
@@ -636,6 +711,7 @@
     name: "bar"
     namespace: "second_ns"
     description: "This is the description of the second flag."
+    bug: ""
     state: ENABLED
     permission: READ_WRITE
     trace {
@@ -646,7 +722,38 @@
 }
 "#;
         let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
-        assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar");
+        assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
+    }
+
+    #[test]
+    fn test_parsed_flag_path_to_declaration() {
+        let text_proto = r#"
+parsed_flag {
+    package: "com.foo"
+    name: "bar"
+    namespace: "first_ns"
+    description: "This is the description of the first flag."
+    bug: "b/12345678"
+    state: DISABLED
+    permission: READ_ONLY
+    trace {
+        source: "flags.declarations"
+        state: DISABLED
+        permission: READ_ONLY
+    }
+    trace {
+        source: "flags.values"
+        state: ENABLED
+        permission: READ_ONLY
+    }
+}
+"#;
+        let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
+        let parsed_flag = &parsed_flags.parsed_flag[0];
+        assert_eq!(
+            crate::protos::parsed_flag::path_to_declaration(parsed_flag),
+            "flags.declarations"
+        );
     }
 
     #[test]
@@ -657,6 +764,7 @@
     name: "first"
     namespace: "first_ns"
     description: "This is the description of the first flag."
+    bug: "a"
     state: DISABLED
     permission: READ_ONLY
     trace {
@@ -670,6 +778,7 @@
     name: "second"
     namespace: "second_ns"
     description: "This is the description of the second flag."
+    bug: "b"
     state: ENABLED
     permission: READ_WRITE
     trace {
@@ -687,6 +796,7 @@
     name: "first"
     namespace: "first_ns"
     description: "This is the description of the first flag."
+    bug: "a"
     state: DISABLED
     permission: READ_ONLY
     trace {
@@ -703,6 +813,7 @@
     package: "com.second"
     name: "second"
     namespace: "second_ns"
+    bug: "b"
     description: "This is the description of the second flag."
     state: ENABLED
     permission: READ_WRITE
@@ -717,7 +828,7 @@
 
         // bad cases
         let error = parsed_flags::merge(vec![first.clone(), first.clone()]).unwrap_err();
-        assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.first.first");
+        assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.first.first (defined in flags.declarations and flags.declarations)");
 
         // valid cases
         assert!(parsed_flags::merge(vec![]).unwrap().parsed_flag.is_empty());
diff --git a/tools/aconfig/src/test.rs b/tools/aconfig/src/test.rs
index 04bbe28..9034704 100644
--- a/tools/aconfig/src/test.rs
+++ b/tools/aconfig/src/test.rs
@@ -41,6 +41,7 @@
     state: DISABLED
     permission: READ_ONLY
   }
+  is_fixed_read_only: false
 }
 parsed_flag {
   package: "com.android.aconfig.test"
@@ -55,13 +56,33 @@
     state: DISABLED
     permission: READ_WRITE
   }
+  is_fixed_read_only: false
+}
+parsed_flag {
+  package: "com.android.aconfig.test"
+  name: "enabled_fixed_ro"
+  namespace: "aconfig_test"
+  description: "This flag is fixed READ_ONLY + ENABLED"
+  bug: ""
+  state: ENABLED
+  permission: READ_ONLY
+  trace {
+    source: "tests/test.aconfig"
+    state: DISABLED
+    permission: READ_ONLY
+  }
+  trace {
+    source: "tests/first.values"
+    state: ENABLED
+    permission: READ_ONLY
+  }
+  is_fixed_read_only: true
 }
 parsed_flag {
   package: "com.android.aconfig.test"
   name: "enabled_ro"
   namespace: "aconfig_test"
   description: "This flag is ENABLED + READ_ONLY"
-  bug: "789"
   bug: "abc"
   state: ENABLED
   permission: READ_ONLY
@@ -80,12 +101,14 @@
     state: ENABLED
     permission: READ_ONLY
   }
+  is_fixed_read_only: false
 }
 parsed_flag {
   package: "com.android.aconfig.test"
   name: "enabled_rw"
   namespace: "aconfig_test"
   description: "This flag is ENABLED + READ_WRITE"
+  bug: ""
   state: ENABLED
   permission: READ_WRITE
   trace {
@@ -98,6 +121,7 @@
     state: ENABLED
     permission: READ_WRITE
   }
+  is_fixed_read_only: false
 }
 "#;
 
@@ -118,6 +142,7 @@
                     reader: Box::new(include_bytes!("../tests/second.values").as_slice()),
                 },
             ],
+            crate::commands::DEFAULT_FLAG_PERMISSION,
         )
         .unwrap();
         crate::protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
diff --git a/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template
new file mode 100644
index 0000000..82bea81
--- /dev/null
+++ b/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template
@@ -0,0 +1,45 @@
+package {package_name};
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class FakeFeatureFlagsImpl implements FeatureFlags \{
+    public FakeFeatureFlagsImpl() \{
+        resetAll();
+    }
+
+{{ for item in class_elements}}
+    @Override
+    public boolean {item.method_name}() \{
+        return getFlag(Flags.FLAG_{item.flag_name_constant_suffix});
+    }
+{{ endfor}}
+    public void setFlag(String flagName, boolean value) \{
+        if (!this.mFlagMap.containsKey(flagName)) \{
+            throw new IllegalArgumentException("no such flag " + flagName);
+        }
+        this.mFlagMap.put(flagName, value);
+    }
+
+    public void resetAll() \{
+        for (Map.Entry entry : mFlagMap.entrySet()) \{
+            entry.setValue(null);
+        }
+    }
+
+    private boolean getFlag(String flagName) \{
+        Boolean value = this.mFlagMap.get(flagName);
+        if (value == null) \{
+            throw new IllegalArgumentException(flagName + " is not set");
+        }
+        return value;
+    }
+
+    private Map<String, Boolean> mFlagMap = new HashMap<>(
+        Map.of(
+            {{-for item in class_elements}}
+            Flags.FLAG_{item.flag_name_constant_suffix}, false{{ if not @last }},{{ endif }}
+            {{ -endfor }}
+        )
+    );
+}
diff --git a/tools/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/templates/FeatureFlagsImpl.java.template
index 082d476..96de06c 100644
--- a/tools/aconfig/templates/FeatureFlagsImpl.java.template
+++ b/tools/aconfig/templates/FeatureFlagsImpl.java.template
@@ -1,65 +1,33 @@
 package {package_name};
-{{ -if is_test_mode }}
-import static java.util.stream.Collectors.toMap;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.stream.Stream;
-{{ else}}
+{{ if not is_test_mode }}
 {{ if is_read_write- }}
 import android.provider.DeviceConfig;
-{{ -endif- }}
 {{ endif }}
 public final class FeatureFlagsImpl implements FeatureFlags \{
 {{ for item in class_elements}}
     @Override
     public boolean {item.method_name}() \{
-        {{ -if not is_test_mode- }}
-        {{ if item.is_read_write }}
+    {{ -if item.is_read_write }}
         return DeviceConfig.getBoolean(
             "{item.device_config_namespace}",
             "{item.device_config_flag}",
             {item.default_value}
         );
-        {{ -else }}
+    {{ else }}
         return {item.default_value};
-        {{ -endif- }}
-        {{ else }}
-        return getFlag(Flags.FLAG_{item.flag_name_constant_suffix});
-        {{ -endif }}
+    {{ endif- }}
     }
-{{ endfor- }}
-{{ if is_test_mode }}
-    public void setFlag(String flagName, boolean value) \{
-        if (!this.mFlagMap.containsKey(flagName)) \{
-            throw new IllegalArgumentException("no such flag" + flagName);
-        }
-        this.mFlagMap.put(flagName, value);
-    }
-
-    public void resetAll() \{
-        for (Map.Entry entry : mFlagMap.entrySet()) \{
-            entry.setValue(null);
-        }
-    }
-
-    private boolean getFlag(String flagName) \{
-        Boolean value = this.mFlagMap.get(flagName);
-        if (value == null) \{
-            throw new IllegalArgumentException(flagName + " is not set");
-        }
-        return value;
-    }
-
-    private HashMap<String, Boolean> mFlagMap = Stream.of(
-            {{-for item in class_elements}}
-            Flags.FLAG_{item.flag_name_constant_suffix}{{ if not @last }},{{ endif }}
-            {{ -endfor }}
-        )
-        .collect(
-            HashMap::new,
-            (map, elem) -> map.put(elem, null),
-            HashMap::putAll
-        );
-{{ -endif }}
+{{ endfor }}
 }
+{{ else }}
+{#- Generate only stub if in test mode #}
+public final class FeatureFlagsImpl implements FeatureFlags \{
+{{ for item in class_elements}}
+    @Override
+    public boolean {item.method_name}() \{
+        throw new UnsupportedOperationException(
+            "Method is not implemented.");
+    }
+{{ endfor }}
+}
+{{ endif }}
diff --git a/tools/aconfig/templates/Flags.java.template b/tools/aconfig/templates/Flags.java.template
index c244b15..012eba6 100644
--- a/tools/aconfig/templates/Flags.java.template
+++ b/tools/aconfig/templates/Flags.java.template
@@ -10,11 +10,11 @@
     }
 {{ endfor }}
 {{ -if is_test_mode }}
-    public static void setFeatureFlagsImpl(FeatureFlags featureFlags) \{
+    public static void setFeatureFlags(FeatureFlags featureFlags) \{
         Flags.FEATURE_FLAGS = featureFlags;
     }
 
-    public static void unsetFeatureFlagsImpl() \{
+    public static void unsetFeatureFlags() \{
         Flags.FEATURE_FLAGS = null;
     }
 {{ endif}}
diff --git a/tools/aconfig/templates/cpp_exported_header.template b/tools/aconfig/templates/cpp_exported_header.template
index e244de3..4d56dbc 100644
--- a/tools/aconfig/templates/cpp_exported_header.template
+++ b/tools/aconfig/templates/cpp_exported_header.template
@@ -1,12 +1,9 @@
-#ifndef {header}_HEADER_H
-#define {header}_HEADER_H
+#pragma once
 
-#include <string>
+#ifdef __cplusplus
+
 #include <memory>
-{{ if readwrite }}
-#include <server_configurable_flags/get_flags.h>
-using namespace server_configurable_flags;
-{{ endif }}
+
 namespace {cpp_namespace} \{
 
 class flag_provider_interface \{
@@ -14,35 +11,63 @@
     virtual ~flag_provider_interface() = default;
     {{ for item in class_elements}}
     virtual bool {item.flag_name}() = 0;
-    {{ endfor }}
-    virtual void override_flag(std::string const&, bool) \{}
 
-    virtual void reset_overrides() \{}
+    {{ if for_test }}
+    virtual void {item.flag_name}(bool val) = 0;
+    {{ -endif }}
+    {{ endfor }}
+
+    {{ if for_test }}
+    virtual void reset_flags() \{}
+    {{ -endif }}
 };
 
 extern std::unique_ptr<flag_provider_interface> provider_;
-{{ for item in class_elements}}
-extern std::string const {item.uppercase_flag_name};{{ endfor }}
+
 {{ for item in class_elements}}
 inline bool {item.flag_name}() \{
-    {{ if for_prod }}
+    {{ if for_test }}
+    return provider_->{item.flag_name}();
+    {{ -else- }}
     {{ if not item.readwrite- }}
     return {item.default_value};
     {{ -else- }}
     return provider_->{item.flag_name}();
     {{ -endif }}
-    {{ -else- }}
-    return provider_->{item.flag_name}();
     {{ -endif }}
 }
+
+{{ if for_test }}
+inline void {item.flag_name}(bool val) \{
+    provider_->{item.flag_name}(val);
+}
+{{ -endif }}
 {{ endfor }}
-inline void override_flag(std::string const& name, bool val) \{
-    return provider_->override_flag(name, val);
-}
 
-inline void reset_overrides() \{
-    return provider_->reset_overrides();
+{{ if for_test }}
+inline void reset_flags() \{
+    return provider_->reset_flags();
 }
+{{ -endif }}
 
 }
+
+extern "C" \{
+#endif // __cplusplus
+
+{{ for item in class_elements }}
+bool {header}_{item.flag_name}();
+
+{{ if for_test }}
+void set_{header}_{item.flag_name}(bool val);
+{{ -endif }}
+{{ endfor - }}
+
+{{ if for_test }}
+void {header}_reset_flags();
+{{ -endif }}
+
+#ifdef __cplusplus
+} // extern "C"
 #endif
+
diff --git a/tools/aconfig/templates/cpp_prod_flag_provider.template b/tools/aconfig/templates/cpp_prod_flag_provider.template
deleted file mode 100644
index c966ed4..0000000
--- a/tools/aconfig/templates/cpp_prod_flag_provider.template
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef {header}_flag_provider_HEADER_H
-#define {header}_flag_provider_HEADER_H
-#include "{header}.h"
-
-namespace {cpp_namespace} \{
-class flag_provider : public flag_provider_interface \{
-public:
-    {{ for item in class_elements}}
-    virtual bool {item.flag_name}() override \{
-        {{ if item.readwrite- }}
-        return GetServerConfigurableFlag(
-            "{item.device_config_namespace}",
-            "{item.device_config_flag}",
-            "{item.default_value}") == "true";
-        {{ -else- }}
-            return {item.default_value};
-        {{ -endif }}
-    }
-    {{ endfor }}
-};
-}
-#endif
diff --git a/tools/aconfig/templates/cpp_source_file.template b/tools/aconfig/templates/cpp_source_file.template
index 1b4f336..f373bc6 100644
--- a/tools/aconfig/templates/cpp_source_file.template
+++ b/tools/aconfig/templates/cpp_source_file.template
@@ -1,10 +1,98 @@
-
 #include "{header}.h"
-#include "{header}_flag_provider.h"
+{{ if readwrite }}
+#include <server_configurable_flags/get_flags.h>
+{{ endif }}
 
 namespace {cpp_namespace} \{
-{{ for item in class_elements}}
-std::string const {item.uppercase_flag_name} = "{item.device_config_flag}";{{ endfor }}
+
+{{ if for_test }}
+    class flag_provider : public flag_provider_interface \{
+    private:
+        std::unordered_map<std::string, bool> overrides_;
+
+    public:
+        flag_provider()
+            : overrides_()
+        \{}
+
+        {{ for item in class_elements}}
+        virtual bool {item.flag_name}() override \{
+            auto it = overrides_.find("{item.flag_name}");
+              if (it != overrides_.end()) \{
+                  return it->second;
+            } else \{
+              {{ if item.readwrite- }}
+              return server_configurable_flags::GetServerConfigurableFlag(
+                  "aconfig_flags.{item.device_config_namespace}",
+                  "{item.device_config_flag}",
+                  "{item.default_value}") == "true";
+              {{ -else- }}
+                  return {item.default_value};
+              {{ -endif }}
+            }
+        }
+
+        virtual void {item.flag_name}(bool val) override \{
+            overrides_["{item.flag_name}"] = val;
+        }
+        {{ endfor }}
+
+        virtual void reset_flags() override \{
+            overrides_.clear();
+        }
+    };
+
+{{ -else- }}
+
+    class flag_provider : public flag_provider_interface \{
+    public:
+        {{ for item in class_elements}}
+        virtual bool {item.flag_name}() override \{
+            {{ if item.readwrite- }}
+            return server_configurable_flags::GetServerConfigurableFlag(
+                "aconfig_flags.{item.device_config_namespace}",
+                "{item.device_config_flag}",
+                "{item.default_value}") == "true";
+            {{ -else- }}
+                return {item.default_value};
+            {{ -endif }}
+        }
+        {{ endfor }}
+    };
+
+
+{{ -endif }}
+
+
 std::unique_ptr<flag_provider_interface> provider_ =
     std::make_unique<flag_provider>();
+
 }
+
+
+{{ for item in class_elements}}
+bool {header}_{item.flag_name}() \{
+    {{ if for_test }}
+    return {cpp_namespace}::{item.flag_name}();
+    {{ -else- }}
+    {{ if not item.readwrite- }}
+    return {item.default_value};
+    {{ -else- }}
+    return {cpp_namespace}::{item.flag_name}();
+    {{ -endif }}
+    {{ -endif }}
+}
+
+{{ if for_test }}
+void set_{header}_{item.flag_name}(bool val) \{
+    {cpp_namespace}::{item.flag_name}(val);
+}
+{{ -endif }}
+{{ endfor -}}
+
+{{ if for_test }}
+void {header}_reset_flags() \{
+     {cpp_namespace}::reset_flags();
+}
+{{ -endif }}
+
diff --git a/tools/aconfig/templates/cpp_test_flag_provider.template b/tools/aconfig/templates/cpp_test_flag_provider.template
deleted file mode 100644
index bd597e7..0000000
--- a/tools/aconfig/templates/cpp_test_flag_provider.template
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef {header}_flag_provider_HEADER_H
-#define {header}_flag_provider_HEADER_H
-#include "{header}.h"
-
-#include <unordered_map>
-#include <unordered_set>
-#include <cassert>
-
-namespace {cpp_namespace} \{
-class flag_provider : public flag_provider_interface \{
-private:
-    std::unordered_map<std::string, bool> overrides_;
-    std::unordered_set<std::string> flag_names_;
-
-public:
-    flag_provider()
-        : overrides_(),
-        flag_names_() \{
-        {{ for item in class_elements}}
-        flag_names_.insert({item.uppercase_flag_name});{{ endfor }}
-    }
-    {{ for item in class_elements}}
-    virtual bool {item.flag_name}() override \{
-        auto it = overrides_.find({item.uppercase_flag_name});
-	      if (it != overrides_.end()) \{
-	          return it->second;
-        } else \{
-          {{ if item.readwrite- }}
-          return GetServerConfigurableFlag(
-              "{item.device_config_namespace}",
-              "{item.device_config_flag}",
-              "{item.default_value}") == "true";
-          {{ -else- }}
-              return {item.default_value};
-          {{ -endif }}
-        }
-    }
-    {{ endfor }}
-    virtual void override_flag(std::string const& flag, bool val) override \{
-        assert(flag_names_.count(flag));
-        overrides_[flag] = val;
-    }
-
-    virtual void reset_overrides() override \{
-        overrides_.clear();
-    }
-};
-}
-#endif
diff --git a/tools/aconfig/templates/rust.template b/tools/aconfig/templates/rust.template
deleted file mode 100644
index 960c494..0000000
--- a/tools/aconfig/templates/rust.template
+++ /dev/null
@@ -1,29 +0,0 @@
-{{- for mod in modules -}}
-pub mod {mod} \{
-{{ endfor -}}
-{{- for flag in template_flags -}}
-{{- if flag.is_read_only_disabled -}}
-#[inline(always)]
-pub const fn r#{flag.name}() -> bool \{
-    false
-}
-
-{{ endif -}}
-{{- if flag.is_read_only_enabled -}}
-#[inline(always)]
-pub const fn r#{flag.name}() -> bool \{
-    true
-}
-
-{{ endif -}}
-{{- if flag.is_read_write -}}
-#[inline(always)]
-pub fn r#{flag.name}() -> bool \{
-    flags_rust::GetServerConfigurableFlag("{flag.device_config_namespace}", "{flag.device_config_flag}", "false") == "true"
-}
-
-{{ endif -}}
-{{- endfor -}}
-{{- for mod in modules -}}
-}
-{{ endfor -}}
diff --git a/tools/aconfig/templates/rust_prod.template b/tools/aconfig/templates/rust_prod.template
new file mode 100644
index 0000000..e22ad6f
--- /dev/null
+++ b/tools/aconfig/templates/rust_prod.template
@@ -0,0 +1,38 @@
+//! codegenerated rust flag lib
+
+/// flag provider
+pub struct FlagProvider;
+
+impl FlagProvider \{
+
+    {{ for flag in template_flags }}
+    /// query flag {flag.name}
+    pub fn {flag.name}(&self) -> bool \{
+    {{ if flag.readwrite -}}
+        flags_rust::GetServerConfigurableFlag(
+          "aconfig_flags.{flag.device_config_namespace}",
+          "{flag.device_config_flag}",
+          "{flag.default_value}") == "true"
+    {{ -else- }}
+        {flag.default_value}
+    {{ -endif }}
+    }
+    {{ endfor }}
+
+}
+
+/// flag provider
+pub static PROVIDER: FlagProvider = FlagProvider;
+
+{{ for flag in template_flags }}
+/// query flag {flag.name}
+#[inline(always)]
+{{ if flag.readwrite -}}
+pub fn {flag.name}() -> bool \{
+    PROVIDER.{flag.name}()
+{{ -else- }}
+pub fn {flag.name}() -> bool \{
+    {flag.default_value}
+{{ -endif }}
+}
+{{ endfor }}
diff --git a/tools/aconfig/templates/rust_test.template b/tools/aconfig/templates/rust_test.template
new file mode 100644
index 0000000..fd1229b
--- /dev/null
+++ b/tools/aconfig/templates/rust_test.template
@@ -0,0 +1,61 @@
+//! codegenerated rust flag lib
+
+use std::collections::BTreeMap;
+use std::sync::Mutex;
+
+/// flag provider
+pub struct FlagProvider \{
+    overrides: BTreeMap<&'static str, bool>,
+}
+
+impl FlagProvider \{
+    {{ for flag in template_flags }}
+    /// query flag {flag.name}
+    pub fn {flag.name}(&self) -> bool \{
+        self.overrides.get("{flag.name}").copied().unwrap_or(
+        {{ if flag.readwrite -}}
+          flags_rust::GetServerConfigurableFlag(
+            "aconfig_flags.{flag.device_config_namespace}",
+            "{flag.device_config_flag}",
+            "{flag.default_value}") == "true"
+        {{ -else- }}
+           {flag.default_value}
+        {{ -endif }}
+        )
+    }
+
+    /// set flag {flag.name}
+    pub fn set_{flag.name}(&mut self, val: bool) \{
+        self.overrides.insert("{flag.name}", val);
+    }
+    {{ endfor }}
+
+    /// clear all flag overrides
+    pub fn reset_flags(&mut self) \{
+        self.overrides.clear();
+    }
+}
+
+/// flag provider
+pub static PROVIDER: Mutex<FlagProvider> = Mutex::new(
+    FlagProvider \{overrides: BTreeMap::new()}
+);
+
+{{ for flag in template_flags }}
+/// query flag {flag.name}
+#[inline(always)]
+pub fn {flag.name}() -> bool \{
+    PROVIDER.lock().unwrap().{flag.name}()
+}
+
+/// set flag {flag.name}
+#[inline(always)]
+pub fn set_{flag.name}(val: bool) \{
+    PROVIDER.lock().unwrap().set_{flag.name}(val);
+}
+{{ endfor }}
+
+/// clear all flag override
+pub fn reset_flags() \{
+    PROVIDER.lock().unwrap().reset_flags()
+}
diff --git a/tools/aconfig/tests/AconfigHostTest.java b/tools/aconfig/tests/AconfigHostTest.java
new file mode 100644
index 0000000..ea71b7e
--- /dev/null
+++ b/tools/aconfig/tests/AconfigHostTest.java
@@ -0,0 +1,88 @@
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+
+import com.android.aconfig.test.FakeFeatureFlagsImpl;
+import com.android.aconfig.test.FeatureFlags;
+import com.android.aconfig.test.FeatureFlagsImpl;
+import com.android.aconfig.test.Flags;
+
+@RunWith(JUnit4.class)
+public final class AconfigHostTest {
+    @Test
+    public void testThrowsExceptionIfFlagNotSet() {
+        assertThrows(NullPointerException.class, () -> Flags.disabledRo());
+        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();
+        assertThrows(IllegalArgumentException.class, () -> featureFlags.disabledRo());
+    }
+
+    @Test
+    public void testSetFlagInFakeFeatureFlagsImpl() {
+        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();
+        featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true);
+        assertTrue(featureFlags.enabledRw());
+        featureFlags.setFlag(Flags.FLAG_ENABLED_RW, false);
+        assertFalse(featureFlags.enabledRw());
+
+        //Set Flags
+        assertThrows(NullPointerException.class, () -> Flags.enabledRw());
+        Flags.setFeatureFlags(featureFlags);
+        featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true);
+        assertTrue(Flags.enabledRw());
+        Flags.unsetFeatureFlags();
+    }
+
+    @Test
+    public void testSetFlagWithRandomName() {
+        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();
+        assertThrows(IllegalArgumentException.class,
+            () -> featureFlags.setFlag("Randome_name", true));
+    }
+
+    @Test
+    public void testResetFlagsInFakeFeatureFlagsImpl() {
+        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();
+        featureFlags.setFlag(Flags.FLAG_ENABLED_RO, true);
+        assertTrue(featureFlags.enabledRo());
+        featureFlags.resetAll();
+        assertThrows(IllegalArgumentException.class, () -> featureFlags.enabledRo());
+
+        // Set value after reset
+        featureFlags.setFlag(Flags.FLAG_ENABLED_RO, false);
+        assertFalse(featureFlags.enabledRo());
+    }
+
+    @Test
+    public void testFlagsSetFeatureFlags() {
+        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();
+        featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true);
+        assertThrows(NullPointerException.class, () -> Flags.enabledRw());
+        Flags.setFeatureFlags(featureFlags);
+        assertTrue(Flags.enabledRw());
+        Flags.unsetFeatureFlags();
+    }
+
+    @Test
+    public void testFlagsUnsetFeatureFlags() {
+        FakeFeatureFlagsImpl featureFlags = new FakeFeatureFlagsImpl();
+        featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true);
+        assertThrows(NullPointerException.class, () -> Flags.enabledRw());
+        Flags.setFeatureFlags(featureFlags);
+        assertTrue(Flags.enabledRw());
+
+        Flags.unsetFeatureFlags();
+        assertThrows(NullPointerException.class, () -> Flags.enabledRw());
+    }
+
+    @Test
+    public void testFeatureFlagsImplNotImpl() {
+        FeatureFlags featureFlags = new FeatureFlagsImpl();
+        assertThrows(UnsupportedOperationException.class,
+            () -> featureFlags.enabledRw());
+    }
+}
diff --git a/tools/aconfig/tests/AconfigTest.java b/tools/aconfig/tests/AconfigTest.java
index 778a4c6..958b02e 100644
--- a/tools/aconfig/tests/AconfigTest.java
+++ b/tools/aconfig/tests/AconfigTest.java
@@ -1,37 +1,67 @@
+import static com.android.aconfig.test.Flags.FLAG_DISABLED_RO;
+import static com.android.aconfig.test.Flags.FLAG_DISABLED_RW;
+import static com.android.aconfig.test.Flags.FLAG_ENABLED_FIXED_RO;
+import static com.android.aconfig.test.Flags.FLAG_ENABLED_RO;
+import static com.android.aconfig.test.Flags.FLAG_ENABLED_RW;
 import static com.android.aconfig.test.Flags.disabledRo;
 import static com.android.aconfig.test.Flags.disabledRw;
+import static com.android.aconfig.test.Flags.enabledFixedRo;
 import static com.android.aconfig.test.Flags.enabledRo;
 import static com.android.aconfig.test.Flags.enabledRw;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import com.android.aconfig.test.FakeFeatureFlagsImpl;
+import com.android.aconfig.test.FeatureFlags;
+
 @RunWith(JUnit4.class)
 public final class AconfigTest {
     @Test
     public void testDisabledReadOnlyFlag() {
+        assertEquals("com.android.aconfig.test.disabled_ro", FLAG_DISABLED_RO);
         assertFalse(disabledRo());
     }
 
     @Test
     public void testEnabledReadOnlyFlag() {
+        assertEquals("com.android.aconfig.test.disabled_rw", FLAG_DISABLED_RW);
         // TODO: change to assertTrue(enabledRo()) when the build supports reading tests/*.values
         // (currently all flags are assigned the default READ_ONLY + DISABLED)
         assertFalse(enabledRo());
     }
 
     @Test
+    public void testEnabledFixedReadOnlyFlag() {
+        assertEquals("com.android.aconfig.test.enabled_fixed_ro", FLAG_ENABLED_FIXED_RO);
+        // TODO: change to assertTrue(enabledFixedRo()) when the build supports reading tests/*.values
+        // (currently all flags are assigned the default READ_ONLY + DISABLED)
+        assertFalse(enabledFixedRo());
+    }
+
+    @Test
     public void testDisabledReadWriteFlag() {
+        assertEquals("com.android.aconfig.test.enabled_ro", FLAG_ENABLED_RO);
         assertFalse(disabledRw());
     }
 
     @Test
     public void testEnabledReadWriteFlag() {
+        assertEquals("com.android.aconfig.test.enabled_rw", FLAG_ENABLED_RW);
         // TODO: change to assertTrue(enabledRw()) when the build supports reading tests/*.values
         // (currently all flags are assigned the default READ_ONLY + DISABLED)
         assertFalse(enabledRw());
     }
+
+    @Test
+    public void testFakeFeatureFlagsImplImpled() {
+        FakeFeatureFlagsImpl fakeFeatureFlags = new FakeFeatureFlagsImpl();
+        fakeFeatureFlags.setFlag(FLAG_ENABLED_RW, false);
+        assertFalse(fakeFeatureFlags.enabledRw());
+    }
 }
diff --git a/tools/aconfig/tests/aconfig_prod_mode_test.rs b/tools/aconfig/tests/aconfig_prod_mode_test.rs
new file mode 100644
index 0000000..708604b
--- /dev/null
+++ b/tools/aconfig/tests/aconfig_prod_mode_test.rs
@@ -0,0 +1,8 @@
+#[test]
+fn test_flags() {
+    assert!(!aconfig_test_rust_library::disabled_ro());
+    assert!(!aconfig_test_rust_library::disabled_rw());
+    // TODO: Fix template to not default both disabled and enabled to false
+    assert!(!aconfig_test_rust_library::enabled_ro());
+    assert!(!aconfig_test_rust_library::enabled_rw());
+}
diff --git a/tools/aconfig/tests/aconfig_test.cpp b/tools/aconfig/tests/aconfig_test.cpp
new file mode 100644
index 0000000..10de347
--- /dev/null
+++ b/tools/aconfig/tests/aconfig_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "com_android_aconfig_test.h"
+#include "gtest/gtest.h"
+
+TEST(AconfigTest, TestDisabledReadOnlyFlag) {
+  ASSERT_FALSE(com_android_aconfig_test_disabled_ro());
+}
+
+TEST(AconfigTest, TestEnabledReadOnlyFlag) {
+  // TODO: change to assertTrue(enabledRo()) when the build supports reading tests/*.values
+  // (currently all flags are assigned the default READ_ONLY + DISABLED)
+  ASSERT_FALSE(com_android_aconfig_test_enabled_ro());
+}
+
+TEST(AconfigTest, TestDisabledReadWriteFlag) {
+  ASSERT_FALSE(com_android_aconfig_test_disabled_rw());
+}
+
+TEST(AconfigTest, TestEnabledReadWriteFlag) {
+  // TODO: change to assertTrue(enabledRo()) when the build supports reading tests/*.values
+  // (currently all flags are assigned the default READ_ONLY + DISABLED)
+  ASSERT_FALSE(com_android_aconfig_test_enabled_rw());
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/tools/aconfig/tests/aconfig_test_mode_test.rs b/tools/aconfig/tests/aconfig_test_mode_test.rs
new file mode 100644
index 0000000..7d40a44
--- /dev/null
+++ b/tools/aconfig/tests/aconfig_test_mode_test.rs
@@ -0,0 +1,23 @@
+#[test]
+fn test_flags() {
+    assert!(!aconfig_test_rust_library::disabled_ro());
+    assert!(!aconfig_test_rust_library::disabled_rw());
+    // TODO: Fix template to not default both disabled and enabled to false
+    assert!(!aconfig_test_rust_library::enabled_ro());
+    assert!(!aconfig_test_rust_library::enabled_rw());
+
+    aconfig_test_rust_library::set_disabled_ro(true);
+    assert!(aconfig_test_rust_library::disabled_ro());
+    aconfig_test_rust_library::set_disabled_rw(true);
+    assert!(aconfig_test_rust_library::disabled_rw());
+    aconfig_test_rust_library::set_enabled_ro(true);
+    assert!(aconfig_test_rust_library::enabled_ro());
+    aconfig_test_rust_library::set_enabled_rw(true);
+    assert!(aconfig_test_rust_library::enabled_rw());
+
+    aconfig_test_rust_library::reset_flags();
+    assert!(!aconfig_test_rust_library::disabled_ro());
+    assert!(!aconfig_test_rust_library::disabled_rw());
+    assert!(!aconfig_test_rust_library::enabled_ro());
+    assert!(!aconfig_test_rust_library::enabled_rw());
+}
diff --git a/tools/aconfig/tests/first.values b/tools/aconfig/tests/first.values
index e524404..a450f78 100644
--- a/tools/aconfig/tests/first.values
+++ b/tools/aconfig/tests/first.values
@@ -16,3 +16,9 @@
     state: ENABLED
     permission: READ_WRITE
 }
+flag_value {
+    package: "com.android.aconfig.test"
+    name: "enabled_fixed_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
diff --git a/tools/aconfig/tests/test.aconfig b/tools/aconfig/tests/test.aconfig
index a8f6652..aaa6df5 100644
--- a/tools/aconfig/tests/test.aconfig
+++ b/tools/aconfig/tests/test.aconfig
@@ -2,6 +2,28 @@
 
 # This flag's final value is calculated from:
 # - test.aconfig: DISABLED + READ_WRITE (default)
+# - first.values: DISABLED + READ_WRITE
+# - second.values: ENABLED + READ_ONLY
+flag {
+    name: "enabled_ro"
+    namespace: "aconfig_test"
+    description: "This flag is ENABLED + READ_ONLY"
+    bug: "abc"
+}
+
+# This flag's final value is calculated from:
+# - test.aconfig: DISABLED + READ_WRITE (default)
+# - first.values: ENABLED + READ_WRITE
+flag {
+    name: "enabled_rw"
+    namespace: "aconfig_test"
+    description: "This flag is ENABLED + READ_WRITE"
+    # for bug fields, the empty string is a discouraged but valid value
+    bug: ""
+}
+
+# This flag's final value is calculated from:
+# - test.aconfig: DISABLED + READ_WRITE (default)
 # - first.values: DISABLED + READ_ONLY
 flag {
     name: "disabled_ro"
@@ -19,24 +41,13 @@
     bug: "456"
 }
 
-# This flag's final value is calculated from:
-# - test.aconfig: DISABLED + READ_WRITE (default)
-# - first.values: DISABLED + READ_WRITE
-# - second.values: ENABLED + READ_ONLY
+# This flag's final value calculated from:
+# - test.aconfig: DISABLED + READ_ONLY
+# - first.values: ENABLED + READ_ONLY
 flag {
-    name: "enabled_ro"
+    name: "enabled_fixed_ro"
     namespace: "aconfig_test"
-    description: "This flag is ENABLED + READ_ONLY"
-    bug: "789"
-    bug: "abc"
-}
-
-# This flag's final value is calculated from:
-# - test.aconfig: DISABLED + READ_WRITE (default)
-# - first.values: ENABLED + READ_WRITE
-flag {
-    name: "enabled_rw"
-    namespace: "aconfig_test"
-    description: "This flag is ENABLED + READ_WRITE"
-    # no bug field: bug is not mandatory
+    description: "This flag is fixed READ_ONLY + ENABLED"
+    bug: ""
+    is_fixed_read_only: true
 }
diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh
index c2e36df..0ed9453 100755
--- a/tools/buildinfo.sh
+++ b/tools/buildinfo.sh
@@ -7,9 +7,9 @@
 if [ "$BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT" = "true" ] ; then
   echo "ro.build.legacy.id=$BUILD_ID"
 else
-  echo "ro.build.id=$BUILD_ID"
+  echo "ro.build.id?=$BUILD_ID"
 fi
-echo "ro.build.display.id=$BUILD_DISPLAY_ID"
+echo "ro.build.display.id?=$BUILD_DISPLAY_ID"
 echo "ro.build.version.incremental=$BUILD_NUMBER"
 echo "ro.build.version.sdk=$PLATFORM_SDK_VERSION"
 echo "ro.build.version.preview_sdk=$PLATFORM_PREVIEW_SDK_VERSION"
@@ -28,7 +28,9 @@
 echo "ro.build.type=$TARGET_BUILD_TYPE"
 echo "ro.build.user=$BUILD_USERNAME"
 echo "ro.build.host=$BUILD_HOSTNAME"
-echo "ro.build.tags=$BUILD_VERSION_TAGS"
+# TODO: Remove any tag-related optional property declarations once the goals
+# from go/arc-android-sigprop-changes have been achieved.
+echo "ro.build.tags?=$BUILD_VERSION_TAGS"
 echo "ro.build.flavor=$TARGET_BUILD_FLAVOR"
 
 # These values are deprecated, use "ro.product.cpu.abilist"
@@ -49,7 +51,7 @@
 echo "ro.build.product=$TARGET_DEVICE"
 
 echo "# Do not try to parse description or thumbprint"
-echo "ro.build.description=$PRIVATE_BUILD_DESC"
+echo "ro.build.description?=$PRIVATE_BUILD_DESC"
 if [ -n "$BUILD_THUMBPRINT" ] ; then
   echo "ro.build.thumbprint=$BUILD_THUMBPRINT"
 fi
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index 7a2dcb7..bd347a1 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -165,10 +165,10 @@
         "ota_utils_lib",
     ],
     required: [
+        "apexd_host",
         "brillo_update_payload",
         "checkvintf",
         "generate_gki_certificate",
-        "minigzip",
         "lz4",
         "toybox",
         "unpack_bootimg",
@@ -244,7 +244,6 @@
         "bsdiff",
         "generate_gki_certificate",
         "imgdiff",
-        "minigzip",
         "lz4",
         "mkbootfs",
         "signapk",
@@ -310,7 +309,6 @@
         "deapexer",
         "generate_gki_certificate",
         "imgdiff",
-        "minigzip",
         "lz4",
         "mkbootfs",
         "signapk",
@@ -347,6 +345,7 @@
     },
     srcs: [
         "merge_ota.py",
+        "ota_signing_utils.py",
     ],
     libs: [
         "ota_metadata_proto",
@@ -496,6 +495,26 @@
 }
 
 python_binary_host {
+    name: "ota_from_raw_img",
+    srcs: [
+        "ota_from_raw_img.py",
+        "ota_signing_utils.py",
+    ],
+    main: "ota_from_raw_img.py",
+    defaults: [
+        "releasetools_binary_defaults",
+    ],
+    required: [
+        "delta_generator",
+    ],
+    libs: [
+        "ota_metadata_proto",
+        "releasetools_common",
+        "ota_utils_lib",
+    ],
+}
+
+python_binary_host {
     name: "ota_package_parser",
     defaults: ["releasetools_binary_defaults"],
     srcs: [
@@ -593,6 +612,7 @@
         "sign_target_files_apks.py",
         "validate_target_files.py",
         "merge_ota.py",
+        "ota_signing_utils.py",
         ":releasetools_merge_sources",
         ":releasetools_merge_tests",
 
@@ -624,6 +644,7 @@
         },
     },
     required: [
+        "apexd_host",
         "deapexer",
     ],
 }
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 465d222..31f8736 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -517,12 +517,14 @@
   return img.name
 
 
-def AddCustomImages(output_zip, partition_name):
-  """Adds and signs custom images in IMAGES/.
+def AddCustomImages(output_zip, partition_name, image_list):
+  """Adds and signs avb custom images as needed in IMAGES/.
 
   Args:
     output_zip: The output zip file (needs to be already open), or None to
         write images to OPTIONS.input_tmp/.
+    partition_name: The custom image partition name.
+    image_list: The image list of the custom image partition.
 
   Uses the image under IMAGES/ if it already exists. Otherwise looks for the
   image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
@@ -531,19 +533,20 @@
     AssertionError: If image can't be found.
   """
 
+  builder = None
   key_path = OPTIONS.info_dict.get("avb_{}_key_path".format(partition_name))
-  algorithm = OPTIONS.info_dict.get("avb_{}_algorithm".format(partition_name))
-  extra_args = OPTIONS.info_dict.get(
-      "avb_{}_add_hashtree_footer_args".format(partition_name))
-  partition_size = OPTIONS.info_dict.get(
-      "avb_{}_partition_size".format(partition_name))
+  if key_path is not None:
+    algorithm = OPTIONS.info_dict.get("avb_{}_algorithm".format(partition_name))
+    extra_args = OPTIONS.info_dict.get(
+        "avb_{}_add_hashtree_footer_args".format(partition_name))
+    partition_size = OPTIONS.info_dict.get(
+        "avb_{}_partition_size".format(partition_name))
 
-  builder = verity_utils.CreateCustomImageBuilder(
-      OPTIONS.info_dict, partition_name, partition_size,
-      key_path, algorithm, extra_args)
+    builder = verity_utils.CreateCustomImageBuilder(
+        OPTIONS.info_dict, partition_name, partition_size,
+        key_path, algorithm, extra_args)
 
-  for img_name in OPTIONS.info_dict.get(
-          "avb_{}_image_list".format(partition_name)).split():
+  for img_name in image_list:
     custom_image = OutputFile(
         output_zip, OPTIONS.input_tmp, "IMAGES", img_name)
     if os.path.exists(custom_image.name):
@@ -684,34 +687,6 @@
   return img.name
 
 
-def AddPartitionTable(output_zip):
-  """Create a partition table image and store it in output_zip."""
-
-  img = OutputFile(
-      output_zip, OPTIONS.input_tmp, "IMAGES", "partition-table.img")
-  bpt = OutputFile(
-      output_zip, OPTIONS.input_tmp, "META", "partition-table.bpt")
-
-  # use BPTTOOL from environ, or "bpttool" if empty or not set.
-  bpttool = os.getenv("BPTTOOL") or "bpttool"
-  cmd = [bpttool, "make_table", "--output_json", bpt.name,
-         "--output_gpt", img.name]
-  input_files_str = OPTIONS.info_dict["board_bpt_input_files"]
-  input_files = input_files_str.split()
-  for i in input_files:
-    cmd.extend(["--input", i])
-  disk_size = OPTIONS.info_dict.get("board_bpt_disk_size")
-  if disk_size:
-    cmd.extend(["--disk_size", disk_size])
-  args = OPTIONS.info_dict.get("board_bpt_make_table_args")
-  if args:
-    cmd.extend(shlex.split(args))
-  common.RunAndCheckOutput(cmd)
-
-  img.Write()
-  bpt.Write()
-
-
 def AddCache(output_zip):
   """Create an empty cache image and store it in output_zip."""
 
@@ -1087,10 +1062,6 @@
     banner("cache")
     AddCache(output_zip)
 
-  if OPTIONS.info_dict.get("board_bpt_enable") == "true":
-    banner("partition-table")
-    AddPartitionTable(output_zip)
-
   add_partition("dtbo",
                 OPTIONS.info_dict.get("has_dtbo") == "true", AddDtbo, [])
   add_partition("pvmfw",
@@ -1098,18 +1069,29 @@
 
   # Custom images.
   custom_partitions = OPTIONS.info_dict.get(
-      "avb_custom_images_partition_list", "").strip().split()
+      "custom_images_partition_list", "").strip().split()
   for partition_name in custom_partitions:
     partition_name = partition_name.strip()
     banner("custom images for " + partition_name)
-    partitions[partition_name] = AddCustomImages(output_zip, partition_name)
+    image_list = OPTIONS.info_dict.get(
+          "{}_image_list".format(partition_name)).split()
+    partitions[partition_name] = AddCustomImages(output_zip, partition_name, image_list)
+
+  avb_custom_partitions = OPTIONS.info_dict.get(
+      "avb_custom_images_partition_list", "").strip().split()
+  for partition_name in avb_custom_partitions:
+    partition_name = partition_name.strip()
+    banner("avb custom images for " + partition_name)
+    image_list = OPTIONS.info_dict.get(
+          "avb_{}_image_list".format(partition_name)).split()
+    partitions[partition_name] = AddCustomImages(output_zip, partition_name, image_list)
 
   if OPTIONS.info_dict.get("avb_enable") == "true":
     # vbmeta_partitions includes the partitions that should be included into
     # top-level vbmeta.img, which are the ones that are not included in any
     # chained VBMeta image plus the chained VBMeta images themselves.
-    # Currently custom_partitions are all chained to VBMeta image.
-    vbmeta_partitions = common.AVB_PARTITIONS[:] + tuple(custom_partitions)
+    # Currently avb_custom_partitions are all chained to VBMeta image.
+    vbmeta_partitions = common.AVB_PARTITIONS[:] + tuple(avb_custom_partitions)
 
     vbmeta_system = OPTIONS.info_dict.get("avb_vbmeta_system", "").strip()
     if vbmeta_system:
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 11bd784..8c6d597 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -683,7 +683,9 @@
   d = {}
   TryParseFingerprint(glob_dict)
 
-  d["timestamp"] = FIXED_FILE_TIMESTAMP
+  # Set fixed timestamp for building the OTA package.
+  if "use_fixed_timestamp" in glob_dict:
+    d["timestamp"] = FIXED_FILE_TIMESTAMP
   if "build.prop" in glob_dict:
     timestamp = glob_dict["build.prop"].GetProp("ro.build.date.utc")
     if timestamp:
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 091121f..ec53a47 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -96,7 +96,6 @@
     self.cache_size = None
     self.stash_threshold = 0.8
     self.logfile = None
-    self.host_tools = {}
     self.sepolicy_name = 'sepolicy.apex'
 
 
@@ -225,23 +224,15 @@
   logging.config.dictConfig(config)
 
 
-def SetHostToolLocation(tool_name, location):
-  OPTIONS.host_tools[tool_name] = location
-
-
 def FindHostToolPath(tool_name):
   """Finds the path to the host tool.
 
   Args:
     tool_name: name of the tool to find
   Returns:
-    path to the tool if found under either one of the host_tools map or under
-    the same directory as this binary is located at. If not found, tool_name
-    is returned.
+    path to the tool if found under the same directory as this binary is located at. If not found,
+    tool_name is returned.
   """
-  if tool_name in OPTIONS.host_tools:
-    return OPTIONS.host_tools[tool_name]
-
   my_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
   tool_path = os.path.join(my_dir, tool_name)
   if os.path.exists(tool_path):
@@ -459,6 +450,10 @@
     return system_prop and system_prop.GetProp("ro.build.version.release") == "11"
 
   @property
+  def vabc_compression_param(self):
+    return self.get("virtual_ab_compression_method", "")
+
+  @property
   def vendor_api_level(self):
     vendor_prop = self.info_dict.get("vendor.build.prop")
     if not vendor_prop:
@@ -985,7 +980,7 @@
         each of the variables.
     ramdisk_format: If name is "boot", the format of ramdisk inside the
         boot image. Otherwise, its value is ignored.
-        Use lz4 to decompress by default. If its value is gzip, use minigzip.
+        Use lz4 to decompress by default. If its value is gzip, use gzip.
   """
 
   def __init__(self, input_file, name, placeholder_values=None):
@@ -1429,7 +1424,7 @@
       if os.path.exists(new_path):
         return new_path
     raise ExternalError(
-        "Failed to find {}".format(new_path))
+        "Failed to find {}".format(path))
 
   if not split_args:
     return split_args
@@ -1638,9 +1633,9 @@
     p2 = Run(["lz4", "-l", "-12", "--favor-decSpeed"], stdin=p1.stdout,
              stdout=ramdisk_img.file.fileno())
   elif ramdisk_format == RamdiskFormat.GZ:
-    p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
+    p2 = Run(["gzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
   else:
-    raise ValueError("Only support lz4 or minigzip ramdisk format.")
+    raise ValueError("Only support lz4 or gzip ramdisk format.")
 
   p2.wait()
   p1.wait()
@@ -2120,20 +2115,33 @@
   # According to https://stackoverflow.com/questions/434641/how-do-i-set-permissions-attributes-on-a-file-in-a-zip-file-using-pythons-zip/6297838#6297838
   # higher bits of |external_attr| are unix file permission and types
   unix_filetype = info.external_attr >> 16
+  file_perm = unix_filetype & 0o777
 
   def CheckMask(a, mask):
     return (a & mask) == mask
 
   def IsSymlink(a):
     return CheckMask(a, stat.S_IFLNK)
+
+  def IsDir(a):
+    return CheckMask(a, stat.S_IFDIR)
   # python3.11 zipfile implementation doesn't handle symlink correctly
   if not IsSymlink(unix_filetype):
-    return input_zip.extract(info, dirname)
+    target = input_zip.extract(info, dirname)
+    # We want to ensure that the file is at least read/writable by owner and readable by all users
+    if IsDir(unix_filetype):
+      os.chmod(target, file_perm | 0o755)
+    else:
+      os.chmod(target, file_perm | 0o644)
+    return target
   if dirname is None:
     dirname = os.getcwd()
   target = os.path.join(dirname, info.filename)
   os.makedirs(os.path.dirname(target), exist_ok=True)
+  if os.path.exists(target):
+    os.unlink(target)
   os.symlink(input_zip.read(info).decode(), target)
+  return target
 
 
 def UnzipToDir(filename, dirname, patterns=None):
@@ -2425,8 +2433,9 @@
             apk_name, proc.returncode, stdoutdata, stderrdata))
 
   for line in stdoutdata.split("\n"):
-    # Looking for lines such as sdkVersion:'23' or sdkVersion:'M'.
-    m = re.match(r'sdkVersion:\'([^\']*)\'', line)
+    # Due to ag/24161708, looking for lines such as minSdkVersion:'23',minSdkVersion:'M'
+    # or sdkVersion:'23', sdkVersion:'M'.
+    m = re.match(r'(?:minSdkVersion|sdkVersion):\'([^\']*)\'', line)
     if m:
       return m.group(1)
   raise ExternalError("No minSdkVersion returned by aapt2")
@@ -4075,7 +4084,7 @@
   Get build.prop from ramdisk within the boot image
 
   Args:
-    boot_img: the boot image file. Ramdisk must be compressed with lz4 or minigzip format.
+    boot_img: the boot image file. Ramdisk must be compressed with lz4 or gzip format.
 
   Return:
     An extracted file that stores properties in the boot image.
@@ -4094,11 +4103,11 @@
     elif ramdisk_format == RamdiskFormat.GZ:
       with open(ramdisk, 'rb') as input_stream:
         with open(uncompressed_ramdisk, 'wb') as output_stream:
-          p2 = Run(['minigzip', '-d'], stdin=input_stream.fileno(),
+          p2 = Run(['gzip', '-d'], stdin=input_stream.fileno(),
                    stdout=output_stream.fileno())
           p2.wait()
     else:
-      logger.error('Only support lz4 or minigzip ramdisk format.')
+      logger.error('Only support lz4 or gzip ramdisk format.')
       return None
 
     abs_uncompressed_ramdisk = os.path.abspath(uncompressed_ramdisk)
diff --git a/tools/releasetools/img_from_target_files.py b/tools/releasetools/img_from_target_files.py
index fa53ad2..5412b2a 100755
--- a/tools/releasetools/img_from_target_files.py
+++ b/tools/releasetools/img_from_target_files.py
@@ -65,6 +65,7 @@
 OPTIONS.build_super = None
 OPTIONS.sparse_userimages = None
 OPTIONS.use_fastboot_info = False
+OPTIONS.build_super_image = None
 
 def LoadOptions(input_file):
   """Loads information from input_file to OPTIONS.
@@ -174,7 +175,13 @@
   input_tmp = common.UnzipTemp(input_file)
 
   super_file = common.MakeTempFile('super_', '.img')
-  BuildSuperImage(input_tmp, super_file)
+
+  # Allow overriding the BUILD_SUPER_IMAGE binary
+  if OPTIONS.build_super_image:
+    command = [OPTIONS.build_super_image, input_tmp, super_file]
+    common.RunAndCheckOutput(command)
+  else:
+    BuildSuperImage(input_tmp, super_file)
 
   logger.info('Writing super.img to archive...')
   with zipfile.ZipFile(
@@ -231,6 +238,8 @@
       OPTIONS.bootable_only = True
     elif o == '--additional':
       OPTIONS.additional_entries.append(a)
+    elif o == '--build_super_image':
+      OPTIONS.build_super_image = a
     else:
       return False
     return True
@@ -240,6 +249,7 @@
                              extra_long_opts=[
                                  'additional=',
                                  'bootable_zip',
+                                 'build_super_image=',
                              ],
                              extra_option_handler=option_handler)
   if len(args) != 2:
diff --git a/tools/releasetools/merge/merge_meta.py b/tools/releasetools/merge/merge_meta.py
index b61f039..81c3510 100644
--- a/tools/releasetools/merge/merge_meta.py
+++ b/tools/releasetools/merge/merge_meta.py
@@ -124,10 +124,10 @@
         merged_meta_dir=merged_meta_dir,
         file_name=file_name)
 
-  MergeUpdateEngineConfig(
-      framework_meta_dir,
-      vendor_meta_dir, merged_meta_dir,
-  )
+  if OPTIONS.merged_misc_info.get('ab_update') == 'true':
+    MergeUpdateEngineConfig(
+        framework_meta_dir,
+        vendor_meta_dir, merged_meta_dir)
 
   # Write the now-finalized OPTIONS.merged_misc_info.
   merge_utils.WriteSortedData(
diff --git a/tools/releasetools/merge/merge_utils.py b/tools/releasetools/merge/merge_utils.py
index b5683a8..d446fc0 100644
--- a/tools/releasetools/merge/merge_utils.py
+++ b/tools/releasetools/merge/merge_utils.py
@@ -217,7 +217,7 @@
 
 # Partitions that are grabbed from the framework partial build by default.
 _FRAMEWORK_PARTITIONS = {
-    'system', 'product', 'system_ext', 'system_other', 'root', 'system_dlkm',
+    'system', 'product', 'system_ext', 'system_other', 'root',
     'vbmeta_system', 'pvmfw'
 }
 
diff --git a/tools/releasetools/merge_ota.py b/tools/releasetools/merge_ota.py
index 441312c..24d9ea9 100644
--- a/tools/releasetools/merge_ota.py
+++ b/tools/releasetools/merge_ota.py
@@ -14,7 +14,6 @@
 
 import argparse
 import logging
-import shlex
 import struct
 import sys
 import update_payload
@@ -31,6 +30,7 @@
 
 from payload_signer import PayloadSigner
 from ota_utils import PayloadGenerator, METADATA_PROTO_NAME, FinalizeMetadata
+from ota_signing_utils import AddSigningArgumentParse
 
 logger = logging.getLogger(__name__)
 
@@ -126,7 +126,7 @@
     ExtendPartitionUpdates(output_manifest.partitions, manifest.partitions)
     try:
       MergeDynamicPartitionMetadata(
-        output_manifest.dynamic_partition_metadata, manifest.dynamic_partition_metadata)
+          output_manifest.dynamic_partition_metadata, manifest.dynamic_partition_metadata)
     except DuplicatePartitionError:
       logger.error(
           "OTA %s has duplicate partition with some of the previous OTAs", payload.name)
@@ -190,6 +190,7 @@
               f"OTA {partition_to_ota[part].name} and {payload.name} have duplicating partition {part}")
         partition_to_ota[part] = payload
 
+
 def ApexInfo(file_paths):
   if len(file_paths) > 1:
     logger.info("More than one target file specified, will ignore "
@@ -201,33 +202,19 @@
       return apex_info_bytes
   return None
 
-def ParseSignerArgs(args):
-  if args is None:
-    return None
-  return shlex.split(args)
 
 def main(argv):
   parser = argparse.ArgumentParser(description='Merge multiple partial OTAs')
   parser.add_argument('packages', type=str, nargs='+',
                       help='Paths to OTA packages to merge')
-  parser.add_argument('--package_key', type=str,
-                      help='Paths to private key for signing payload')
-  parser.add_argument('--search_path', type=str,
-                      help='Search path for framework/signapk.jar')
-  parser.add_argument('--payload_signer', type=str,
-                      help='Path to custom payload signer')
-  parser.add_argument('--payload_signer_args', type=ParseSignerArgs,
-                      help='Arguments for payload signer if necessary')
-  parser.add_argument('--payload_signer_maximum_signature_size', type=str,
-                      help='Maximum signature size (in bytes) that would be '
-                      'generated by the given payload signer')
   parser.add_argument('--output', type=str,
                       help='Paths to output merged ota', required=True)
   parser.add_argument('--metadata_ota', type=str,
                       help='Output zip will use build metadata from this OTA package, if unspecified, use the last OTA package in merge list')
-  parser.add_argument('--private_key_suffix', type=str,
-                      help='Suffix to be appended to package_key path', default=".pk8")
-  parser.add_argument('-v', action="store_true", help="Enable verbose logging", dest="verbose")
+  parser.add_argument('-v', action="store_true",
+                      help="Enable verbose logging", dest="verbose")
+  AddSigningArgumentParse(parser)
+
   parser.epilog = ('This tool can also be used to resign a regular OTA. For a single regular OTA, '
                    'apex_info.pb will be written to output. When merging multiple OTAs, '
                    'apex_info.pb will not be written.')
@@ -301,8 +288,6 @@
   return 0
 
 
-
-
 if __name__ == '__main__':
   logging.basicConfig()
   sys.exit(main(sys.argv))
diff --git a/tools/releasetools/ota_from_raw_img.py b/tools/releasetools/ota_from_raw_img.py
new file mode 100644
index 0000000..63f88ea
--- /dev/null
+++ b/tools/releasetools/ota_from_raw_img.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Given a series of .img files, produces an OTA package that installs thoese images
+"""
+
+import sys
+import os
+import argparse
+import subprocess
+import tempfile
+import logging
+import zipfile
+
+import common
+from payload_signer import PayloadSigner
+from ota_utils import PayloadGenerator
+from ota_signing_utils import AddSigningArgumentParse
+
+
+logger = logging.getLogger(__name__)
+
+
+def ResolveBinaryPath(filename, search_path):
+  if not search_path:
+    return filename
+  if not os.path.exists(search_path):
+    return filename
+  path = os.path.join(search_path, "bin", filename)
+  if os.path.exists(path):
+    return path
+  path = os.path.join(search_path, filename)
+  if os.path.exists(path):
+    return path
+  return path
+
+
+def main(argv):
+  parser = argparse.ArgumentParser(
+      prog=argv[0], description="Given a series of .img files, produces a full OTA package that installs thoese images")
+  parser.add_argument("images", nargs="+", type=str,
+                      help="List of images to generate OTA")
+  parser.add_argument("--partition_names", nargs='+', type=str,
+                      help="Partition names to install the images, default to basename of the image(no file name extension)")
+  parser.add_argument('--output', type=str,
+                      help='Paths to output merged ota', required=True)
+  parser.add_argument('--max_timestamp', type=int,
+                      help='Maximum build timestamp allowed to install this OTA')
+  parser.add_argument("-v", action="store_true",
+                      help="Enable verbose logging", dest="verbose")
+  AddSigningArgumentParse(parser)
+
+  args = parser.parse_args(argv[1:])
+  if args.verbose:
+    logger.setLevel(logging.INFO)
+  logger.info(args)
+  if not args.partition_names:
+    args.partition_names = [os.path.os.path.splitext(os.path.basename(path))[
+        0] for path in args.images]
+  with tempfile.NamedTemporaryFile() as unsigned_payload, tempfile.NamedTemporaryFile() as dynamic_partition_info_file:
+    dynamic_partition_info_file.writelines(
+        [b"virtual_ab=true\n", b"super_partition_groups=\n"])
+    dynamic_partition_info_file.flush()
+    cmd = [ResolveBinaryPath("delta_generator", args.search_path)]
+    cmd.append("--partition_names=" + ",".join(args.partition_names))
+    cmd.append("--dynamic_partition_info_file=" +
+               dynamic_partition_info_file.name)
+    cmd.append("--new_partitions=" + ",".join(args.images))
+    cmd.append("--out_file=" + unsigned_payload.name)
+    cmd.append("--is_partial_update")
+    if args.max_timestamp:
+      cmd.append("--max_timestamp=" + str(args.max_timestamp))
+    logger.info("Running %s", cmd)
+
+    subprocess.check_call(cmd)
+    generator = PayloadGenerator()
+    generator.payload_file = unsigned_payload.name
+    logger.info("Payload size: %d", os.path.getsize(generator.payload_file))
+
+    # Get signing keys
+    key_passwords = common.GetKeyPasswords([args.package_key])
+
+    if args.package_key:
+      logger.info("Signing payload...")
+      # TODO: remove OPTIONS when no longer used as fallback in payload_signer
+      common.OPTIONS.payload_signer_args = None
+      common.OPTIONS.payload_signer_maximum_signature_size = None
+      signer = PayloadSigner(args.package_key, args.private_key_suffix,
+                             key_passwords[args.package_key],
+                             payload_signer=args.payload_signer,
+                             payload_signer_args=args.payload_signer_args,
+                             payload_signer_maximum_signature_size=args.payload_signer_maximum_signature_size)
+      generator.payload_file = unsigned_payload.name
+      generator.Sign(signer)
+
+    logger.info("Payload size: %d", os.path.getsize(generator.payload_file))
+
+    logger.info("Writing to %s", args.output)
+    with zipfile.ZipFile(args.output, "w") as zfp:
+      generator.WriteToZip(zfp)
+
+
+if __name__ == "__main__":
+  logging.basicConfig()
+  main(sys.argv)
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index f3e6f1e..56ec929 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -147,7 +147,7 @@
 A/B OTA specific options
 
   --disable_fec_computation
-      Disable the on device FEC data computation for incremental updates.
+      Disable the on device FEC data computation for incremental updates. OTA will be larger but installation will be faster.
 
   --include_secondary
       Additionally include the payload for secondary slot images (default:
@@ -224,7 +224,7 @@
       wait time in recovery.
 
   --enable_vabc_xor
-      Enable the VABC xor feature. Will reduce space requirements for OTA
+      Enable the VABC xor feature. Will reduce space requirements for OTA, but OTA installation will be slower.
 
   --force_minor_version
       Override the update_engine minor version for delta generation.
@@ -233,7 +233,10 @@
       A colon ':' separated list of compressors. Allowed values are bz2 and brotli.
 
   --enable_zucchini
-      Whether to enable to zucchini feature. Will generate smaller OTA but uses more memory.
+      Whether to enable to zucchini feature. Will generate smaller OTA but uses more memory, OTA generation will take longer.
+
+  --enable_puffdiff
+      Whether to enable to puffdiff feature. Will generate smaller OTA but uses more memory, OTA generation will take longer.
 
   --enable_lz4diff
       Whether to enable lz4diff feature. Will generate smaller OTA for EROFS but
@@ -244,7 +247,9 @@
       older SPL.
 
   --vabc_compression_param
-      Compression algorithm to be used for VABC. Available options: gz, brotli, none
+      Compression algorithm to be used for VABC. Available options: gz, lz4, zstd, brotli, none. 
+      Compression level can be specified by appending ",$LEVEL" to option. 
+      e.g. --vabc_compression_param=gz,9 specifies level 9 compression with gz algorithm
 
   --security_patch_level
       Override the security patch level in target files
@@ -269,7 +274,7 @@
 import care_map_pb2
 import common
 import ota_utils
-from ota_utils import (UNZIP_PATTERN, FinalizeMetadata, GetPackageMetadata,
+from ota_utils import (VABC_COMPRESSION_PARAM_SUPPORT, FinalizeMetadata, GetPackageMetadata,
                        PayloadGenerator, SECURITY_PATCH_LEVEL_PROP_NAME, ExtractTargetFiles, CopyTargetFilesDir)
 from common import DoesInputFileContain, IsSparseImage
 import target_files_diff
@@ -320,6 +325,7 @@
 OPTIONS.force_minor_version = None
 OPTIONS.compressor_types = None
 OPTIONS.enable_zucchini = True
+OPTIONS.enable_puffdiff = None
 OPTIONS.enable_lz4diff = False
 OPTIONS.vabc_compression_param = None
 OPTIONS.security_patch_level = None
@@ -456,48 +462,51 @@
   target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
   target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
 
-  with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
-    infolist = input_zip.infolist()
+  fileslist = []
+  for (root, dirs, files) in os.walk(input_file):
+    root = root.lstrip(input_file).lstrip("/")
+    fileslist.extend([os.path.join(root, d) for d in dirs])
+    fileslist.extend([os.path.join(root, d) for d in files])
 
-  input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
-  for info in infolist:
-    unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
-    if info.filename == 'IMAGES/system_other.img':
+  input_tmp = input_file
+  for filename in fileslist:
+    unzipped_file = os.path.join(input_tmp, *filename.split('/'))
+    if filename == 'IMAGES/system_other.img':
       common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
 
     # Primary images and friends need to be skipped explicitly.
-    elif info.filename in ('IMAGES/system.img',
-                           'IMAGES/system.map'):
+    elif filename in ('IMAGES/system.img',
+                      'IMAGES/system.map'):
       pass
 
     # Copy images that are not in SECONDARY_PAYLOAD_SKIPPED_IMAGES.
-    elif info.filename.startswith(('IMAGES/', 'RADIO/')):
-      image_name = os.path.basename(info.filename)
+    elif filename.startswith(('IMAGES/', 'RADIO/')):
+      image_name = os.path.basename(filename)
       if image_name not in ['{}.img'.format(partition) for partition in
                             SECONDARY_PAYLOAD_SKIPPED_IMAGES]:
-        common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
+        common.ZipWrite(target_zip, unzipped_file, arcname=filename)
 
     # Skip copying the postinstall config if requested.
-    elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
+    elif skip_postinstall and filename == POSTINSTALL_CONFIG:
       pass
 
-    elif info.filename.startswith('META/'):
+    elif filename.startswith('META/'):
       # Remove the unnecessary partitions for secondary images from the
       # ab_partitions file.
-      if info.filename == AB_PARTITIONS:
+      if filename == AB_PARTITIONS:
         with open(unzipped_file) as f:
           partition_list = f.read().splitlines()
         partition_list = [partition for partition in partition_list if partition
                           and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
-        common.ZipWriteStr(target_zip, info.filename,
+        common.ZipWriteStr(target_zip, filename,
                            '\n'.join(partition_list))
       # Remove the unnecessary partitions from the dynamic partitions list.
-      elif (info.filename == 'META/misc_info.txt' or
-            info.filename == DYNAMIC_PARTITION_INFO):
+      elif (filename == 'META/misc_info.txt' or
+            filename == DYNAMIC_PARTITION_INFO):
         modified_info = GetInfoForSecondaryImages(unzipped_file)
-        common.ZipWriteStr(target_zip, info.filename, modified_info)
+        common.ZipWriteStr(target_zip, filename, modified_info)
       else:
-        common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
+        common.ZipWrite(target_zip, unzipped_file, arcname=filename)
 
   common.ZipClose(target_zip)
 
@@ -863,6 +872,10 @@
     if not source_info.is_vabc or not target_info.is_vabc:
       logger.info("Either source or target does not support VABC, disabling.")
       OPTIONS.disable_vabc = True
+    if source_info.vabc_compression_param != target_info.vabc_compression_param:
+      logger.info("Source build and target build use different compression methods {} vs {}, default to source builds parameter {}".format(
+          source_info.vabc_compression_param, target_info.vabc_compression_param, source_info.vabc_compression_param))
+      OPTIONS.vabc_compression_param = source_info.vabc_compression_param
 
     # Virtual AB Compression was introduced in Androd S.
     # Later, we backported VABC to Android R. But verity support was not
@@ -877,6 +890,22 @@
         "META/ab_partitions.txt is required for ab_update."
     target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
     source_info = None
+    if target_info.vabc_compression_param:
+      minimum_api_level_required = VABC_COMPRESSION_PARAM_SUPPORT[
+          target_info.vabc_compression_param]
+      if target_info.vendor_api_level < minimum_api_level_required:
+        logger.warning(
+            "This full OTA is configured to use VABC compression algorithm"
+            " {}, which is supported since"
+            " Android API level {}, but device is "
+            "launched with {} . If this full OTA is"
+            " served to a device running old build, OTA might fail due to "
+            "unsupported compression parameter. For safety, gz is used because "
+            "it's supported since day 1.".format(
+                target_info.vabc_compression_param,
+                minimum_api_level_required,
+                target_info.vendor_api_level))
+        OPTIONS.vabc_compression_param = "gz"
 
   if OPTIONS.partial == []:
     logger.info(
@@ -994,6 +1023,9 @@
 
   additional_args += ["--enable_zucchini=" +
                       str(OPTIONS.enable_zucchini).lower()]
+  if OPTIONS.enable_puffdiff is not None:
+    additional_args += ["--enable_puffdiff=" +
+                        str(OPTIONS.enable_puffdiff).lower()]
 
   if not ota_utils.IsLz4diffCompatible(source_file, target_file):
     logger.warning(
@@ -1193,11 +1225,20 @@
     elif o == "--enable_zucchini":
       assert a.lower() in ["true", "false"]
       OPTIONS.enable_zucchini = a.lower() != "false"
+    elif o == "--enable_puffdiff":
+      assert a.lower() in ["true", "false"]
+      OPTIONS.enable_puffdiff = a.lower() != "false"
     elif o == "--enable_lz4diff":
       assert a.lower() in ["true", "false"]
       OPTIONS.enable_lz4diff = a.lower() != "false"
     elif o == "--vabc_compression_param":
+      words = a.split(",")
+      assert len(words) >= 1 and len(words) <= 2
       OPTIONS.vabc_compression_param = a.lower()
+      if len(words) == 2:
+        if not words[1].isdigit():
+          raise ValueError("Cannot parse value %r for option $COMPRESSION_LEVEL - only "
+                         "integers are allowed." % words[1])
     elif o == "--security_patch_level":
       OPTIONS.security_patch_level = a
     elif o in ("--max_threads"):
@@ -1254,6 +1295,7 @@
                                  "force_minor_version=",
                                  "compressor_types=",
                                  "enable_zucchini=",
+                                 "enable_puffdiff=",
                                  "enable_lz4diff=",
                                  "vabc_compression_param=",
                                  "security_patch_level=",
diff --git a/tools/releasetools/ota_signing_utils.py b/tools/releasetools/ota_signing_utils.py
new file mode 100644
index 0000000..60c8c94
--- /dev/null
+++ b/tools/releasetools/ota_signing_utils.py
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import shlex
+
+
+def ParseSignerArgs(args):
+  if args is None:
+    return None
+  return shlex.split(args)
+
+
+def AddSigningArgumentParse(parser: argparse.ArgumentParser):
+  parser.add_argument('--package_key', type=str,
+                      help='Paths to private key for signing payload')
+  parser.add_argument('--search_path', '--path', type=str,
+                      help='Search path for framework/signapk.jar')
+  parser.add_argument('--payload_signer', type=str,
+                      help='Path to custom payload signer')
+  parser.add_argument('--payload_signer_args', type=ParseSignerArgs,
+                      help='Arguments for payload signer if necessary')
+  parser.add_argument('--payload_signer_maximum_signature_size', type=str,
+                      help='Maximum signature size (in bytes) that would be '
+                      'generated by the given payload signer')
+  parser.add_argument('--private_key_suffix', type=str,
+                      help='Suffix to be appended to package_key path', default=".pk8")
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index 68c6887..9b3367e 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -51,6 +51,19 @@
 TARGET_FILES_IMAGES_SUBDIR = ["IMAGES", "PREBUILT_IMAGES", "RADIO"]
 
 
+# Key is the compression algorithm, value is minimum API level required to
+# use this compression algorithm for VABC OTA on device.
+VABC_COMPRESSION_PARAM_SUPPORT = {
+    "gz": 31,
+    "brotli": 31,
+    "none": 31,
+    # lz4 support is added in Android U
+    "lz4": 34,
+    # zstd support is added in Android V
+    "zstd": 35,
+}
+
+
 def FinalizeMetadata(metadata, input_file, output_file, needed_property_files=None, package_key=None, pw=None):
   """Finalizes the metadata and signs an A/B OTA package.
 
@@ -727,6 +740,7 @@
     logger.info("target files %s is already extracted", path)
     return path
   extracted_dir = common.MakeTempDir("target_files")
+  logger.info(f"Extracting target files {path} to {extracted_dir}")
   common.UnzipToDir(path, extracted_dir, UNZIP_PATTERN + [""])
   for subdir in TARGET_FILES_IMAGES_SUBDIR:
     image_dir = os.path.join(extracted_dir, subdir)
@@ -850,7 +864,7 @@
       cmd.extend(["--dynamic_partition_info_file", dynamic_partition_info])
 
     apex_info = os.path.join(
-      target_dir, "META", "apex_info.pb")
+        target_dir, "META", "apex_info.pb")
     if os.path.exists(apex_info):
       cmd.extend(["--apex_info_file", apex_info])
 
@@ -883,30 +897,7 @@
     """
     assert isinstance(payload_signer, PayloadSigner)
 
-    # 1. Generate hashes of the payload and metadata files.
-    payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
-    metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
-    cmd = ["brillo_update_payload", "hash",
-           "--unsigned_payload", self.payload_file,
-           "--signature_size", str(payload_signer.maximum_signature_size),
-           "--metadata_hash_file", metadata_sig_file,
-           "--payload_hash_file", payload_sig_file]
-    self._Run(cmd)
-
-    # 2. Sign the hashes.
-    signed_payload_sig_file = payload_signer.SignHashFile(payload_sig_file)
-    signed_metadata_sig_file = payload_signer.SignHashFile(metadata_sig_file)
-
-    # 3. Insert the signatures back into the payload file.
-    signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
-                                              suffix=".bin")
-    cmd = ["brillo_update_payload", "sign",
-           "--unsigned_payload", self.payload_file,
-           "--payload", signed_payload_file,
-           "--signature_size", str(payload_signer.maximum_signature_size),
-           "--metadata_signature_file", signed_metadata_sig_file,
-           "--payload_signature_file", signed_payload_sig_file]
-    self._Run(cmd)
+    signed_payload_file = payload_signer.SignPayload(self.payload_file)
 
     self.payload_file = signed_payload_file
 
@@ -920,9 +911,9 @@
     # 4. Dump the signed payload properties.
     properties_file = common.MakeTempFile(prefix="payload-properties-",
                                           suffix=".txt")
-    cmd = ["brillo_update_payload", "properties",
-           "--payload", self.payload_file,
-           "--properties_file", properties_file]
+    cmd = ["delta_generator",
+           "--in_file=" + self.payload_file,
+           "--properties_file=" + properties_file]
     self._Run(cmd)
 
     if self.secondary:
diff --git a/tools/releasetools/payload_signer.py b/tools/releasetools/payload_signer.py
index 9933aef..bbd2896 100644
--- a/tools/releasetools/payload_signer.py
+++ b/tools/releasetools/payload_signer.py
@@ -95,11 +95,11 @@
     # 1. Generate hashes of the payload and metadata files.
     payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
     metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
-    cmd = ["brillo_update_payload", "hash",
-           "--unsigned_payload", unsigned_payload,
-           "--signature_size", str(self.maximum_signature_size),
-           "--metadata_hash_file", metadata_sig_file,
-           "--payload_hash_file", payload_sig_file]
+    cmd = ["delta_generator",
+           "--in_file=" + unsigned_payload,
+           "--signature_size=" + str(self.maximum_signature_size),
+           "--out_metadata_hash_file=" + metadata_sig_file,
+           "--out_hash_file=" + payload_sig_file]
     self._Run(cmd)
 
     # 2. Sign the hashes.
@@ -109,16 +109,15 @@
     # 3. Insert the signatures back into the payload file.
     signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
                                               suffix=".bin")
-    cmd = ["brillo_update_payload", "sign",
-           "--unsigned_payload", unsigned_payload,
-           "--payload", signed_payload_file,
-           "--signature_size", str(self.maximum_signature_size),
-           "--metadata_signature_file", signed_metadata_sig_file,
-           "--payload_signature_file", signed_payload_sig_file]
+    cmd = ["delta_generator",
+           "--in_file=" + unsigned_payload,
+           "--out_file=" + signed_payload_file,
+           "--signature_size=" + str(self.maximum_signature_size),
+           "--metadata_signature_file=" + signed_metadata_sig_file,
+           "--payload_signature_file=" + signed_payload_sig_file]
     self._Run(cmd)
     return signed_payload_file
 
-
   def SignHashFile(self, in_file):
     """Signs the given input file. Returns the output filename."""
     out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 8291448..2b45825 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -1234,11 +1234,11 @@
     vendor_misc_info["no_recovery"] = "true"  # recovery
     vendor_misc_info["avb_enable"] = "false"  # vbmeta
 
-  vendor_misc_info["board_bpt_enable"] = "false"  # partition-table
   vendor_misc_info["has_dtbo"] = "false"  # dtbo
   vendor_misc_info["has_pvmfw"] = "false"  # pvmfw
-  vendor_misc_info["avb_custom_images_partition_list"] = ""  # custom images
+  vendor_misc_info["avb_custom_images_partition_list"] = ""  # avb custom images
   vendor_misc_info["avb_building_vbmeta_image"] = "false" # skip building vbmeta
+  vendor_misc_info["custom_images_partition_list"] = ""  # custom images
   vendor_misc_info["use_dynamic_partitions"] = "false"  # super_empty
   vendor_misc_info["build_super_partition"] = "false"  # super split
   vendor_misc_info["avb_vbmeta_system"] = ""  # skip building vbmeta_system
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index 86fb480..c69a13d 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -15,14 +15,13 @@
 #
 
 import copy
-import json
 import os
 import subprocess
 import tempfile
-import time
 import unittest
 import zipfile
 from hashlib import sha1
+from typing import BinaryIO
 
 import common
 import test_utils
@@ -36,14 +35,24 @@
 GiB = 1024 * MiB
 
 
-def get_2gb_string():
+def get_2gb_file():
   size = int(2 * GiB + 1)
   block_size = 4 * KiB
   step_size = 4 * MiB
-  # Generate a long string with holes, e.g. 'xyz\x00abc\x00...'.
+  tmpfile = tempfile.NamedTemporaryFile()
+  tmpfile.truncate(size)
   for _ in range(0, size, step_size):
-    yield os.urandom(block_size)
-    yield b'\0' * (step_size - block_size)
+    tmpfile.write(os.urandom(block_size))
+    tmpfile.seek(step_size - block_size, os.SEEK_CUR)
+  return tmpfile
+
+
+def hash_file(filename):
+  sha1_hash = sha1()
+  with open(filename, "rb") as fp:
+    for data in iter(lambda: fp.read(4*MiB), b''):
+      sha1_hash.update(data)
+  return sha1_hash
 
 
 class BuildInfoTest(test_utils.ReleaseToolsTestCase):
@@ -222,17 +231,17 @@
     info_dict = copy.deepcopy(self.TEST_INFO_FINGERPRINT_DICT)
     build_info = common.BuildInfo(info_dict)
     self.assertEqual(
-      'product-brand/product-name/product-device:version-release/build-id/'
-      'version-incremental:build-type/build-tags', build_info.fingerprint)
+        'product-brand/product-name/product-device:version-release/build-id/'
+        'version-incremental:build-type/build-tags', build_info.fingerprint)
 
     build_props = info_dict['build.prop'].build_props
     del build_props['ro.build.id']
     build_props['ro.build.legacy.id'] = 'legacy-build-id'
     build_info = common.BuildInfo(info_dict, use_legacy_id=True)
     self.assertEqual(
-      'product-brand/product-name/product-device:version-release/'
-      'legacy-build-id/version-incremental:build-type/build-tags',
-      build_info.fingerprint)
+        'product-brand/product-name/product-device:version-release/'
+        'legacy-build-id/version-incremental:build-type/build-tags',
+        build_info.fingerprint)
 
     self.assertRaises(common.ExternalError, common.BuildInfo, info_dict, None,
                       False)
@@ -241,9 +250,9 @@
     info_dict['vbmeta_digest'] = 'abcde12345'
     build_info = common.BuildInfo(info_dict, use_legacy_id=False)
     self.assertEqual(
-      'product-brand/product-name/product-device:version-release/'
-      'legacy-build-id.abcde123/version-incremental:build-type/build-tags',
-      build_info.fingerprint)
+        'product-brand/product-name/product-device:version-release/'
+        'legacy-build-id.abcde123/version-incremental:build-type/build-tags',
+        build_info.fingerprint)
 
   def test___getitem__(self):
     target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
@@ -376,7 +385,7 @@
     info_dict['build.prop'].build_props[
         'ro.product.property_source_order'] = 'bad-source'
     with self.assertRaisesRegexp(common.ExternalError,
-        'Invalid ro.product.property_source_order'):
+                                 'Invalid ro.product.property_source_order'):
       info = common.BuildInfo(info_dict, None)
       info.GetBuildProp('ro.product.device')
 
@@ -429,6 +438,13 @@
     self.assertIsNone(zip_file.testzip())
 
   def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
+    with tempfile.NamedTemporaryFile() as test_file:
+      test_file_name = test_file.name
+      for data in contents:
+        test_file.write(bytes(data))
+      return self._test_ZipWriteFile(test_file_name, extra_zipwrite_args)
+
+  def _test_ZipWriteFile(self, test_file_name, extra_zipwrite_args=None):
     extra_zipwrite_args = dict(extra_zipwrite_args or {})
 
     test_file = tempfile.NamedTemporaryFile(delete=False)
@@ -441,17 +457,12 @@
     arcname = extra_zipwrite_args.get("arcname", test_file_name)
     if arcname[0] == "/":
       arcname = arcname[1:]
+    sha1_hash = hash_file(test_file_name)
 
     zip_file.close()
     zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
 
     try:
-      sha1_hash = sha1()
-      for data in contents:
-        sha1_hash.update(bytes(data))
-        test_file.write(bytes(data))
-      test_file.close()
-
       expected_mode = extra_zipwrite_args.get("perms", 0o644)
       expected_compress_type = extra_zipwrite_args.get("compress_type",
                                                        zipfile.ZIP_STORED)
@@ -467,7 +478,6 @@
                    test_file_name, expected_stat, expected_mode,
                    expected_compress_type)
     finally:
-      os.remove(test_file_name)
       os.remove(zip_file_name)
 
   def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
@@ -502,14 +512,13 @@
     finally:
       os.remove(zip_file_name)
 
-  def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
+  def _test_ZipWriteStr_large_file(self, large_file: BinaryIO, small, extra_args=None):
     extra_args = dict(extra_args or {})
 
     zip_file = tempfile.NamedTemporaryFile(delete=False)
     zip_file_name = zip_file.name
 
-    test_file = tempfile.NamedTemporaryFile(delete=False)
-    test_file_name = test_file.name
+    test_file_name = large_file.name
 
     arcname_large = test_file_name
     arcname_small = "bar"
@@ -522,11 +531,7 @@
     zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
 
     try:
-      sha1_hash = sha1()
-      for data in large:
-        sha1_hash.update(data)
-        test_file.write(data)
-      test_file.close()
+      sha1_hash = hash_file(test_file_name)
 
       # Arbitrary timestamp, just to make sure common.ZipWrite() restores
       # the timestamp after writing.
@@ -551,7 +556,6 @@
                    expected_compress_type=expected_compress_type)
     finally:
       os.remove(zip_file_name)
-      os.remove(test_file_name)
 
   def _test_reset_ZIP64_LIMIT(self, func, *args):
     default_limit = (1 << 31) - 1
@@ -577,10 +581,10 @@
     })
 
   def test_ZipWrite_large_file(self):
-    file_contents = get_2gb_string()
-    self._test_ZipWrite(file_contents, {
-        "compress_type": zipfile.ZIP_DEFLATED,
-    })
+    with get_2gb_file() as tmpfile:
+      self._test_ZipWriteFile(tmpfile.name, {
+          "compress_type": zipfile.ZIP_DEFLATED,
+      })
 
   def test_ZipWrite_resets_ZIP64_LIMIT(self):
     self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
@@ -627,11 +631,11 @@
     # zipfile.writestr() doesn't work when the str size is over 2GiB even with
     # the workaround. We will only test the case of writing a string into a
     # large archive.
-    long_string = get_2gb_string()
     short_string = os.urandom(1024)
-    self._test_ZipWriteStr_large_file(long_string, short_string, {
-        "compress_type": zipfile.ZIP_DEFLATED,
-    })
+    with get_2gb_file() as large_file:
+      self._test_ZipWriteStr_large_file(large_file, short_string, {
+          "compress_type": zipfile.ZIP_DEFLATED,
+      })
 
   def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
     self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
@@ -821,9 +825,9 @@
   )
 
   APKCERTS_CERTMAP1 = {
-      'RecoveryLocalizer.apk' : 'certs/devkey',
-      'Settings.apk' : 'build/make/target/product/security/platform',
-      'TV.apk' : 'PRESIGNED',
+      'RecoveryLocalizer.apk': 'certs/devkey',
+      'Settings.apk': 'build/make/target/product/security/platform',
+      'TV.apk': 'PRESIGNED',
   }
 
   APKCERTS_TXT2 = (
@@ -838,10 +842,10 @@
   )
 
   APKCERTS_CERTMAP2 = {
-      'Compressed1.apk' : 'certs/compressed1',
-      'Compressed2a.apk' : 'certs/compressed2',
-      'Compressed2b.apk' : 'certs/compressed2',
-      'Compressed3.apk' : 'certs/compressed3',
+      'Compressed1.apk': 'certs/compressed1',
+      'Compressed2a.apk': 'certs/compressed2',
+      'Compressed2b.apk': 'certs/compressed2',
+      'Compressed3.apk': 'certs/compressed3',
   }
 
   APKCERTS_TXT3 = (
@@ -850,7 +854,7 @@
   )
 
   APKCERTS_CERTMAP3 = {
-      'Compressed4.apk' : 'certs/compressed4',
+      'Compressed4.apk': 'certs/compressed4',
   }
 
   # Test parsing with no optional fields, both optional fields, and only the
@@ -867,9 +871,9 @@
   )
 
   APKCERTS_CERTMAP4 = {
-      'RecoveryLocalizer.apk' : 'certs/devkey',
-      'Settings.apk' : 'build/make/target/product/security/platform',
-      'TV.apk' : 'PRESIGNED',
+      'RecoveryLocalizer.apk': 'certs/devkey',
+      'Settings.apk': 'build/make/target/product/security/platform',
+      'TV.apk': 'PRESIGNED',
   }
 
   def setUp(self):
@@ -973,7 +977,7 @@
     extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
     extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
     with open(extracted_from_privkey, 'rb') as privkey_fp, \
-        open(extracted_from_pubkey, 'rb') as pubkey_fp:
+            open(extracted_from_pubkey, 'rb') as pubkey_fp:
       self.assertEqual(privkey_fp.read(), pubkey_fp.read())
 
   def test_ParseCertificate(self):
@@ -1237,7 +1241,8 @@
     self.assertEqual(
         '1-5 9-10',
         sparse_image.file_map['//system/file1'].extra['text_str'])
-    self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
+    self.assertTrue(
+        sparse_image.file_map['//system/file2'].extra['incomplete'])
     self.assertTrue(
         sparse_image.file_map['/system/app/file3'].extra['incomplete'])
 
@@ -1345,7 +1350,7 @@
       'recovery_api_version': 3,
       'fstab_version': 2,
       'system_root_image': 'true',
-      'no_recovery' : 'true',
+      'no_recovery': 'true',
       'recovery_as_boot': 'true',
   }
 
@@ -1667,6 +1672,7 @@
     self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
                       test_file.name, 'generic_kernel')
 
+
 class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
   """Checks the format of install-recovery.sh.
 
@@ -1676,7 +1682,7 @@
   def setUp(self):
     self._tempdir = common.MakeTempDir()
     # Create a fake dict that contains the fstab info for boot&recovery.
-    self._info = {"fstab" : {}}
+    self._info = {"fstab": {}}
     fake_fstab = [
         "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
         "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
@@ -2023,11 +2029,11 @@
           input_zip, 'odm', placeholder_values)
 
     self.assertEqual({
-      'ro.odm.build.date.utc': '1578430045',
-      'ro.odm.build.fingerprint':
-      'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
-      'ro.product.odm.device': 'coral',
-      'ro.product.odm.name': 'product1',
+        'ro.odm.build.date.utc': '1578430045',
+        'ro.odm.build.fingerprint':
+        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
+        'ro.product.odm.device': 'coral',
+        'ro.product.odm.name': 'product1',
     }, partition_props.build_props)
 
     with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
@@ -2210,8 +2216,8 @@
 
     copied_props = copy.deepcopy(partition_props)
     self.assertEqual({
-      'ro.odm.build.date.utc': '1578430045',
-      'ro.odm.build.fingerprint':
-      'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
-      'ro.product.odm.device': 'coral',
+        'ro.odm.build.date.utc': '1578430045',
+        'ro.odm.build.fingerprint':
+        'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
+        'ro.product.odm.device': 'coral',
     }, copied_props.build_props)
diff --git a/tools/sbom/Android.bp b/tools/sbom/Android.bp
index 4837dde..519251e 100644
--- a/tools/sbom/Android.bp
+++ b/tools/sbom/Android.bp
@@ -53,5 +53,27 @@
     libs: [
         "sbom_lib",
     ],
+    version: {
+        py3: {
+            embedded_launcher: true,
+        },
+    },
+    test_suites: ["general-tests"],
+}
+
+python_test_host {
+    name: "sbom_data_test",
+    main: "sbom_data_test.py",
+    srcs: [
+        "sbom_data_test.py",
+    ],
+    libs: [
+        "sbom_lib",
+    ],
+    version: {
+        py3: {
+            embedded_launcher: true,
+        },
+    },
     test_suites: ["general-tests"],
 }
diff --git a/tools/sbom/sbom_data.py b/tools/sbom/sbom_data.py
index ea38e36..71f8660 100644
--- a/tools/sbom/sbom_data.py
+++ b/tools/sbom/sbom_data.py
@@ -133,7 +133,7 @@
       checksums = []
       for file in self.files:
         if file.id in package.file_ids:
-          checksums.append(file.checksum)
+          checksums.append(file.checksum.split(': ')[1])
       checksums.sort()
       h = hashlib.sha1()
       h.update(''.join(checksums).encode(encoding='utf-8'))
diff --git a/tools/sbom/sbom_data_test.py b/tools/sbom/sbom_data_test.py
new file mode 100644
index 0000000..69bc9d2
--- /dev/null
+++ b/tools/sbom/sbom_data_test.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import hashlib
+import unittest
+import sbom_data
+
+BUILD_FINGER_PRINT = 'build_finger_print'
+SUPPLIER_GOOGLE = 'Organization: Google'
+SUPPLIER_UPSTREAM = 'Organization: upstream'
+
+SPDXID_PREBUILT_PACKAGE1 = 'SPDXRef-PREBUILT-package1'
+SPDXID_SOURCE_PACKAGE1 = 'SPDXRef-SOURCE-package1'
+SPDXID_UPSTREAM_PACKAGE1 = 'SPDXRef-UPSTREAM-package1'
+
+SPDXID_FILE1 = 'SPDXRef-file1'
+SPDXID_FILE2 = 'SPDXRef-file2'
+SPDXID_FILE3 = 'SPDXRef-file3'
+SPDXID_FILE4 = 'SPDXRef-file4'
+
+
+class SBOMDataTest(unittest.TestCase):
+
+  def setUp(self):
+    # SBOM of a product
+    self.sbom_doc = sbom_data.Document(name='test doc',
+                                       namespace='http://www.google.com/sbom/spdx/android',
+                                       creators=[SUPPLIER_GOOGLE],
+                                       created='2023-03-31T22:17:58Z',
+                                       describes=sbom_data.SPDXID_PRODUCT)
+    self.sbom_doc.add_external_ref(
+        sbom_data.DocumentExternalReference(id='DocumentRef-external_doc_ref',
+                                            uri='external_doc_uri',
+                                            checksum='SHA1: 1234567890'))
+    self.sbom_doc.add_package(
+        sbom_data.Package(id=sbom_data.SPDXID_PRODUCT,
+                          name=sbom_data.PACKAGE_NAME_PRODUCT,
+                          download_location=sbom_data.VALUE_NONE,
+                          supplier=SUPPLIER_GOOGLE,
+                          version=BUILD_FINGER_PRINT,
+                          files_analyzed=True,
+                          verification_code='',
+                          file_ids=[SPDXID_FILE1, SPDXID_FILE2, SPDXID_FILE3, SPDXID_FILE4]))
+
+    self.sbom_doc.add_package(
+        sbom_data.Package(id=sbom_data.SPDXID_PLATFORM,
+                          name=sbom_data.PACKAGE_NAME_PLATFORM,
+                          download_location=sbom_data.VALUE_NONE,
+                          supplier=SUPPLIER_GOOGLE,
+                          version=BUILD_FINGER_PRINT,
+                          ))
+
+    self.sbom_doc.add_package(
+        sbom_data.Package(id=SPDXID_PREBUILT_PACKAGE1,
+                          name='Prebuilt package1',
+                          download_location=sbom_data.VALUE_NONE,
+                          supplier=SUPPLIER_GOOGLE,
+                          version=BUILD_FINGER_PRINT,
+                          ))
+
+    self.sbom_doc.add_package(
+        sbom_data.Package(id=SPDXID_SOURCE_PACKAGE1,
+                          name='Source package1',
+                          download_location=sbom_data.VALUE_NONE,
+                          supplier=SUPPLIER_GOOGLE,
+                          version=BUILD_FINGER_PRINT,
+                          external_refs=[sbom_data.PackageExternalRef(
+                              category=sbom_data.PackageExternalRefCategory.SECURITY,
+                              type=sbom_data.PackageExternalRefType.cpe22Type,
+                              locator='cpe:/a:jsoncpp_project:jsoncpp:1.9.4')]
+                          ))
+
+    self.sbom_doc.add_package(
+        sbom_data.Package(id=SPDXID_UPSTREAM_PACKAGE1,
+                          name='Upstream package1',
+                          supplier=SUPPLIER_UPSTREAM,
+                          version='1.1',
+                          ))
+
+    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_SOURCE_PACKAGE1,
+                                                          relationship=sbom_data.RelationshipType.VARIANT_OF,
+                                                          id2=SPDXID_UPSTREAM_PACKAGE1))
+
+    self.sbom_doc.files.append(
+        sbom_data.File(id=SPDXID_FILE1, name='/bin/file1',
+                       checksum='SHA1: 356a192b7913b04c54574d18c28d46e6395428ab'))  # sha1 hash of 1
+    self.sbom_doc.files.append(
+        sbom_data.File(id=SPDXID_FILE2, name='/bin/file2',
+                       checksum='SHA1: da4b9237bacccdf19c0760cab7aec4a8359010b0'))  # sha1 hash of 2
+    self.sbom_doc.files.append(
+        sbom_data.File(id=SPDXID_FILE3, name='/bin/file3',
+                       checksum='SHA1: 77de68daecd823babbb58edb1c8e14d7106e83bb'))  # sha1 hash of 3
+    self.sbom_doc.files.append(
+        sbom_data.File(id=SPDXID_FILE4, name='file4.a',
+                       checksum='SHA1: 1b6453892473a467d07372d45eb05abc2031647a'))  # sha1 of 4
+
+    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE1,
+                                                          relationship=sbom_data.RelationshipType.GENERATED_FROM,
+                                                          id2=sbom_data.SPDXID_PLATFORM))
+    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE2,
+                                                          relationship=sbom_data.RelationshipType.GENERATED_FROM,
+                                                          id2=SPDXID_PREBUILT_PACKAGE1))
+    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE3,
+                                                          relationship=sbom_data.RelationshipType.GENERATED_FROM,
+                                                          id2=SPDXID_SOURCE_PACKAGE1
+                                                          ))
+    self.sbom_doc.add_relationship(sbom_data.Relationship(id1=SPDXID_FILE1,
+                                                          relationship=sbom_data.RelationshipType.STATIC_LINK,
+                                                          id2=SPDXID_FILE4
+                                                          ))
+
+  def test_package_verification_code(self):
+    checksums = []
+    for file in self.sbom_doc.files:
+      checksums.append(file.checksum.split(': ')[1])
+      checksums.sort()
+    h = hashlib.sha1()
+    h.update(''.join(checksums).encode(encoding='utf-8'))
+    expected_package_verification_code = h.hexdigest()
+
+    self.sbom_doc.generate_packages_verification_code()
+    self.assertEqual(expected_package_verification_code, self.sbom_doc.packages[0].verification_code)
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/tools/zipalign/Android.bp b/tools/zipalign/Android.bp
index 0e1d58e..8be7e25 100644
--- a/tools/zipalign/Android.bp
+++ b/tools/zipalign/Android.bp
@@ -70,6 +70,7 @@
         "libgmock",
     ],
     data: [
+         "tests/data/apkWithUncompressedSharedLibs.zip",
          "tests/data/archiveWithOneDirectoryEntry.zip",
          "tests/data/diffOrders.zip",
          "tests/data/holes.zip",
diff --git a/tools/zipalign/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp
index 23840e3..f32f90b 100644
--- a/tools/zipalign/ZipAlign.cpp
+++ b/tools/zipalign/ZipAlign.cpp
@@ -17,6 +17,7 @@
 #include "ZipFile.h"
 
 #include <stdio.h>
+#include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 
@@ -36,17 +37,14 @@
 }
 
 static int getAlignment(bool pageAlignSharedLibs, int defaultAlignment,
-    ZipEntry* pEntry) {
-
-    static const int kPageAlignment = 4096;
-
+    ZipEntry* pEntry, int pageSize) {
     if (!pageAlignSharedLibs) {
         return defaultAlignment;
     }
 
     const char* ext = strrchr(pEntry->getFileName(), '.');
     if (ext && strcmp(ext, ".so") == 0) {
-        return kPageAlignment;
+        return pageSize;
     }
 
     return defaultAlignment;
@@ -56,7 +54,7 @@
  * Copy all entries from "pZin" to "pZout", aligning as needed.
  */
 static int copyAndAlign(ZipFile* pZin, ZipFile* pZout, int alignment, bool zopfli,
-    bool pageAlignSharedLibs)
+    bool pageAlignSharedLibs, int pageSize)
 {
     int numEntries = pZin->getNumEntries();
     ZipEntry* pEntry;
@@ -84,7 +82,8 @@
                 status = pZout->add(pZin, pEntry, padding, &pNewEntry);
             }
         } else {
-            const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry);
+            const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry,
+                                             pageSize);
 
             //printf("--- %s: orig at %ld(+%d) len=%ld, adding pad=%d\n",
             //    pEntry->getFileName(), (long) pEntry->getFileOffset(),
@@ -107,7 +106,7 @@
  * output file exists and "force" wasn't specified.
  */
 int process(const char* inFileName, const char* outFileName,
-    int alignment, bool force, bool zopfli, bool pageAlignSharedLibs)
+    int alignment, bool force, bool zopfli, bool pageAlignSharedLibs, int pageSize)
 {
     ZipFile zin, zout;
 
@@ -127,7 +126,7 @@
     }
 
     if (zin.open(inFileName, ZipFile::kOpenReadOnly) != OK) {
-        fprintf(stderr, "Unable to open '%s' as zip archive\n", inFileName);
+        fprintf(stderr, "Unable to open '%s' as zip archive: %s\n", inFileName, strerror(errno));
         return 1;
     }
     if (zout.open(outFileName,
@@ -138,7 +137,8 @@
         return 1;
     }
 
-    int result = copyAndAlign(&zin, &zout, alignment, zopfli, pageAlignSharedLibs);
+    int result = copyAndAlign(&zin, &zout, alignment, zopfli, pageAlignSharedLibs,
+                              pageSize);
     if (result != 0) {
         printf("zipalign: failed rewriting '%s' to '%s'\n",
             inFileName, outFileName);
@@ -150,7 +150,7 @@
  * Verify the alignment of a zip archive.
  */
 int verify(const char* fileName, int alignment, bool verbose,
-    bool pageAlignSharedLibs)
+    bool pageAlignSharedLibs, int pageSize)
 {
     ZipFile zipFile;
     bool foundBad = false;
@@ -181,7 +181,8 @@
             continue;
        } else {
             off_t offset = pEntry->getFileOffset();
-            const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry);
+            const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry,
+                                             pageSize);
             if ((offset % alignTo) != 0) {
                 if (verbose) {
                     printf("%8jd %s (BAD - %jd)\n",
diff --git a/tools/zipalign/ZipAlignMain.cpp b/tools/zipalign/ZipAlignMain.cpp
index 53fc8d4..2f24403 100644
--- a/tools/zipalign/ZipAlignMain.cpp
+++ b/tools/zipalign/ZipAlignMain.cpp
@@ -34,15 +34,18 @@
     fprintf(stderr, "Zip alignment utility\n");
     fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n");
     fprintf(stderr,
-        "Usage: zipalign [-f] [-p] [-v] [-z] <align> infile.zip outfile.zip\n"
-        "       zipalign -c [-p] [-v] <align> infile.zip\n\n" );
+        "Usage: zipalign [-f] [-p] [-P <pagesize_kb>] [-v] [-z] <align> infile.zip outfile.zip\n"
+        "       zipalign -c [-p] [-P <pagesize_kb>] [-v] <align> infile.zip\n\n" );
     fprintf(stderr,
         "  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n");
     fprintf(stderr, "  -c: check alignment only (does not modify file)\n");
     fprintf(stderr, "  -f: overwrite existing outfile.zip\n");
-    fprintf(stderr, "  -p: page-align uncompressed .so files\n");
+    fprintf(stderr, "  -p: 4kb page-align uncompressed .so files\n");
     fprintf(stderr, "  -v: verbose output\n");
     fprintf(stderr, "  -z: recompress using Zopfli\n");
+    fprintf(stderr, "  -P <pagesize_kb>: Align uncompressed .so files to the specified\n");
+    fprintf(stderr, "                    page size. Valid values for <pagesize_kb> are 4, 16\n");
+    fprintf(stderr, "                    and 64. '-P' cannot be used in combination with '-p'.\n");
 }
 
 
@@ -57,12 +60,16 @@
     bool verbose = false;
     bool zopfli = false;
     bool pageAlignSharedLibs = false;
+    int pageSize = 4096;
+    bool legacyPageAlignmentFlag = false;   // -p
+    bool pageAlignmentFlag = false;         // -P <pagesize_kb>
     int result = 1;
     int alignment;
     char* endp;
 
     int opt;
-    while ((opt = getopt(argc, argv, "fcpvz")) != -1) {
+
+    while ((opt = getopt(argc, argv, "fcpvzP:")) != -1) {
         switch (opt) {
         case 'c':
             check = true;
@@ -77,7 +84,29 @@
             zopfli = true;
             break;
         case 'p':
+            legacyPageAlignmentFlag = true;
             pageAlignSharedLibs = true;
+            pageSize = 4096;
+            break;
+        case 'P':
+            pageAlignmentFlag = true;
+            pageAlignSharedLibs = true;
+
+            if (!optarg) {
+                fprintf(stderr, "ERROR: -P requires an argument\n");
+                wantUsage = true;
+                goto bail;
+            }
+
+            pageSize = atoi(optarg);
+            if (pageSize != 4 && pageSize != 16 && pageSize != 64) {
+                fprintf(stderr, "ERROR: Invalid argument for -P: %s\n", optarg);
+                wantUsage = true;
+                goto bail;
+            }
+
+            pageSize *= 1024;  // Convert from kB to bytes.
+
             break;
         default:
             fprintf(stderr, "ERROR: unknown flag -%c\n", opt);
@@ -86,6 +115,13 @@
         }
     }
 
+    if (legacyPageAlignmentFlag && pageAlignmentFlag) {
+            fprintf(stderr, "ERROR: Invalid options: '-P <pagesize_kb>' and '-p'"
+                            "cannot be used in combination.\n");
+            wantUsage = true;
+            goto bail;
+    }
+
     if (!((check && (argc - optind) == 2) || (!check && (argc - optind) == 3))) {
         wantUsage = true;
         goto bail;
@@ -100,14 +136,15 @@
 
     if (check) {
         /* check existing archive for correct alignment */
-        result = verify(argv[optind + 1], alignment, verbose, pageAlignSharedLibs);
+        result = verify(argv[optind + 1], alignment, verbose, pageAlignSharedLibs, pageSize);
     } else {
         /* create the new archive */
-        result = process(argv[optind + 1], argv[optind + 2], alignment, force, zopfli, pageAlignSharedLibs);
+        result = process(argv[optind + 1], argv[optind + 2], alignment, force, zopfli,
+                         pageAlignSharedLibs, pageSize);
 
         /* trust, but verify */
         if (result == 0) {
-            result = verify(argv[optind + 2], alignment, verbose, pageAlignSharedLibs);
+            result = verify(argv[optind + 2], alignment, verbose, pageAlignSharedLibs, pageSize);
         }
     }
 
diff --git a/tools/zipalign/include/ZipAlign.h b/tools/zipalign/include/ZipAlign.h
index ab36086..85dda14 100644
--- a/tools/zipalign/include/ZipAlign.h
+++ b/tools/zipalign/include/ZipAlign.h
@@ -25,24 +25,28 @@
  * - force  : Overwrite output if it exists, fail otherwise.
  * - zopfli : Recompress compressed entries with more efficient algorithm.
  *            Copy compressed entries as-is, and unaligned, otherwise.
- * - pageAlignSharedLibs: Align .so files to 4096 and other files to
+ * - pageAlignSharedLibs: Align .so files to @pageSize and other files to
  *   alignTo, or all files to alignTo if false..
+ * - pageSize: Specifies the page size of the target device. This is used
+ *             to correctly page-align shared libraries.
  *
  * Returns 0 on success.
  */
 int process(const char* input, const char* output, int alignTo, bool force,
-    bool zopfli, bool pageAlignSharedLibs);
+    bool zopfli, bool pageAlignSharedLibs, int pageSize);
 
 /*
  * Verify the alignment of a zip archive.
  * - alignTo: Alignment (in bytes) for uncompressed entries.
- * - pageAlignSharedLibs: Align .so files to 4096 and other files to
+ * - pageAlignSharedLibs: Align .so files to @pageSize and other files to
  *   alignTo, or all files to alignTo if false..
+ * - pageSize: Specifies the page size of the target device. This is used
+ *             to correctly page-align shared libraries.
  *
  * Returns 0 on success.
  */
 int verify(const char* fileName, int alignTo, bool verbose,
-    bool pageAlignSharedLibs);
+    bool pageAlignSharedLibs, int pageSize);
 
 } // namespace android
 
diff --git a/tools/zipalign/tests/data/apkWithUncompressedSharedLibs.zip b/tools/zipalign/tests/data/apkWithUncompressedSharedLibs.zip
new file mode 100644
index 0000000..930e3b5
--- /dev/null
+++ b/tools/zipalign/tests/data/apkWithUncompressedSharedLibs.zip
Binary files differ
diff --git a/tools/zipalign/tests/src/align_test.cpp b/tools/zipalign/tests/src/align_test.cpp
index a8433fa..07ad7cc 100644
--- a/tools/zipalign/tests/src/align_test.cpp
+++ b/tools/zipalign/tests/src/align_test.cpp
@@ -48,11 +48,12 @@
 TEST(Align, Unaligned) {
   const std::string src = GetTestPath("unaligned.zip");
   const std::string dst = GetTempPath("unaligned_out.zip");
+  int pageSize = 4096;
 
-  int processed = process(src.c_str(), dst.c_str(), 4, true, false, 4096);
+  int processed = process(src.c_str(), dst.c_str(), 4, true, false, false, pageSize);
   ASSERT_EQ(0, processed);
 
-  int verified = verify(dst.c_str(), 4, true, false);
+  int verified = verify(dst.c_str(), 4, true, false, pageSize);
   ASSERT_EQ(0, verified);
 }
 
@@ -60,18 +61,19 @@
   const std::string src = GetTestPath("unaligned.zip");
   const std::string tmp = GetTempPath("da_aligned.zip");
   const std::string dst = GetTempPath("da_d_aligner.zip");
+  int pageSize = 4096;
 
-  int processed = process(src.c_str(), tmp.c_str(), 4, true, false, 4096);
+  int processed = process(src.c_str(), tmp.c_str(), 4, true, false, false, pageSize);
   ASSERT_EQ(0, processed);
 
-  int verified = verify(tmp.c_str(), 4, true, false);
+  int verified = verify(tmp.c_str(), 4, true, false, pageSize);
   ASSERT_EQ(0, verified);
 
   // Align the result of the previous run. Essentially double aligning.
-  processed = process(tmp.c_str(), dst.c_str(), 4, true, false, 4096);
+  processed = process(tmp.c_str(), dst.c_str(), 4, true, false, false, pageSize);
   ASSERT_EQ(0, processed);
 
-  verified = verify(dst.c_str(), 4, true, false);
+  verified = verify(dst.c_str(), 4, true, false, pageSize);
   ASSERT_EQ(0, verified);
 
   // Nothing should have changed between tmp and dst.
@@ -90,11 +92,12 @@
 TEST(Align, Holes) {
   const std::string src = GetTestPath("holes.zip");
   const std::string dst = GetTempPath("holes_out.zip");
+  int pageSize = 4096;
 
-  int processed = process(src.c_str(), dst.c_str(), 4, true, false, 4096);
+  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);
   ASSERT_EQ(0, processed);
 
-  int verified = verify(dst.c_str(), 4, false, true);
+  int verified = verify(dst.c_str(), 4, false, true, pageSize);
   ASSERT_EQ(0, verified);
 }
 
@@ -102,28 +105,85 @@
 TEST(Align, DifferenteOrders) {
   const std::string src = GetTestPath("diffOrders.zip");
   const std::string dst = GetTempPath("diffOrders_out.zip");
+  int pageSize = 4096;
 
-  int processed = process(src.c_str(), dst.c_str(), 4, true, false, 4096);
+  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);
   ASSERT_EQ(0, processed);
 
-  int verified = verify(dst.c_str(), 4, false, true);
+  int verified = verify(dst.c_str(), 4, false, true, pageSize);
   ASSERT_EQ(0, verified);
 }
 
 TEST(Align, DirectoryEntryDoNotRequireAlignment) {
   const std::string src = GetTestPath("archiveWithOneDirectoryEntry.zip");
-  int verified = verify(src.c_str(), 4, false, true);
+  int pageSize = 4096;
+  int verified = verify(src.c_str(), 4, false, true, pageSize);
   ASSERT_EQ(0, verified);
 }
 
 TEST(Align, DirectoryEntry) {
   const std::string src = GetTestPath("archiveWithOneDirectoryEntry.zip");
   const std::string dst = GetTempPath("archiveWithOneDirectoryEntry_out.zip");
+  int pageSize = 4096;
 
-  int processed = process(src.c_str(), dst.c_str(), 4, true, false, 4096);
+  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);
   ASSERT_EQ(0, processed);
   ASSERT_EQ(true, sameContent(src, dst));
 
-  int verified = verify(dst.c_str(), 4, false, true);
+  int verified = verify(dst.c_str(), 4, false, true, pageSize);
+  ASSERT_EQ(0, verified);
+}
+
+class UncompressedSharedLibsTest : public ::testing::Test {
+  protected:
+    static void SetUpTestSuite() {
+      src = GetTestPath("apkWithUncompressedSharedLibs.zip");
+      dst = GetTempPath("apkWithUncompressedSharedLibs_out.zip");
+    }
+
+    static std::string src;
+    static std::string dst;
+};
+
+std::string UncompressedSharedLibsTest::src;
+std::string UncompressedSharedLibsTest::dst;
+
+TEST_F(UncompressedSharedLibsTest, Unaligned) {
+  int pageSize = 4096;
+
+  int processed = process(src.c_str(), dst.c_str(), 4, true, false, false, pageSize);
+  ASSERT_EQ(0, processed);
+
+  int verified = verify(dst.c_str(), 4, true, true, pageSize);
+  ASSERT_NE(0, verified); // .so's not page-aligned
+}
+
+TEST_F(UncompressedSharedLibsTest, AlignedPageSize4kB) {
+  int pageSize = 4096;
+
+  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);
+  ASSERT_EQ(0, processed);
+
+  int verified = verify(dst.c_str(), 4, true, true, pageSize);
+  ASSERT_EQ(0, verified);
+}
+
+TEST_F(UncompressedSharedLibsTest, AlignedPageSize16kB) {
+  int pageSize = 16384;
+
+  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);
+  ASSERT_EQ(0, processed);
+
+  int verified = verify(dst.c_str(), 4, true, true, pageSize);
+  ASSERT_EQ(0, verified);
+}
+
+TEST_F(UncompressedSharedLibsTest, AlignedPageSize64kB) {
+  int pageSize = 65536;
+
+  int processed = process(src.c_str(), dst.c_str(), 4, true, false, true, pageSize);
+  ASSERT_EQ(0, processed);
+
+  int verified = verify(dst.c_str(), 4, true, true, pageSize);
   ASSERT_EQ(0, verified);
 }