Merge "Add BUILD_BROKEN_PLUGIN_VALIDATION flag"
diff --git a/Changes.md b/Changes.md
index 6d20173..fc6701d 100644
--- a/Changes.md
+++ b/Changes.md
@@ -505,6 +505,24 @@
 
 will copy `bar/baz` into `$DIST_DIR/baz` when `m foo dist` is run.
 
+#### FILE_NAME_TAG  {#FILE_NAME_TAG}
+
+To embed the `BUILD_NUMBER` (or for local builds, `eng.${USER}`), include
+`FILE_NAME_TAG_PLACEHOLDER` in the destination:
+
+``` make
+# you can use dist-for-goals-with-filenametag function
+$(call dist-for-goals-with-filenametag,foo,bar.zip)
+# or use FILE_NAME_TAG_PLACEHOLDER manually
+$(call dist-for-goals,foo,bar.zip:baz-FILE_NAME_TAG_PLACEHOLDER.zip)
+```
+
+Which will produce `$DIST_DIR/baz-1234567.zip` on build servers which set
+`BUILD_NUMBER=1234567`, or `$DIST_DIR/baz-eng.builder.zip` for local builds.
+
+If you just want to append `BUILD_NUMBER` at the end of basename, use
+`dist-for-goals-with-filenametag` instead of `dist-for-goals`.
+
 #### Renames during copy
 
 Instead of specifying just a file, a destination name can be specified,
diff --git a/core/Makefile b/core/Makefile
index ccfa3f2..4e5787d 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -711,7 +711,7 @@
 ifeq ($(TARGET_BUILD_TYPE),debug)
   name := $(name)_debug
 endif
-name := $(name)-apkcerts-$(FILE_NAME_TAG)
+name := $(name)-apkcerts
 intermediates := \
 	$(call intermediates-dir-for,PACKAGING,apkcerts)
 APKCERTS_FILE := $(intermediates)/$(name).txt
@@ -1001,6 +1001,42 @@
 
 BUILT_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img
 
+
+ifneq ($(BOARD_KERNEL_MODULES_16K),)
+
+TARGET_OUT_RAMDISK_16K := $(PRODUCT_OUT)/ramdisk_16k
+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))))
+	$(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) > $@
+
+# Builds a ramdisk using modules defined in BOARD_KERNEL_MODULES_16K
+ramdisk_16k: $(BUILT_RAMDISK_16K_TARGET)
+.PHONY: ramdisk_16k
+
+endif
+
+ifneq ($(BOARD_KERNEL_PATH_16K),)
+BUILT_KERNEL_16K_TARGET := $(PRODUCT_OUT)/kernel_16k
+
+$(eval $(call copy-one-file,$(BOARD_KERNEL_PATH_16K),$(BUILT_KERNEL_16K_TARGET)))
+
+# Copies BOARD_KERNEL_PATH_16K to output directory as is
+kernel_16k: $(BUILT_KERNEL_16K_TARGET)
+.PHONY: kernel_16k
+
+endif
+
+
 ifeq ($(BOARD_RAMDISK_USE_LZ4),true)
 # -l enables the legacy format used by the Linux kernel
 COMPRESSION_COMMAND_DEPS := $(LZ4)
@@ -5674,7 +5710,7 @@
 ifeq ($(TARGET_BUILD_TYPE),debug)
   name := $(name)_debug
 endif
-name := $(name)-target_files-$(FILE_NAME_TAG)
+name := $(name)-target_files
 
 intermediates := $(call intermediates-dir-for,PACKAGING,target_files)
 BUILT_TARGET_FILES_DIR := $(intermediates)/$(name).zip.list
@@ -5965,6 +6001,8 @@
 	    $(INSTALLED_RAMDISK_TARGET) \
 	    $(INSTALLED_DTBIMAGE_TARGET) \
 	    $(INSTALLED_2NDBOOTLOADER_TARGET) \
+	    $(BUILT_RAMDISK_16K_TARGET) \
+	    $(BUILT_KERNEL_16K_TARGET) \
 	    $(BOARD_PREBUILT_DTBOIMAGE) \
 	    $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE) \
 	    $(BOARD_RECOVERY_ACPIO) \
@@ -6308,6 +6346,14 @@
 	$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES
 	$(hide) cp $(INSTALLED_DTBOIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/
 endif # BOARD_PREBUILT_DTBOIMAGE
+ifdef BUILT_KERNEL_16K_TARGET
+	$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES
+	$(hide) cp $(BUILT_KERNEL_16K_TARGET) $(zip_root)/PREBUILT_IMAGES/
+endif # BUILT_KERNEL_16K_TARGET
+ifdef BUILT_RAMDISK_16K_TARGET
+	$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES
+	$(hide) cp $(BUILT_RAMDISK_16K_TARGET) $(zip_root)/PREBUILT_IMAGES/
+endif # BUILT_RAMDISK_16K_TARGET
 ifeq ($(BOARD_USES_PVMFWIMAGE),true)
 	$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES
 	$(hide) cp $(INSTALLED_PVMFWIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/
@@ -6444,7 +6490,7 @@
             $(BUILT_KERNEL_CONFIGS_FILE) \
             $(BUILT_KERNEL_VERSION_FILE),$(BUILT_TARGET_FILES_PACKAGE):)
 
-$(call dist-for-goals, target-files-package, $(BUILT_TARGET_FILES_PACKAGE))
+$(call dist-for-goals-with-filenametag, target-files-package, $(BUILT_TARGET_FILES_PACKAGE))
 
 # -----------------------------------------------------------------
 # NDK Sysroot Package
@@ -6479,7 +6525,7 @@
 ifeq ($(TARGET_BUILD_TYPE),debug)
   product_name := $(product_name)_debug
 endif
-name := $(product_name)-ota-$(FILE_NAME_TAG)
+name := $(product_name)-ota
 
 INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 INTERNAL_OTA_METADATA := $(PRODUCT_OUT)/ota_metadata
@@ -6499,7 +6545,7 @@
 otapackage: $(INTERNAL_OTA_PACKAGE_TARGET)
 
 ifeq ($(BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE),true)
-name := $(product_name)-ota-retrofit-$(FILE_NAME_TAG)
+name := $(product_name)-ota-retrofit
 
 INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
@@ -6520,7 +6566,7 @@
 endif # BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE
 
 ifneq ($(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST),)
-name := $(product_name)-partial-ota-$(FILE_NAME_TAG)
+name := $(product_name)-partial-ota
 
 INTERNAL_OTA_PARTIAL_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
@@ -6620,9 +6666,9 @@
 endif
 
 # The path to the zip file containing binaries with symbols.
-SYMBOLS_ZIP := $(PRODUCT_OUT)/$(name)-symbols-$(FILE_NAME_TAG).zip
+SYMBOLS_ZIP := $(PRODUCT_OUT)/$(name)-symbols.zip
 # The path to a file containing mappings from elf IDs to filenames.
-SYMBOLS_MAPPING := $(PRODUCT_OUT)/$(name)-symbols-mapping-$(FILE_NAME_TAG).textproto
+SYMBOLS_MAPPING := $(PRODUCT_OUT)/$(name)-symbols-mapping.textproto
 .KATI_READONLY := SYMBOLS_ZIP SYMBOLS_MAPPING
 # For apps_only build we'll establish the dependency later in build/make/core/main.mk.
 ifeq (,$(TARGET_BUILD_UNBUNDLED))
@@ -6697,7 +6743,7 @@
 ifeq ($(TARGET_BUILD_TYPE),debug)
   name := $(name)_debug
 endif
-name := $(name)-apps-$(FILE_NAME_TAG)
+name := $(name)-apps
 
 APPS_ZIP := $(PRODUCT_OUT)/$(name).zip
 $(APPS_ZIP): $(FULL_SYSTEMIMAGE_DEPS)
@@ -6748,9 +6794,9 @@
 #
 
 # The path to the zip file containing proguard dictionaries.
-PROGUARD_DICT_ZIP := $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict-$(FILE_NAME_TAG).zip
+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-$(FILE_NAME_TAG).textproto
+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))
@@ -6779,7 +6825,7 @@
 #------------------------------------------------------------------
 # A zip of Proguard usage files.
 #
-PROGUARD_USAGE_ZIP := $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-usage-$(FILE_NAME_TAG).zip
+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): \
@@ -6956,7 +7002,7 @@
 ifeq ($(TARGET_BUILD_TYPE),debug)
   name := $(name)_debug
 endif
-name := $(name)-img-$(FILE_NAME_TAG)
+name := $(name)-img
 
 INTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 
@@ -6972,7 +7018,7 @@
 
 .PHONY: updatepackage
 updatepackage: $(INTERNAL_UPDATE_PACKAGE_TARGET)
-$(call dist-for-goals,updatepackage,$(INTERNAL_UPDATE_PACKAGE_TARGET))
+$(call dist-for-goals-with-filenametag,updatepackage,$(INTERNAL_UPDATE_PACKAGE_TARGET))
 
 
 # -----------------------------------------------------------------
@@ -7101,7 +7147,7 @@
         $(INSTALLED_SYSTEMIMAGE_TARGET) \
         $(INSTALLED_USERDATAIMAGE_TARGET)
 
-name := $(TARGET_PRODUCT)-emulator-$(FILE_NAME_TAG)
+name := $(TARGET_PRODUCT)-emulator
 
 INTERNAL_EMULATOR_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 
@@ -7129,7 +7175,7 @@
 ifneq ($(HOST_OS),linux)
   $(error Building the monolithic SDK is only supported on Linux)
 endif
-sdk_name := android-sdk_$(FILE_NAME_TAG)
+sdk_name := android-sdk
 INTERNAL_SDK_HOST_OS_NAME := linux-$(SDK_HOST_ARCH)
 sdk_name := $(sdk_name)_$(INTERNAL_SDK_HOST_OS_NAME)
 
diff --git a/core/config.mk b/core/config.mk
index e272389..0e2d271 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -856,6 +856,7 @@
 .KATI_READONLY := MAINLINE_SEPOLICY_DEV_CERTIFICATES
 
 BUILD_NUMBER_FROM_FILE := $$(cat $(SOONG_OUT_DIR)/build_number.txt)
+BUILD_HOSTNAME_FROM_FILE := $$(cat $(SOONG_OUT_DIR)/build_hostname.txt)
 BUILD_DATETIME_FROM_FILE := $$(cat $(BUILD_DATETIME_FILE))
 
 # SEPolicy versions
@@ -1230,16 +1231,7 @@
 RSCOMPAT_32BIT_ONLY_API_LEVELS := 8 9 10 11 12 13 14 15 16 17 18 19 20
 RSCOMPAT_NO_USAGEIO_API_LEVELS := 8 9 10 11 12 13
 
-# Add BUILD_NUMBER to apps default version name if it's unbundled build.
-ifdef TARGET_BUILD_APPS
-TARGET_BUILD_WITH_APPS_VERSION_NAME := true
-endif
-
-ifdef TARGET_BUILD_WITH_APPS_VERSION_NAME
-APPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION)-$(BUILD_NUMBER_FROM_FILE)
-else
 APPS_DEFAULT_VERSION_NAME := $(PLATFORM_VERSION)
-endif
 
 # ANDROID_WARNING_ALLOWED_PROJECTS is generated by build/soong.
 define find_warning_allowed_projects
diff --git a/core/distdir.mk b/core/distdir.mk
index bce8e7f..032d1b7 100644
--- a/core/distdir.mk
+++ b/core/distdir.mk
@@ -45,6 +45,18 @@
     $(eval _all_dist_goal_output_pairs += $$(goal):$$(dst))))
 endef
 
+define add_file_name_tag_suffix
+$(basename $(notdir $1))-FILE_NAME_TAG_PLACEHOLDER$(suffix $1)
+endef
+
+# This function appends suffix FILE_NAME_TAG_PLACEHOLDER from the input file
+# $(1): a list of goals  (e.g. droid, sdk, ndk). These must be PHONY
+# $(2): the dist files to add to those goals.
+define dist-for-goals-with-filenametag
+$(if $(strip $(2)), \
+  $(foreach file,$(2), \
+    $(call dist-for-goals,$(1),$(file):$(call add_file_name_tag_suffix,$(file)))))
+endef
 .PHONY: shareprojects
 
 define __share-projects-rule
@@ -209,4 +221,4 @@
           fi))
 endef
 
-.KATI_READONLY := dist-for-goals dist-write-file
+.KATI_READONLY := dist-for-goals dist-write-file dist-for-goals-with-filenametag
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 860ce79..8887ddc 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -30,14 +30,13 @@
 # In order to avoid running starlark every time the stamp file is checked, we use
 # $(KATI_shell_no_rerun). Then, to make sure that we actually do rerun kati when
 # modifying the starlark files, we add the starlark files to the kati stamp file with
-# $(KATI_extra_file_deps). This behavior can be modified by passing a list of starlark files
-# to exclude from the dependency list as $(2)
+# $(KATI_extra_file_deps).
 define run-starlark
 $(eval _starlark_results := $(OUT_DIR)/starlark_results/$(subst /,_,$(1)).mk)
 $(KATI_shell_no_rerun mkdir -p $(OUT_DIR)/starlark_results && $(OUT_DIR)/rbcrun --mode=make $(1) >$(_starlark_results) && touch -t 200001010000 $(_starlark_results))
 $(if $(filter-out 0,$(.SHELLSTATUS)),$(error Starlark failed to run))
 $(eval include $(_starlark_results))
-$(KATI_extra_file_deps $(filter-out $(2),$(LOADED_STARLARK_FILES)))
+$(KATI_extra_file_deps $(LOADED_STARLARK_FILES))
 $(eval LOADED_STARLARK_FILES :=)
 $(eval _starlark_results :=)
 endef
diff --git a/core/host_java_library.mk b/core/host_java_library.mk
index 89aa53c..d45da48 100644
--- a/core/host_java_library.mk
+++ b/core/host_java_library.mk
@@ -98,9 +98,7 @@
 $(full_classes_combined_jar): $(full_classes_compiled_jar) \
                               $(jar_manifest_file) \
                               $(full_static_java_libs) | $(MERGE_ZIPS)
-	$(if $(PRIVATE_JAR_MANIFEST), $(hide) sed -e "s/%BUILD_NUMBER%/$(BUILD_NUMBER_FROM_FILE)/" \
-            $(PRIVATE_JAR_MANIFEST) > $(dir $@)/manifest.mf)
-	$(MERGE_ZIPS) -j --ignore-duplicates $(if $(PRIVATE_JAR_MANIFEST),-m $(dir $@)/manifest.mf) \
+	$(MERGE_ZIPS) -j --ignore-duplicates $(if $(PRIVATE_JAR_MANIFEST),-m $(PRIVATE_JAR_MANIFEST)) \
             $(if $(PRIVATE_DONT_DELETE_JAR_META_INF),,-stripDir META-INF -zipToNotStrip $<) \
             $@ $< $(PRIVATE_STATIC_JAVA_LIBRARIES)
 
diff --git a/core/java.mk b/core/java.mk
index b13ef4d..842fcbf 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -296,9 +296,7 @@
 $(full_classes_combined_jar): $(full_classes_compiled_jar) \
                               $(jar_manifest_file) \
                               $(full_static_java_libs) | $(MERGE_ZIPS)
-	$(if $(PRIVATE_JAR_MANIFEST), $(hide) sed -e "s/%BUILD_NUMBER%/$(BUILD_NUMBER_FROM_FILE)/" \
-            $(PRIVATE_JAR_MANIFEST) > $(dir $@)/manifest.mf)
-	$(MERGE_ZIPS) -j --ignore-duplicates $(if $(PRIVATE_JAR_MANIFEST),-m $(dir $@)/manifest.mf) \
+	$(MERGE_ZIPS) -j --ignore-duplicates $(if $(PRIVATE_JAR_MANIFEST),-m $(PRIVATE_JAR_MANIFEST)) \
             $(if $(PRIVATE_DONT_DELETE_JAR_META_INF),,-stripDir META-INF -zipToNotStrip $<) \
             $@ $< $(PRIVATE_STATIC_JAVA_LIBRARIES)
 
diff --git a/core/main.mk b/core/main.mk
index cb4dca6..903e261 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -40,31 +40,23 @@
 # Write the build number to a file so it can be read back in
 # without changing the command line every time.  Avoids rebuilds
 # when using ninja.
-$(shell mkdir -p $(SOONG_OUT_DIR) && \
-    echo -n $(BUILD_NUMBER) > $(SOONG_OUT_DIR)/build_number.tmp; \
-    if ! cmp -s $(SOONG_OUT_DIR)/build_number.tmp $(SOONG_OUT_DIR)/build_number.txt; then \
-        mv $(SOONG_OUT_DIR)/build_number.tmp $(SOONG_OUT_DIR)/build_number.txt; \
-    else \
-        rm $(SOONG_OUT_DIR)/build_number.tmp; \
-    fi)
 BUILD_NUMBER_FILE := $(SOONG_OUT_DIR)/build_number.txt
-.KATI_READONLY := BUILD_NUMBER_FILE
 $(KATI_obsolete_var BUILD_NUMBER,See https://android.googlesource.com/platform/build/+/master/Changes.md#BUILD_NUMBER)
+BUILD_HOSTNAME_FILE := $(SOONG_OUT_DIR)/build_hostname.txt
+$(KATI_obsolete_var BUILD_HOSTNAME,Use BUILD_HOSTNAME_FROM_FILE instead)
+$(KATI_obsolete_var FILE_NAME_TAG,https://android.googlesource.com/platform/build/+/master/Changes.md#FILE_NAME_TAG)
+
 $(BUILD_NUMBER_FILE):
-	touch $@
+	# empty rule to prevent dangling rule error for a file that is written by soong_ui
+$(BUILD_HOSTNAME_FILE):
+	# empty rule to prevent dangling rule error for a file that is written by soong_ui
+
+.KATI_RESTAT: $(BUILD_NUMBER_FILE)
+.KATI_RESTAT: $(BUILD_HOSTNAME_FILE)
 
 DATE_FROM_FILE := date -d @$(BUILD_DATETIME_FROM_FILE)
 .KATI_READONLY := DATE_FROM_FILE
 
-# Pick a reasonable string to use to identify files.
-ifeq ($(strip $(HAS_BUILD_NUMBER)),false)
-  # BUILD_NUMBER has a timestamp in it, which means that
-  # it will change every time.  Pick a stable value.
-  FILE_NAME_TAG := eng.$(BUILD_USERNAME)
-else
-  FILE_NAME_TAG := $(file <$(BUILD_NUMBER_FILE))
-endif
-.KATI_READONLY := FILE_NAME_TAG
 
 # Make an empty directory, which can be used to make empty jars
 EMPTY_DIRECTORY := $(OUT_DIR)/empty
@@ -1390,7 +1382,7 @@
     $(CUSTOM_MODULES) \
   )
 
-# Dedpulicate compatibility suite dist files across modules and packages before
+# Deduplicate compatibility suite dist files across modules and packages before
 # copying them to their requested locations. Assign the eval result to an unused
 # var to prevent Make from trying to make a sense of it.
 _unused := $(call copy-many-files, $(sort $(ALL_COMPATIBILITY_DIST_FILES)))
@@ -1751,15 +1743,15 @@
   endif
 
   $(PROGUARD_DICT_ZIP) : $(apps_only_installed_files)
-  $(call dist-for-goals,apps_only, $(PROGUARD_DICT_ZIP) $(PROGUARD_DICT_MAPPING))
+  $(call dist-for-goals-with-filenametag,apps_only, $(PROGUARD_DICT_ZIP) $(PROGUARD_DICT_ZIP) $(PROGUARD_DICT_MAPPING))
   $(call declare-container-license-deps,$(PROGUARD_DICT_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/)
 
   $(PROGUARD_USAGE_ZIP) : $(apps_only_installed_files)
-  $(call dist-for-goals,apps_only, $(PROGUARD_USAGE_ZIP))
+  $(call dist-for-goals-with-filenametag,apps_only, $(PROGUARD_USAGE_ZIP))
   $(call declare-container-license-deps,$(PROGUARD_USAGE_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/)
 
   $(SYMBOLS_ZIP) : $(apps_only_installed_files)
-  $(call dist-for-goals,apps_only, $(SYMBOLS_ZIP) $(SYMBOLS_MAPPING))
+  $(call dist-for-goals-with-filenametag,apps_only, $(SYMBOLS_ZIP) $(SYMBOLS_MAPPING))
   $(call declare-container-license-deps,$(SYMBOLS_ZIP),$(apps_only_installed_files),$(PRODUCT_OUT)/:/)
 
   $(COVERAGE_ZIP) : $(apps_only_installed_files)
@@ -1805,17 +1797,23 @@
   # avoid disting targets that would cause building framework java sources,
   # which we want to avoid in an unbundled build.
 
-  $(call dist-for-goals, droidcore-unbundled, \
+  $(call dist-for-goals-with-filenametag, droidcore-unbundled, \
     $(INTERNAL_UPDATE_PACKAGE_TARGET) \
     $(INTERNAL_OTA_PACKAGE_TARGET) \
-    $(INTERNAL_OTA_METADATA) \
     $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET) \
+    $(BUILT_RAMDISK_16K_TARGET) \
+    $(BUILT_KERNEL_16K_TARGET) \
     $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET) \
     $(SYMBOLS_ZIP) \
     $(SYMBOLS_MAPPING) \
     $(PROGUARD_DICT_ZIP) \
     $(PROGUARD_DICT_MAPPING) \
     $(PROGUARD_USAGE_ZIP) \
+    $(BUILT_TARGET_FILES_PACKAGE) \
+  )
+
+  $(call dist-for-goals, droidcore-unbundled, \
+    $(INTERNAL_OTA_METADATA) \
     $(COVERAGE_ZIP) \
     $(INSTALLED_FILES_FILE) \
     $(INSTALLED_FILES_JSON) \
@@ -1843,7 +1841,6 @@
     $(INSTALLED_ODM_BUILD_PROP_TARGET):build.prop-odm \
     $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET):build.prop-system_ext \
     $(INSTALLED_RAMDISK_BUILD_PROP_TARGET):build.prop-ramdisk \
-    $(BUILT_TARGET_FILES_PACKAGE) \
     $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
     $(INSTALLED_MISC_INFO_TARGET) \
     $(INSTALLED_RAMDISK_TARGET) \
@@ -1855,7 +1852,7 @@
     $(call dist-for-goals, droidcore-unbundled, $(f)))
 
   ifneq ($(ANDROID_BUILD_EMBEDDED),true)
-    $(call dist-for-goals, droidcore, \
+    $(call dist-for-goals-with-filenametag, droidcore, \
       $(APPS_ZIP) \
       $(INTERNAL_EMULATOR_PACKAGE_TARGET) \
     )
@@ -1955,10 +1952,8 @@
 ifeq ($(HOST_OS),linux)
 ALL_SDK_TARGETS := $(INTERNAL_SDK_TARGET)
 sdk: $(ALL_SDK_TARGETS)
-$(call dist-for-goals,sdk, \
-    $(ALL_SDK_TARGETS) \
-    $(INSTALLED_BUILD_PROP_TARGET) \
-)
+$(call dist-for-goals-with-filenametag,sdk,$(ALL_SDK_TARGETS))
+$(call dist-for-goals,sdk,$(INSTALLED_BUILD_PROP_TARGET))
 endif
 
 # umbrella targets to assit engineers in verifying builds
diff --git a/core/release_config.bzl b/core/release_config.bzl
deleted file mode 100644
index e73c90f..0000000
--- a/core/release_config.bzl
+++ /dev/null
@@ -1,90 +0,0 @@
-# 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.
-
-# Partitions that get build system flag summaries
-_flag_partitions = [
-    "product",
-    "system",
-    "system_ext",
-    "vendor",
-]
-
-def _combine_dicts_no_duplicate_keys(dicts):
-    result = {}
-    for d in dicts:
-        for k, v in d.items():
-            if k in result:
-                fail("Duplicate key: " + k)
-            result[k] = v
-    return result
-
-def release_config(target_release, flag_definitions, config_maps, fail_if_no_release_config = True):
-    result = {
-        "_ALL_RELEASE_FLAGS": [flag.name for flag in flag_definitions],
-    }
-    all_flags = {}
-    for flag in flag_definitions:
-        if sorted(dir(flag)) != ["default", "name", "partitions"]:
-            fail("Flag structs must contain 3 fields: name, partitions, and default")
-        if not flag.partitions:
-            fail("At least 1 partition is required")
-        for partition in flag.partitions:
-            if partition == "all":
-                if len(flag.partitions) > 1:
-                    fail("\"all\" can't be combined with other partitions: " + str(flag.partitions))
-            elif partition not in _flag_partitions:
-                fail("Invalid partition: " + flag.partition + ", allowed partitions: " + str(_flag_partitions))
-        if not flag.name.startswith("RELEASE_"):
-            fail("Release flag names must start with RELEASE_")
-        if " " in flag.name or "\t" in flag.name or "\n" in flag.name:
-            fail("Flag names must not contain whitespace.")
-        if flag.name in all_flags:
-            fail("Duplicate declaration of flag " + flag.name)
-        all_flags[flag.name] = True
-
-        default = flag.default
-        if type(default) == "bool":
-            default = "true" if default else ""
-
-        result["_ALL_RELEASE_FLAGS." + flag.name + ".PARTITIONS"] = flag.partitions
-        result["_ALL_RELEASE_FLAGS." + flag.name + ".DEFAULT"] = default
-        result["_ALL_RELEASE_FLAGS." + flag.name + ".VALUE"] = default
-
-    # If TARGET_RELEASE is set, fail if there is no matching release config
-    # If it isn't set, no release config files will be included and all flags
-    # will get their default values.
-    if target_release:
-        config_map = _combine_dicts_no_duplicate_keys(config_maps)
-        if target_release not in config_map:
-            fail("No release config found for TARGET_RELEASE: " + target_release + ". Available releases are: " + str(config_map.keys()))
-        release_config = config_map[target_release]
-        if sorted(dir(release_config)) != ["flags", "release_version"]:
-            fail("A release config must be a struct with a flags and release_version fields")
-        result["_RELEASE_VERSION"] = release_config.release_version
-        for flag in release_config.flags:
-            if sorted(dir(release_config)) != ["name", "value"]:
-                fail("A flag be a struct with name and value fields")
-            if flag.name not in all_flags:
-                fail("Undeclared build flag: " + flag.name)
-            value = flag.value
-            if type(value) == "bool":
-                value = "true" if value else ""
-            result["_ALL_RELEASE_FLAGS." + flag.name + ".VALUE"] = value
-    elif fail_if_no_release_config:
-        fail("FAIL_IF_NO_RELEASE_CONFIG was set and TARGET_RELEASE was not")
-    else:
-        # No TARGET_RELEASE means release version 0
-        result["_RELEASE_VERSION"] = 0
-
-    return result
diff --git a/core/release_config.mk b/core/release_config.mk
index 621c7d1..fdfc6a0 100644
--- a/core/release_config.mk
+++ b/core/release_config.mk
@@ -12,45 +12,78 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# Partitions that get build system flag summaries
+_FLAG_PARTITIONS := system vendor system_ext product
+
+# All possible release flags. Defined in the build_flags.mk files
+# throughout the tree
+_ALL_RELEASE_FLAGS :=
+
+# -----------------------------------------------------------------
+# Choose the flag files
+# Do this first, because we're going to unset TARGET_RELEASE before
+# including anyone, so they don't start making conditionals based on it.
+
 # If this is a google source tree, restrict it to only the one file
 # which has OWNERS control.  If it isn't let others define their own.
 # TODO: Remove wildcard for build/release one when all branch manifests
 # have updated.
-flag_declaration_files := $(wildcard build/release/build_flags.bzl) \
-    $(if $(wildcard vendor/google/release/build_flags.bzl), \
-        vendor/google/release/build_flags.bzl, \
+config_map_files := $(wildcard build/release/release_config_map.mk) \
+    $(if $(wildcard vendor/google/release/release_config_map.mk), \
+        vendor/google/release/release_config_map.mk, \
         $(sort \
-            $(wildcard device/*/release/build_flags.bzl) \
-            $(wildcard device/*/*/release/build_flags.bzl) \
-            $(wildcard vendor/*/release/build_flags.bzl) \
-            $(wildcard vendor/*/*/release/build_flags.bzl) \
-        ) \
-    )
-config_map_files := $(wildcard build/release/release_config_map.bzl) \
-    $(if $(wildcard vendor/google/release/release_config_map.bzl), \
-        vendor/google/release/release_config_map.bzl, \
-        $(sort \
-            $(wildcard device/*/release/release_config_map.bzl) \
-            $(wildcard device/*/*/release/release_config_map.bzl) \
-            $(wildcard vendor/*/release/release_config_map.bzl) \
-            $(wildcard vendor/*/*/release/release_config_map.bzl) \
+            $(wildcard device/*/release/release_config_map.mk) \
+            $(wildcard device/*/*/release/release_config_map.mk) \
+            $(wildcard vendor/*/release/release_config_map.mk) \
+            $(wildcard vendor/*/*/release/release_config_map.mk) \
         ) \
     )
 
-# Because starlark can't find files with $(wildcard), write an entrypoint starlark script that
-# contains the result of the above wildcards for the starlark code to use.
-filename_to_starlark=$(subst /,_,$(subst .,_,$(1)))
-_c:=load("//build/make/core/release_config.bzl", "release_config")
-_c+=$(foreach f,$(flag_declaration_files),$(newline)load("//$(f)", flags_$(call filename_to_starlark,$(f)) = "flags"))
-_c+=$(foreach f,$(config_map_files),$(newline)load("//$(f)", config_maps_$(call filename_to_starlark,$(f)) = "config_maps"))
-_c+=$(newline)all_flags = [] $(foreach f,$(flag_declaration_files),+ flags_$(call filename_to_starlark,$(f)))
-_c+=$(newline)all_config_maps = [$(foreach f,$(config_map_files),config_maps_$(call filename_to_starlark,$(f))$(comma))]
-_c+=$(newline)target_release = "$(TARGET_RELEASE)"
-_c+=$(newline)fail_if_no_release_config = True if "$(FAIL_IF_NO_RELEASE_CONFIG)" else False
-_c+=$(newline)variables_to_export_to_make = release_config(target_release, all_flags, all_config_maps, fail_if_no_release_config)
-$(file >$(OUT_DIR)/release_config_entrypoint.bzl,$(_c))
-_c:=
-filename_to_starlark:=
+# $1 config name
+# $2 release config files
+define declare-release-config
+    $(eval # No duplicates)
+    $(if $(filter $(_all_release_configs), $(strip $(1))), \
+        $(error declare-release-config: config $(strip $(1)) declared in: $(_included) Previously declared here: $(_all_release_configs.$(strip $(1)).DECLARED_IN)) \
+    )
+    $(eval # Must have release config files)
+    $(if $(strip $(2)),,  \
+        $(error declare-release-config: config $(strip $(1)) must have release config files) \
+    )
+    $(eval _all_release_configs := $(sort $(_all_release_configs) $(strip $(1))))
+    $(eval _all_release_configs.$(strip $(1)).DECLARED_IN := $(_included))
+    $(eval _all_release_configs.$(strip $(1)).FILES := $(strip $(2)))
+endef
+
+# Include the config map files
+$(foreach f, $(config_map_files), \
+    $(eval _included := $(f)) \
+    $(eval include $(f)) \
+)
+
+# If TARGET_RELEASE is set, fail if there is no matching release config
+# If it isn't set, no release config files will be included and all flags
+# will get their default values.
+ifneq ($(TARGET_RELEASE),)
+ifeq ($(filter $(_all_release_configs), $(TARGET_RELEASE)),)
+    $(error No release config found for TARGET_RELEASE: $(TARGET_RELEASE). Available releases are: $(_all_release_configs))
+else
+    # Choose flag files
+    # Don't sort this, use it in the order they gave us.
+    _release_config_files := $(_all_release_configs.$(TARGET_RELEASE).FILES)
+endif
+else
+# Useful for finding scripts etc that aren't passing or setting TARGET_RELEASE
+ifneq ($(FAIL_IF_NO_RELEASE_CONFIG),)
+    $(error FAIL_IF_NO_RELEASE_CONFIG was set and TARGET_RELEASE was not)
+endif
+_release_config_files :=
+endif
+
+# Unset variables so they can't use it
+define declare-release-config
+$(error declare-release-config can only be called from inside release_config_map.mk files)
+endef
 
 # TODO: Remove this check after enough people have sourced lunch that we don't
 # need to worry about it trying to do get_build_vars TARGET_RELEASE. Maybe after ~9/2023
@@ -63,7 +96,127 @@
 endif
 .KATI_READONLY := TARGET_RELEASE
 
-# Exclude the entrypoint file as a dependency (by passing it as the 2nd argument) so that we don't
-# rerun kati every build. Kati will replay the $(file) command that generates it every build,
-# updating its timestamp.
-$(call run-starlark,$(OUT_DIR)/release_config_entrypoint.bzl,$(OUT_DIR)/release_config_entrypoint.bzl)
+$(foreach config, $(_all_release_configs), \
+    $(eval _all_release_configs.$(config).DECLARED_IN:= ) \
+    $(eval _all_release_configs.$(config).FILES:= ) \
+)
+_all_release_configs:=
+config_map_files:=
+
+# -----------------------------------------------------------------
+# Declare the flags
+
+# $1 partition(s)
+# $2 flag name. Must start with RELEASE_
+# $3 default. True or false
+define declare-build-flag
+    $(if $(filter-out all $(_FLAG_PARTITIONS), $(strip $(1))), \
+        $(error declare-build-flag: invalid partitions: $(strip $(1))) \
+    )
+    $(if $(and $(filter all,$(strip $(1))),$(filter-out all, $(strip $(1)))), \
+        $(error declare-build-flag: "all" can't be combined with other partitions: $(strip $(1))), \
+        $(eval declare-build-flag.partition := $(_FLAG_PARTITIONS)) \
+    )
+    $(if $(filter-out RELEASE_%, $(strip $(2))), \
+        $(error declare-build-flag: Release flag names must start with RELEASE_: $(strip $(2))) \
+    )
+    $(eval _ALL_RELEASE_FLAGS += $(strip $(2)))
+    $(foreach partition, $(declare-build-flag.partition), \
+        $(eval _ALL_RELEASE_FLAGS.PARTITIONS.$(partition) := $(sort \
+            $(_ALL_RELEASE_FLAGS.PARTITIONS.$(partition)) $(strip $(2)))) \
+    )
+    $(eval _ALL_RELEASE_FLAGS.$(strip $(2)).PARTITIONS := $(declare-build-flag.partition))
+    $(eval _ALL_RELEASE_FLAGS.$(strip $(2)).DEFAULT := $(strip $(3)))
+    $(eval _ALL_RELEASE_FLAGS.$(strip $(2)).DECLARED_IN := $(_included))
+    $(eval _ALL_RELEASE_FLAGS.$(strip $(2)).VALUE := $(strip $(3)))
+    $(eval _ALL_RELEASE_FLAGS.$(strip $(2)).SET_IN := $(_included))
+    $(eval declare-build-flag.partition:=)
+endef
+
+
+# Choose the files
+# If this is a google source tree, restrict it to only the one file
+# which has OWNERS control.  If it isn't let others define their own.
+flag_declaration_files := $(wildcard build/release/build_flags.mk) \
+    $(if $(wildcard vendor/google/release/build_flags.mk), \
+        vendor/google/release/build_flags.mk, \
+        $(sort \
+            $(wildcard device/*/release/build_flags.mk) \
+            $(wildcard device/*/*/release/build_flags.mk) \
+            $(wildcard vendor/*/release/build_flags.mk) \
+            $(wildcard vendor/*/*/release/build_flags.mk) \
+        ) \
+    )
+
+# Include the files
+$(foreach f, $(flag_declaration_files), \
+    $(eval _included := $(f)) \
+    $(eval include $(f)) \
+)
+
+# Don't let anyone declare build flags after here
+define declare-build-flag
+$(error declare-build-flag can only be called from inside flag definition files.)
+endef
+
+# No more flags from here on
+.KATI_READONLY := _ALL_RELEASE_FLAGS
+
+# -----------------------------------------------------------------
+# Set the flags
+
+# $(1): Flag name. Must start with RELEASE_ and have been defined by declare-build-flag
+# $(2): Value. True or false
+define set-build-flag
+    $(if $(filter-out $(_ALL_RELEASE_FLAGS), $(strip $(1))), \
+        $(error set-build-flag: Undeclared build flag: $(strip $(1))) \
+    )
+    $(eval _ALL_RELEASE_FLAGS.$(strip $(1)).VALUE := $(strip $(2)))
+    $(eval _ALL_RELEASE_FLAGS.$(strip $(1)).SET_IN := $(_included))
+endef
+
+# This writes directly to a file so that the version never exists in make for
+# people to write conditionals upon.
+define set-release-version
+    $(eval _RELEASE_VERSION := $(strip $(1)))
+endef
+
+# Include the files (if there are any)
+ifneq ($(strip $(_release_config_files)),)
+    $(foreach f, $(_release_config_files), \
+        $(eval _included := $(f)) \
+        $(eval include $(f)) \
+    )
+else
+    # No TARGET_RELEASE means release version 0
+    $(call set-release-version, 0)
+endif
+
+
+ifeq ($(_RELEASE_VERSION)),)
+    $(error No release config file called set-release-version. Included files were: $(_release_config_files))
+endif
+
+# Don't let anyone declare build flags after here
+define set-build-flag
+$(error set-build-flag can only be called from inside release config files.)
+endef
+
+# Don't let anyone set the release version after here
+define set-release-version
+$(error set-release-version can only be called from inside release config files.)
+endef
+
+# Set the flag values, and don't allow any one to modify them.
+$(foreach flag, $(_ALL_RELEASE_FLAGS), \
+    $(eval $(flag) := $(_ALL_RELEASE_FLAGS.$(flag).VALUE)) \
+    $(eval .KATI_READONLY := $(flag)) \
+)
+
+
+# -----------------------------------------------------------------
+# Clear out vars
+flag_declaration_files:=
+flag_files:=
+_included:=
+_release_config_files:=
diff --git a/core/sysprop.mk b/core/sysprop.mk
index bd6f3d9..cf4b1f6 100644
--- a/core/sysprop.mk
+++ b/core/sysprop.mk
@@ -174,7 +174,7 @@
   ifeq ($(strip $(HAS_BUILD_NUMBER)),false)
     BF_BUILD_NUMBER := $(BUILD_USERNAME)$$($(DATE_FROM_FILE) +%m%d%H%M)
   else
-    BF_BUILD_NUMBER := $(file <$(BUILD_NUMBER_FILE))
+    BF_BUILD_NUMBER := $$(cat $(SOONG_OUT_DIR)/build_hostname.txt)
   endif
   BUILD_FINGERPRINT := $(PRODUCT_BRAND)/$(TARGET_PRODUCT)/$(TARGET_DEVICE):$(PLATFORM_VERSION)/$(BUILD_ID)/$(BF_BUILD_NUMBER):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)
 endif
@@ -196,6 +196,9 @@
 endif
 
 BUILD_THUMBPRINT_FILE := $(PRODUCT_OUT)/build_thumbprint.txt
+ifeq ($(strip $(HAS_BUILD_NUMBER)),true)
+$(BUILD_THUMBPRINT_FILE): $(BUILD_NUMBER_FILE)
+endif
 ifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_THUMBPRINT) >$(BUILD_THUMBPRINT_FILE) && grep " " $(BUILD_THUMBPRINT_FILE)))
   $(error BUILD_THUMBPRINT cannot contain spaces: "$(file <$(BUILD_THUMBPRINT_FILE))")
 endif
@@ -260,7 +263,11 @@
 endef
 
 gen_from_buildinfo_sh := $(call intermediates-dir-for,PACKAGING,system_build_prop)/buildinfo.prop
-$(gen_from_buildinfo_sh): $(INTERNAL_BUILD_ID_MAKEFILE) $(API_FINGERPRINT) | $(BUILD_DATETIME_FILE) $(BUILD_NUMBER_FILE)
+
+ifeq ($(strip $(HAS_BUILD_NUMBER)),true)
+$(gen_from_buildinfo_sh): $(BUILD_NUMBER_FILE)
+endif
+$(gen_from_buildinfo_sh): $(INTERNAL_BUILD_ID_MAKEFILE) $(API_FINGERPRINT) $(BUILD_HOSTNAME_FILE) | $(BUILD_DATETIME_FILE)
 	$(hide) TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)" \
 	        TARGET_BUILD_FLAVOR="$(TARGET_BUILD_FLAVOR)" \
 	        TARGET_DEVICE="$(TARGET_DEVICE)" \
@@ -271,7 +278,7 @@
 	        BUILD_DISPLAY_ID="$(BUILD_DISPLAY_ID)" \
 	        DATE="$(DATE_FROM_FILE)" \
 	        BUILD_USERNAME="$(BUILD_USERNAME)" \
-	        BUILD_HOSTNAME="$(BUILD_HOSTNAME)" \
+	        BUILD_HOSTNAME="$(BUILD_HOSTNAME_FROM_FILE)" \
 	        BUILD_NUMBER="$(BUILD_NUMBER_FROM_FILE)" \
 	        BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT="$(BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT)" \
 	        PLATFORM_VERSION="$(PLATFORM_VERSION)" \
diff --git a/core/tasks/sdk-addon.mk b/core/tasks/sdk-addon.mk
index 5097f12..7acac72 100644
--- a/core/tasks/sdk-addon.mk
+++ b/core/tasks/sdk-addon.mk
@@ -19,12 +19,13 @@
 addon_name := $(PRODUCT_SDK_ADDON_NAME)
 ifneq ($(addon_name),)
 
-addon_dir_leaf  := $(addon_name)-$(FILE_NAME_TAG)-$(INTERNAL_SDK_HOST_OS_NAME)
-addon_dir_img   := $(addon_dir_leaf)-img
-intermediates   := $(HOST_OUT_INTERMEDIATES)/SDK_ADDON/$(addon_name)_intermediates
-full_target     := $(HOST_OUT_SDK_ADDON)/$(addon_dir_leaf).zip
-full_target_img := $(HOST_OUT_SDK_ADDON)/$(addon_dir_img).zip
-staging         := $(intermediates)
+addon_dir_leaf        := $(addon_name)-$(INTERNAL_SDK_HOST_OS_NAME)
+addon_dir_img         := $(addon_dir_leaf)-img
+intermediates         := $(HOST_OUT_INTERMEDIATES)/SDK_ADDON/$(addon_name)_intermediates
+full_target           := $(HOST_OUT_SDK_ADDON)/$(addon_dir_leaf).zip
+full_target_dist_name := $(addon_name)-FILE_NAME_TAG_PLACEHOLDER-$(INTERNAL_SDK_HOST_OS_NAME)
+full_target_img       := $(HOST_OUT_SDK_ADDON)/$(addon_dir_img).zip
+staging               := $(intermediates)
 
 sdk_addon_deps :=
 files_to_copy :=
@@ -140,7 +141,7 @@
 else
 # When not building an sdk_repo, just dist the addon zip file
 # as-is.
-$(call dist-for-goals, sdk_addon, $(full_target))
+$(call dist-for-goals, sdk_addon, $(full_target):$(full_target_dist_name))
 endif
 
 else # addon_name
diff --git a/core/tasks/tools/build_custom_image.mk b/core/tasks/tools/build_custom_image.mk
index 2626120..ba97e8a 100644
--- a/core/tasks/tools/build_custom_image.mk
+++ b/core/tasks/tools/build_custom_image.mk
@@ -105,6 +105,9 @@
 else ifneq (,$(filter true, $(CUSTOM_IMAGE_AVB_HASH_ENABLE) $(CUSTOM_IMAGE_AVB_HASHTREE_ENABLE)))
   $(error Cannot set both CUSTOM_IMAGE_AVB_HASH_ENABLE and CUSTOM_IMAGE_AVB_HASHTREE_ENABLE to true)
 endif
+ifeq ($(strip $(HAS_BUILD_NUMBER)),true)
+$(my_built_custom_image): $(BUILD_NUMBER_FILE)
+endif
 $(my_built_custom_image): $(INTERNAL_USERIMAGES_DEPS) $(my_built_modules) $(my_image_copy_files) $(my_custom_image_modules_dep) \
   $(CUSTOM_IMAGE_DICT_FILE)
 	@echo "Build image $@"
diff --git a/core/tasks/tools/compatibility.mk b/core/tasks/tools/compatibility.mk
index 8ae2a9a..dd2305e 100644
--- a/core/tasks/tools/compatibility.mk
+++ b/core/tasks/tools/compatibility.mk
@@ -118,6 +118,9 @@
 $(compatibility_zip): PRIVATE_JDK := $(test_suite_jdk)
 $(compatibility_zip): PRIVATE_tests_list := $(out_dir)-tests_list
 $(compatibility_zip): PRIVATE_tests_list_zip := $(compatibility_tests_list_zip)
+ifeq ($(strip $(HAS_BUILD_NUMBER)),true)
+$(compatibility_zip): $(BUILD_NUMBER_FILE)
+endif
 $(compatibility_zip): $(compatibility_zip_deps) | $(ADB) $(ACP)
 # Make dir structure
 	mkdir -p $(PRIVATE_OUT_DIR)/tools $(PRIVATE_OUT_DIR)/testcases
diff --git a/core/tasks/with-license.mk b/core/tasks/with-license.mk
index d41e77a..5ca974a 100644
--- a/core/tasks/with-license.mk
+++ b/core/tasks/with-license.mk
@@ -20,7 +20,8 @@
 	name := $(name)_debug
 endif
 
-name := $(name)-flashable-$(FILE_NAME_TAG)-with-license
+dist_name := $(name)-flashable-FILE_NAME_TAG_PLACEHOLDER-with-license
+name := $(name)-flashable-with-license
 
 with_license_intermediates := \
 	$(call intermediates-dir-for,PACKAGING,with_license)
@@ -42,6 +43,7 @@
 $(call declare-container-deps,$(license_image_input_zip),$(BUILT_TARGET_FILES_PACKAGE))
 
 with_license_zip := $(PRODUCT_OUT)/$(name).sh
+dist_name := $(dist_name).sh
 $(with_license_zip): PRIVATE_NAME := $(name)
 $(with_license_zip): PRIVATE_INPUT_ZIP := $(license_image_input_zip)
 $(with_license_zip): PRIVATE_VENDOR_BLOBS_LICENSE := $(VENDOR_BLOBS_LICENSE)
@@ -51,7 +53,7 @@
 	$(HOST_OUT_EXECUTABLES)/generate-self-extracting-archive $@ \
 		$(PRIVATE_INPUT_ZIP) $(PRIVATE_NAME) $(PRIVATE_VENDOR_BLOBS_LICENSE)
 with-license : $(with_license_zip)
-$(call dist-for-goals, with-license, $(with_license_zip))
+$(call dist-for-goals, with-license, $(with_license_zip):$(dist_name))
 
 $(call declare-1p-container,$(with_license_zip),)
 $(call declare-container-license-deps,$(with_license_zip),$(license_image_input_zip),$(with_license_zip):)
diff --git a/core/version_util.mk b/core/version_util.mk
index 47883d8..d4ce113 100644
--- a/core/version_util.mk
+++ b/core/version_util.mk
@@ -246,21 +246,10 @@
 # to soong_ui.
 $(KATI_obsolete_var BUILD_DATETIME,Use BUILD_DATETIME_FROM_FILE)
 
-HAS_BUILD_NUMBER := true
-ifndef BUILD_NUMBER
-  # BUILD_NUMBER should be set to the source control value that
-  # represents the current state of the source code.  E.g., a
-  # perforce changelist number or a git hash.  Can be an arbitrary string
-  # (to allow for source control that uses something other than numbers),
-  # but must be a single word and a valid file name.
-  #
-  # If no BUILD_NUMBER is set, create a useful "I am an engineering build
-  # from this date/time" value.  Make it start with a non-digit so that
-  # anyone trying to parse it as an integer will probably get "0".
-  BUILD_NUMBER := eng.$(shell echo $${BUILD_USERNAME:0:6}).$(shell $(DATE) +%Y%m%d.%H%M%S)
+ifndef HAS_BUILD_NUMBER
   HAS_BUILD_NUMBER := false
 endif
-.KATI_READONLY := BUILD_NUMBER HAS_BUILD_NUMBER
+.KATI_READONLY := HAS_BUILD_NUMBER
 
 ifndef PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION
   # Used to set minimum supported target sdk version. Apps targeting sdk
diff --git a/envsetup.sh b/envsetup.sh
index ef48249..d292dbb 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -312,7 +312,7 @@
     # would prevent exporting type info from those packages.
     #
     # http://b/266688086
-    export ANDROID_PYTHONPATH=$T/development/python-packages/adb:$T/development/python-packages:
+    export ANDROID_PYTHONPATH=$T/development/python-packages/adb:$T/development/python-packages/gdbrunner:$T/development/python-packages:
     if [ -n $VENDOR_PYTHONPATH ]; then
         ANDROID_PYTHONPATH=$ANDROID_PYTHONPATH$VENDOR_PYTHONPATH
     fi
diff --git a/packaging/distdir.mk b/packaging/distdir.mk
index 264a8b0..c9508af 100644
--- a/packaging/distdir.mk
+++ b/packaging/distdir.mk
@@ -18,10 +18,12 @@
 DIST_GOAL_OUTPUT_PAIRS :=
 DIST_SRC_DST_PAIRS :=
 include $(KATI_PACKAGE_MK_DIR)/dist.mk
+FILE_NAME_TAG := $(file <$(OUT_DIR)/file_name_tag.txt)
+.KATI_READONLY := FILE_NAME_TAG
 
 $(foreach pair,$(DIST_GOAL_OUTPUT_PAIRS), \
   $(eval goal := $(call word-colon,1,$(pair))) \
-  $(eval output := $(call word-colon,2,$(pair))) \
+  $(eval output := $(subst FILE_NAME_TAG_PLACEHOLDER,$(FILE_NAME_TAG),$(call word-colon,2,$(pair)))) \
   $(eval .PHONY: _dist_$$(goal)) \
   $(if $(call streq,$(DIST),true),\
     $(eval _dist_$$(goal): $$(DIST_DIR)/$$(output)), \
@@ -37,7 +39,7 @@
 ifeq ($(DIST),true)
   $(foreach pair,$(DIST_SRC_DST_PAIRS), \
     $(eval src := $(call word-colon,1,$(pair))) \
-    $(eval dst := $(DIST_DIR)/$(call word-colon,2,$(pair))) \
+    $(eval dst := $(subst FILE_NAME_TAG_PLACEHOLDER,$(FILE_NAME_TAG),$(DIST_DIR)/$(call word-colon,2,$(pair)))) \
     $(eval $(call copy-one-dist-file,$(src),$(dst))))
 endif
 
diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt
index 474cb20..2df85e5 100644
--- a/target/product/gsi/current.txt
+++ b/target/product/gsi/current.txt
@@ -21,6 +21,7 @@
 LLNDK: libvulkan.so
 VNDK-SP: android.hardware.common-V2-ndk.so
 VNDK-SP: android.hardware.common.fmq-V1-ndk.so
+VNDK-SP: android.hardware.graphics.allocator-V2-ndk.so
 VNDK-SP: android.hardware.graphics.common-V4-ndk.so
 VNDK-SP: android.hardware.graphics.common@1.0.so
 VNDK-SP: android.hardware.graphics.common@1.1.so
@@ -30,7 +31,6 @@
 VNDK-SP: android.hardware.graphics.mapper@2.1.so
 VNDK-SP: android.hardware.graphics.mapper@3.0.so
 VNDK-SP: android.hardware.graphics.mapper@4.0.so
-VNDK-SP: android.hardware.graphics.allocator-V2-ndk.so
 VNDK-SP: android.hardware.renderscript@1.0.so
 VNDK-SP: android.hidl.memory.token@1.0.so
 VNDK-SP: android.hidl.memory@1.0-impl.so
@@ -90,7 +90,6 @@
 VNDK-core: libcrypto.so
 VNDK-core: libcrypto_utils.so
 VNDK-core: libcurl.so
-VNDK-core: libdiskconfig.so
 VNDK-core: libdumpstateutil.so
 VNDK-core: libevent.so
 VNDK-core: libexif.so
diff --git a/tools/aconfig/src/cache.rs b/tools/aconfig/src/cache.rs
index c546f7b..30810fa 100644
--- a/tools/aconfig/src/cache.rs
+++ b/tools/aconfig/src/cache.rs
@@ -51,72 +51,42 @@
     items: Vec<Item>,
 }
 
-impl Cache {
-    pub fn new(namespace: String) -> Result<Cache> {
-        ensure!(!namespace.is_empty(), "empty namespace");
-        Ok(Cache { namespace, items: vec![] })
+// TODO: replace this function with Iterator.is_sorted_by_key(...)) when that API becomes stable
+fn iter_is_sorted_by_key<'a, T: 'a, F, K>(iter: impl Iterator<Item = &'a T>, f: F) -> bool
+where
+    F: FnMut(&'a T) -> K,
+    K: PartialOrd<K>,
+{
+    let mut last: Option<K> = None;
+    for current in iter.map(f) {
+        if let Some(l) = last {
+            if l > current {
+                return false;
+            }
+        }
+        last = Some(current);
     }
+    true
+}
 
+impl Cache {
     pub fn read_from_reader(reader: impl Read) -> Result<Cache> {
-        serde_json::from_reader(reader).map_err(|e| e.into())
+        let cache: Cache = serde_json::from_reader(reader)?;
+        ensure!(
+            iter_is_sorted_by_key(cache.iter(), |item| &item.name),
+            "internal error: flags in cache file not sorted"
+        );
+        Ok(cache)
     }
 
     pub fn write_to_writer(&self, writer: impl Write) -> Result<()> {
+        ensure!(
+            iter_is_sorted_by_key(self.iter(), |item| &item.name),
+            "internal error: flags in cache file not sorted"
+        );
         serde_json::to_writer(writer, self).map_err(|e| e.into())
     }
 
-    pub fn add_flag_declaration(
-        &mut self,
-        source: Source,
-        declaration: FlagDeclaration,
-    ) -> Result<()> {
-        ensure!(!declaration.name.is_empty(), "empty flag name");
-        ensure!(!declaration.description.is_empty(), "empty flag description");
-        ensure!(
-            self.items.iter().all(|item| item.name != declaration.name),
-            "failed to declare flag {} from {}: flag already declared",
-            declaration.name,
-            source
-        );
-        self.items.push(Item {
-            namespace: self.namespace.clone(),
-            name: declaration.name.clone(),
-            description: declaration.description,
-            state: DEFAULT_FLAG_STATE,
-            permission: DEFAULT_FLAG_PERMISSION,
-            trace: vec![Tracepoint {
-                source,
-                state: DEFAULT_FLAG_STATE,
-                permission: DEFAULT_FLAG_PERMISSION,
-            }],
-        });
-        Ok(())
-    }
-
-    pub fn add_flag_value(&mut self, source: Source, value: FlagValue) -> Result<()> {
-        ensure!(!value.namespace.is_empty(), "empty flag namespace");
-        ensure!(!value.name.is_empty(), "empty flag name");
-        ensure!(
-            value.namespace == self.namespace,
-            "failed to set values for flag {}/{} from {}: expected namespace {}",
-            value.namespace,
-            value.name,
-            source,
-            self.namespace
-        );
-        let Some(existing_item) = self.items.iter_mut().find(|item| item.name == value.name) else {
-            bail!("failed to set values for flag {}/{} from {}: flag not declared", value.namespace, value.name, source);
-        };
-        existing_item.state = value.state;
-        existing_item.permission = value.permission;
-        existing_item.trace.push(Tracepoint {
-            source,
-            state: value.state,
-            permission: value.permission,
-        });
-        Ok(())
-    }
-
     pub fn iter(&self) -> impl Iterator<Item = &Item> {
         self.items.iter()
     }
@@ -131,6 +101,80 @@
     }
 }
 
+#[derive(Debug)]
+pub struct CacheBuilder {
+    cache: Cache,
+}
+
+impl CacheBuilder {
+    pub fn new(namespace: String) -> Result<CacheBuilder> {
+        ensure!(!namespace.is_empty(), "empty namespace");
+        let cache = Cache { namespace, items: vec![] };
+        Ok(CacheBuilder { cache })
+    }
+
+    pub fn add_flag_declaration(
+        &mut self,
+        source: Source,
+        declaration: FlagDeclaration,
+    ) -> Result<&mut CacheBuilder> {
+        ensure!(!declaration.name.is_empty(), "empty flag name");
+        ensure!(!declaration.description.is_empty(), "empty flag description");
+        ensure!(
+            self.cache.items.iter().all(|item| item.name != declaration.name),
+            "failed to declare flag {} from {}: flag already declared",
+            declaration.name,
+            source
+        );
+        self.cache.items.push(Item {
+            namespace: self.cache.namespace.clone(),
+            name: declaration.name.clone(),
+            description: declaration.description,
+            state: DEFAULT_FLAG_STATE,
+            permission: DEFAULT_FLAG_PERMISSION,
+            trace: vec![Tracepoint {
+                source,
+                state: DEFAULT_FLAG_STATE,
+                permission: DEFAULT_FLAG_PERMISSION,
+            }],
+        });
+        Ok(self)
+    }
+
+    pub fn add_flag_value(
+        &mut self,
+        source: Source,
+        value: FlagValue,
+    ) -> Result<&mut CacheBuilder> {
+        ensure!(!value.namespace.is_empty(), "empty flag namespace");
+        ensure!(!value.name.is_empty(), "empty flag name");
+        ensure!(
+            value.namespace == self.cache.namespace,
+            "failed to set values for flag {}/{} from {}: expected namespace {}",
+            value.namespace,
+            value.name,
+            source,
+            self.cache.namespace
+        );
+        let Some(existing_item) = self.cache.items.iter_mut().find(|item| item.name == value.name) else {
+            bail!("failed to set values for flag {}/{} from {}: flag not declared", value.namespace, value.name, source);
+        };
+        existing_item.state = value.state;
+        existing_item.permission = value.permission;
+        existing_item.trace.push(Tracepoint {
+            source,
+            state: value.state,
+            permission: value.permission,
+        });
+        Ok(self)
+    }
+
+    pub fn build(mut self) -> Cache {
+        self.cache.items.sort_by_cached_key(|item| item.name.clone());
+        self.cache
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -138,14 +182,14 @@
 
     #[test]
     fn test_add_flag_declaration() {
-        let mut cache = Cache::new("ns".to_string()).unwrap();
-        cache
+        let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
+        builder
             .add_flag_declaration(
                 Source::File("first.txt".to_string()),
                 FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
             )
             .unwrap();
-        let error = cache
+        let error = builder
             .add_flag_declaration(
                 Source::File("second.txt".to_string()),
                 FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
@@ -155,17 +199,26 @@
             &format!("{:?}", error),
             "failed to declare flag foo from second.txt: flag already declared"
         );
+        builder
+            .add_flag_declaration(
+                Source::File("first.txt".to_string()),
+                FlagDeclaration { name: "bar".to_string(), description: "desc".to_string() },
+            )
+            .unwrap();
+
+        let cache = builder.build();
+
+        // check flags are sorted by name
+        assert_eq!(
+            cache.into_iter().map(|item| item.name).collect::<Vec<_>>(),
+            vec!["bar".to_string(), "foo".to_string()]
+        );
     }
 
     #[test]
     fn test_add_flag_value() {
-        fn check(cache: &Cache, name: &str, expected: (FlagState, Permission)) -> bool {
-            let item = cache.iter().find(|&item| item.name == name).unwrap();
-            item.state == expected.0 && item.permission == expected.1
-        }
-
-        let mut cache = Cache::new("ns".to_string()).unwrap();
-        let error = cache
+        let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
+        let error = builder
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
@@ -181,15 +234,14 @@
             "failed to set values for flag ns/foo from <memory>: flag not declared"
         );
 
-        cache
+        builder
             .add_flag_declaration(
                 Source::File("first.txt".to_string()),
                 FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
             )
             .unwrap();
-        assert!(check(&cache, "foo", (DEFAULT_FLAG_STATE, DEFAULT_FLAG_PERMISSION)));
 
-        cache
+        builder
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
@@ -200,9 +252,8 @@
                 },
             )
             .unwrap();
-        assert!(check(&cache, "foo", (FlagState::Disabled, Permission::ReadOnly)));
 
-        cache
+        builder
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
@@ -213,10 +264,9 @@
                 },
             )
             .unwrap();
-        assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
 
         // different namespace -> no-op
-        let error = cache
+        let error = builder
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
@@ -228,19 +278,23 @@
             )
             .unwrap_err();
         assert_eq!(&format!("{:?}", error), "failed to set values for flag some-other-namespace/foo from <memory>: expected namespace ns");
-        assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
+
+        let cache = builder.build();
+        let item = cache.iter().find(|&item| item.name == "foo").unwrap();
+        assert_eq!(FlagState::Enabled, item.state);
+        assert_eq!(Permission::ReadWrite, item.permission);
     }
 
     #[test]
     fn test_reject_empty_cache_namespace() {
-        Cache::new("".to_string()).unwrap_err();
+        CacheBuilder::new("".to_string()).unwrap_err();
     }
 
     #[test]
     fn test_reject_empty_flag_declaration_fields() {
-        let mut cache = Cache::new("ns".to_string()).unwrap();
+        let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
 
-        let error = cache
+        let error = builder
             .add_flag_declaration(
                 Source::Memory,
                 FlagDeclaration { name: "".to_string(), description: "Description".to_string() },
@@ -248,7 +302,7 @@
             .unwrap_err();
         assert_eq!(&format!("{:?}", error), "empty flag name");
 
-        let error = cache
+        let error = builder
             .add_flag_declaration(
                 Source::Memory,
                 FlagDeclaration { name: "foo".to_string(), description: "".to_string() },
@@ -259,15 +313,15 @@
 
     #[test]
     fn test_reject_empty_flag_value_files() {
-        let mut cache = Cache::new("ns".to_string()).unwrap();
-        cache
+        let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
+        builder
             .add_flag_declaration(
                 Source::Memory,
                 FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
             )
             .unwrap();
 
-        let error = cache
+        let error = builder
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
@@ -280,7 +334,7 @@
             .unwrap_err();
         assert_eq!(&format!("{:?}", error), "empty flag namespace");
 
-        let error = cache
+        let error = builder
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
@@ -293,4 +347,11 @@
             .unwrap_err();
         assert_eq!(&format!("{:?}", error), "empty flag name");
     }
+
+    #[test]
+    fn test_iter_is_sorted_by_key() {
+        assert!(iter_is_sorted_by_key(["a", "b", "c"].iter(), |s| s));
+        assert!(iter_is_sorted_by_key(Vec::<&str>::new().iter(), |s| s));
+        assert!(!iter_is_sorted_by_key(["a", "c", "b"].iter(), |s| s));
+    }
 }
diff --git a/tools/aconfig/src/codegen_cpp.rs b/tools/aconfig/src/codegen_cpp.rs
index cb266f1..2aeea6a 100644
--- a/tools/aconfig/src/codegen_cpp.rs
+++ b/tools/aconfig/src/codegen_cpp.rs
@@ -64,13 +64,14 @@
 mod tests {
     use super::*;
     use crate::aconfig::{FlagDeclaration, FlagState, FlagValue, Permission};
+    use crate::cache::CacheBuilder;
     use crate::commands::Source;
 
     #[test]
     fn test_cpp_codegen_build_time_flag_only() {
         let namespace = "my_namespace";
-        let mut cache = Cache::new(namespace.to_string()).unwrap();
-        cache
+        let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
+        builder
             .add_flag_declaration(
                 Source::File("aconfig_one.txt".to_string()),
                 FlagDeclaration {
@@ -78,8 +79,7 @@
                     description: "buildtime disable".to_string(),
                 },
             )
-            .unwrap();
-        cache
+            .unwrap()
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
@@ -89,8 +89,7 @@
                     permission: Permission::ReadOnly,
                 },
             )
-            .unwrap();
-        cache
+            .unwrap()
             .add_flag_declaration(
                 Source::File("aconfig_two.txt".to_string()),
                 FlagDeclaration {
@@ -98,8 +97,7 @@
                     description: "buildtime enable".to_string(),
                 },
             )
-            .unwrap();
-        cache
+            .unwrap()
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
@@ -110,6 +108,7 @@
                 },
             )
             .unwrap();
+        let cache = builder.build();
         let expect_content = r#"#ifndef my_namespace_HEADER_H
         #define my_namespace_HEADER_H
         #include "my_namespace.h"
@@ -144,8 +143,8 @@
     #[test]
     fn test_cpp_codegen_runtime_flag() {
         let namespace = "my_namespace";
-        let mut cache = Cache::new(namespace.to_string()).unwrap();
-        cache
+        let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
+        builder
             .add_flag_declaration(
                 Source::File("aconfig_one.txt".to_string()),
                 FlagDeclaration {
@@ -153,8 +152,7 @@
                     description: "buildtime disable".to_string(),
                 },
             )
-            .unwrap();
-        cache
+            .unwrap()
             .add_flag_declaration(
                 Source::File("aconfig_two.txt".to_string()),
                 FlagDeclaration {
@@ -162,8 +160,7 @@
                     description: "runtime enable".to_string(),
                 },
             )
-            .unwrap();
-        cache
+            .unwrap()
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
@@ -174,6 +171,7 @@
                 },
             )
             .unwrap();
+        let cache = builder.build();
         let expect_content = r#"#ifndef my_namespace_HEADER_H
         #define my_namespace_HEADER_H
         #include "my_namespace.h"
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index 476a89d..733b1c5 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -71,13 +71,14 @@
 mod tests {
     use super::*;
     use crate::aconfig::{FlagDeclaration, FlagValue};
+    use crate::cache::CacheBuilder;
     use crate::commands::Source;
 
     #[test]
     fn test_generate_java_code() {
         let namespace = "com.example";
-        let mut cache = Cache::new(namespace.to_string()).unwrap();
-        cache
+        let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
+        builder
             .add_flag_declaration(
                 Source::File("test.txt".to_string()),
                 FlagDeclaration {
@@ -85,8 +86,7 @@
                     description: "buildtime enable".to_string(),
                 },
             )
-            .unwrap();
-        cache
+            .unwrap()
             .add_flag_declaration(
                 Source::File("test2.txt".to_string()),
                 FlagDeclaration {
@@ -94,8 +94,7 @@
                     description: "runtime disable".to_string(),
                 },
             )
-            .unwrap();
-        cache
+            .unwrap()
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
@@ -106,6 +105,7 @@
                 },
             )
             .unwrap();
+        let cache = builder.build();
         let expect_content = r#"package com.example;
 
         import android.provider.DeviceConfig;
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index 0bdb0b5..22de331 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -23,7 +23,7 @@
 use std::path::PathBuf;
 
 use crate::aconfig::{FlagDeclarations, FlagValue};
-use crate::cache::Cache;
+use crate::cache::{Cache, CacheBuilder};
 use crate::codegen_cpp::generate_cpp_code;
 use crate::codegen_java::generate_java_code;
 use crate::protos::ProtoParsedFlags;
@@ -59,7 +59,7 @@
     declarations: Vec<Input>,
     values: Vec<Input>,
 ) -> Result<Cache> {
-    let mut cache = Cache::new(namespace.to_owned())?;
+    let mut builder = CacheBuilder::new(namespace.to_owned())?;
 
     for mut input in declarations {
         let mut contents = String::new();
@@ -74,7 +74,7 @@
             dec_list.namespace
         );
         for d in dec_list.flags.into_iter() {
-            cache.add_flag_declaration(input.source.clone(), d)?;
+            builder.add_flag_declaration(input.source.clone(), d)?;
         }
     }
 
@@ -85,11 +85,11 @@
             .with_context(|| format!("Failed to parse {}", input.source))?;
         for v in values_list {
             // TODO: warn about flag values that do not take effect?
-            let _ = cache.add_flag_value(input.source.clone(), v);
+            let _ = builder.add_flag_value(input.source.clone(), v);
         }
     }
 
-    Ok(cache)
+    Ok(builder.build())
 }
 
 pub fn create_java_lib(cache: &Cache) -> Result<OutputFile> {
@@ -107,29 +107,35 @@
     Protobuf,
 }
 
-pub fn dump_cache(cache: Cache, format: DumpFormat) -> Result<Vec<u8>> {
-    match format {
-        DumpFormat::Text => {
-            let mut lines = vec![];
-            for item in cache.iter() {
-                lines.push(format!("{}: {:?}\n", item.name, item.state));
+pub fn dump_cache(mut caches: Vec<Cache>, format: DumpFormat) -> Result<Vec<u8>> {
+    let mut output = Vec::new();
+    caches.sort_by_cached_key(|cache| cache.namespace().to_string());
+    for cache in caches.into_iter() {
+        match format {
+            DumpFormat::Text => {
+                let mut lines = vec![];
+                for item in cache.iter() {
+                    lines.push(format!(
+                        "{}/{}: {:?} {:?}\n",
+                        item.namespace, item.name, item.state, item.permission
+                    ));
+                }
+                output.append(&mut lines.concat().into());
             }
-            Ok(lines.concat().into())
-        }
-        DumpFormat::Debug => {
-            let mut lines = vec![];
-            for item in cache.iter() {
-                lines.push(format!("{:?}\n", item));
+            DumpFormat::Debug => {
+                let mut lines = vec![];
+                for item in cache.iter() {
+                    lines.push(format!("{:?}\n", item));
+                }
+                output.append(&mut lines.concat().into());
             }
-            Ok(lines.concat().into())
-        }
-        DumpFormat::Protobuf => {
-            let parsed_flags: ProtoParsedFlags = cache.into();
-            let mut output = vec![];
-            parsed_flags.write_to_vec(&mut output)?;
-            Ok(output)
+            DumpFormat::Protobuf => {
+                let parsed_flags: ProtoParsedFlags = cache.into();
+                parsed_flags.write_to_vec(&mut output)?;
+            }
         }
     }
+    Ok(output)
 }
 
 #[cfg(test)]
@@ -137,9 +143,9 @@
     use super::*;
     use crate::aconfig::{FlagState, Permission};
 
-    fn create_test_cache() -> Cache {
+    fn create_test_cache_ns1() -> Cache {
         let s = r#"
-        namespace: "ns"
+        namespace: "ns1"
         flag {
             name: "a"
             description: "Description of a"
@@ -152,28 +158,49 @@
         let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
         let o = r#"
         flag_value {
-            namespace: "ns"
+            namespace: "ns1"
             name: "a"
             state: DISABLED
             permission: READ_ONLY
         }
         "#;
         let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
-        create_cache("ns", declarations, values).unwrap()
+        create_cache("ns1", declarations, values).unwrap()
+    }
+
+    fn create_test_cache_ns2() -> Cache {
+        let s = r#"
+        namespace: "ns2"
+        flag {
+            name: "c"
+            description: "Description of c"
+        }
+        "#;
+        let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
+        let o = r#"
+        flag_value {
+            namespace: "ns2"
+            name: "c"
+            state: DISABLED
+            permission: READ_ONLY
+        }
+        "#;
+        let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
+        create_cache("ns2", declarations, values).unwrap()
     }
 
     #[test]
     fn test_create_cache() {
-        let cache = create_test_cache(); // calls create_cache
-        let item = cache.iter().find(|&item| item.name == "a").unwrap();
+        let caches = create_test_cache_ns1(); // calls create_cache
+        let item = caches.iter().find(|&item| item.name == "a").unwrap();
         assert_eq!(FlagState::Disabled, item.state);
         assert_eq!(Permission::ReadOnly, item.permission);
     }
 
     #[test]
     fn test_dump_text_format() {
-        let cache = create_test_cache();
-        let bytes = dump_cache(cache, DumpFormat::Text).unwrap();
+        let caches = vec![create_test_cache_ns1()];
+        let bytes = dump_cache(caches, DumpFormat::Text).unwrap();
         let text = std::str::from_utf8(&bytes).unwrap();
         assert!(text.contains("a: Disabled"));
     }
@@ -183,8 +210,8 @@
         use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint};
         use protobuf::Message;
 
-        let cache = create_test_cache();
-        let bytes = dump_cache(cache, DumpFormat::Protobuf).unwrap();
+        let caches = vec![create_test_cache_ns1()];
+        let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
         let actual = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
 
         assert_eq!(
@@ -194,7 +221,7 @@
 
         let item =
             actual.parsed_flag.iter().find(|item| item.name == Some("b".to_string())).unwrap();
-        assert_eq!(item.namespace(), "ns");
+        assert_eq!(item.namespace(), "ns1");
         assert_eq!(item.name(), "b");
         assert_eq!(item.description(), "Description of b");
         assert_eq!(item.state(), ProtoFlagState::DISABLED);
@@ -205,4 +232,23 @@
         tp.set_permission(ProtoFlagPermission::READ_WRITE);
         assert_eq!(item.trace, vec![tp]);
     }
+
+    #[test]
+    fn test_dump_multiple_caches() {
+        let caches = vec![create_test_cache_ns1(), create_test_cache_ns2()];
+        let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
+        let dump = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
+        assert_eq!(
+            dump.parsed_flag
+                .iter()
+                .map(|parsed_flag| format!("{}/{}", parsed_flag.namespace(), parsed_flag.name()))
+                .collect::<Vec<_>>(),
+            vec!["ns1/a".to_string(), "ns1/b".to_string(), "ns2/c".to_string()]
+        );
+
+        let caches = vec![create_test_cache_ns2(), create_test_cache_ns1()];
+        let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
+        let dump_reversed_input = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
+        assert_eq!(dump, dump_reversed_input);
+    }
 }
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index 6db5948..d02307d 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -56,7 +56,7 @@
         )
         .subcommand(
             Command::new("dump")
-                .arg(Arg::new("cache").long("cache").required(true))
+                .arg(Arg::new("cache").long("cache").action(ArgAction::Append).required(true))
                 .arg(
                     Arg::new("format")
                         .long("format")
@@ -130,11 +130,14 @@
             write_output_file_realtive_to_dir(&dir, &generated_file)?;
         }
         Some(("dump", sub_matches)) => {
-            let path = get_required_arg::<String>(sub_matches, "cache")?;
-            let file = fs::File::open(path)?;
-            let cache = Cache::read_from_reader(file)?;
+            let mut caches = Vec::new();
+            for path in sub_matches.get_many::<String>("cache").unwrap_or_default() {
+                let file = fs::File::open(path)?;
+                let cache = Cache::read_from_reader(file)?;
+                caches.push(cache);
+            }
             let format = get_required_arg::<DumpFormat>(sub_matches, "format")?;
-            let output = commands::dump_cache(cache, *format)?;
+            let output = commands::dump_cache(caches, *format)?;
             let path = get_required_arg::<String>(sub_matches, "out")?;
             let mut file: Box<dyn Write> = if *path == "-" {
                 Box::new(io::stdout())
diff --git a/tools/releasetools/img_from_target_files.py b/tools/releasetools/img_from_target_files.py
index 7c27ef7..17f4cc5 100755
--- a/tools/releasetools/img_from_target_files.py
+++ b/tools/releasetools/img_from_target_files.py
@@ -122,10 +122,14 @@
   ]
   with zipfile.ZipFile(input_file) as input_zip:
     namelist = input_zip.namelist()
+  if 'PREBUILT_IMAGES/kernel_16k' in namelist:
+    entries.append('PREBUILT_IMAGES/kernel_16k:kernel_16k')
+  if 'PREBUILT_IMAGES/ramdisk_16k.img' in namelist:
+    entries.append('PREBUILT_IMAGES/ramdisk_16k.img:ramdisk_16k.img')
 
   for image_path in [name for name in namelist if name.startswith('IMAGES/')]:
     image = os.path.basename(image_path)
-    if OPTIONS.bootable_only and image not in('boot.img', 'recovery.img', 'bootloader', 'init_boot.img'):
+    if OPTIONS.bootable_only and image not in ('boot.img', 'recovery.img', 'bootloader', 'init_boot.img'):
       continue
     if not image.endswith('.img') and image != 'bootloader':
       continue
@@ -173,8 +177,8 @@
 
   logger.info('Writing super.img to archive...')
   with zipfile.ZipFile(
-      output_file, 'a', compression=zipfile.ZIP_DEFLATED,
-      allowZip64=True) as output_zip:
+          output_file, 'a', compression=zipfile.ZIP_DEFLATED,
+          allowZip64=True) as output_zip:
     common.ZipWrite(output_zip, super_file, 'super.img')