Merge "Add dt partition in fastboot-info"
diff --git a/core/Makefile b/core/Makefile
index 23bbe19..1814f98 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -27,26 +27,28 @@
 $(eval $(strip $(1)): PRIVATE_FLAG_NAMES := $(strip $(2)))
 $(strip $(1)):
 	mkdir -p $$(dir $$(PRIVATE_OUT))
-	( \
-		echo '{' ; \
-		echo 'flags: [' ; \
-		$$(foreach flag, $$(PRIVATE_FLAG_NAMES), \
+	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", }' \
+			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' ; \
-		) \
-		echo "]" ; \
-		echo "}" \
-	) >> $$(PRIVATE_OUT)
+		) >> $$(PRIVATE_OUT) \
+	)
+	echo "]" >> $$(PRIVATE_OUT)
+	echo "}" >> $$(PRIVATE_OUT)
 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) \
@@ -5885,11 +5887,22 @@
     echo "virtual_ab_cow_version=$(PRODUCT_VIRTUAL_AB_COW_VERSION)" >> $(1))
 endef
 
+# Copy an image file to a directory and generate a block list map file from the image.
+# $(1): path of the image file
+# $(2): target out directory
+# $(3): name of the map file. skip generating map file if empty
+define copy-image-and-generate-map
+  mkdir -p $(2)
+  cp $(1) $(2)
+  $(if $(3),$(HOST_OUT_EXECUTABLES)/map_file_generator $(1) $(2)/$(3))
+endef
+
 # By conditionally including the dependency of the target files package on the
 # full system image deps, we speed up builds that do not build the system
 # image.
 ifdef BUILDING_SYSTEM_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(FULL_SYSTEMIMAGE_DEPS)
+  $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEMIMAGE)
 else
   # releasetools may need the system build.prop even when building a
   # system-image-less product.
@@ -5902,6 +5915,7 @@
 
 ifdef BUILDING_SYSTEM_OTHER_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(INTERNAL_SYSTEMOTHERIMAGE_FILES)
+  $(BUILT_TARGET_FILES_DIR): $(BUILT_SYSTEMOTHERIMAGE_TARGET)
 endif
 
 ifdef BUILDING_VENDOR_BOOT_IMAGE
@@ -5933,18 +5947,21 @@
 
 ifdef BUILDING_VENDOR_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDORIMAGE_FILES)
+  $(BUILT_TARGET_FILES_DIR): $(BUILT_VENDORIMAGE_TARGET)
 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)
 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)
 else ifdef BOARD_PREBUILT_SYSTEM_EXTIMAGE
   $(BUILT_TARGET_FILES_DIR): $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)
 endif
@@ -5959,24 +5976,28 @@
 
 ifdef BUILDING_ODM_IMAGE
   $(BUILT_TARGET_FILES_DIR): $(INTERNAL_ODMIMAGE_FILES)
+  $(BUILT_TARGET_FILES_DIR): $(BUILT_ODMIMAGE_TARGET)
 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)
 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)
 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)
 else ifdef BOARD_PREBUILT_SYSTEM_DLKMIMAGE
   $(BUILT_TARGET_FILES_DIR): $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)
 endif
@@ -6028,6 +6049,7 @@
 	    $(SOONG_APEX_KEYS_FILE) \
 	    $(SOONG_ZIP) \
 	    $(HOST_OUT_EXECUTABLES)/fs_config \
+	    $(HOST_OUT_EXECUTABLES)/map_file_generator \
 	    $(ADD_IMG_TO_TARGET_FILES) \
 	    $(MAKE_RECOVERY_PATCH) \
 	    $(BUILT_KERNEL_CONFIGS_FILE) \
@@ -6384,27 +6406,35 @@
 	@# Run fs_config on all the system, vendor, boot ramdisk,
 	@# and recovery ramdisk files in the zip, and save the output
 ifdef BUILDING_SYSTEM_IMAGE
+	$(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEMIMAGE),$(zip_root)/IMAGES,system.map)
 	$(hide) $(call fs_config,$(zip_root)/SYSTEM,system/) > $(zip_root)/META/filesystem_config.txt
 endif
 ifdef BUILDING_VENDOR_IMAGE
+	$(hide) $(call copy-image-and-generate-map,$(BUILT_VENDORIMAGE_TARGET),$(zip_root)/IMAGES,vendor.map)
 	$(hide) $(call fs_config,$(zip_root)/VENDOR,vendor/) > $(zip_root)/META/vendor_filesystem_config.txt
 endif
 ifdef BUILDING_PRODUCT_IMAGE
+	$(hide) $(call copy-image-and-generate-map,$(BUILT_PRODUCTIMAGE_TARGET),$(zip_root)/IMAGES,product.map)
 	$(hide) $(call fs_config,$(zip_root)/PRODUCT,product/) > $(zip_root)/META/product_filesystem_config.txt
 endif
 ifdef BUILDING_SYSTEM_EXT_IMAGE
+	$(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEM_EXTIMAGE_TARGET),$(zip_root)/IMAGES,system_ext.map)
 	$(hide) $(call fs_config,$(zip_root)/SYSTEM_EXT,system_ext/) > $(zip_root)/META/system_ext_filesystem_config.txt
 endif
 ifdef BUILDING_ODM_IMAGE
+	$(hide) $(call copy-image-and-generate-map,$(BUILT_ODMIMAGE_TARGET),$(zip_root)/IMAGES,odm.map)
 	$(hide) $(call fs_config,$(zip_root)/ODM,odm/) > $(zip_root)/META/odm_filesystem_config.txt
 endif
 ifdef BUILDING_VENDOR_DLKM_IMAGE
+	$(hide)$(call copy-image-and-generate-map,$(BUILT_VENDOR_DLKMIMAGE_TARGET),$(zip_root)/IMAGES,vendor_dlkm.map)
 	$(hide) $(call fs_config,$(zip_root)/VENDOR_DLKM,vendor_dlkm/) > $(zip_root)/META/vendor_dlkm_filesystem_config.txt
 endif
 ifdef BUILDING_ODM_DLKM_IMAGE
+	$(hide) $(call copy-image-and-generate-map,$(BUILT_ODM_DLKMIMAGE_TARGET),$(zip_root)/IMAGES,odm_dlkm.map)
 	$(hide) $(call fs_config,$(zip_root)/ODM_DLKM,odm_dlkm/) > $(zip_root)/META/odm_dlkm_filesystem_config.txt
 endif
 ifdef BUILDING_SYSTEM_DLKM_IMAGE
+	$(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEM_DLKMIMAGE_TARGET),$(zip_root)/IMAGES,system_dlkm.map)
 	$(hide) $(call fs_config,$(zip_root)/SYSTEM_DLKM,system_dlkm/) > $(zip_root)/META/system_dlkm_filesystem_config.txt
 endif
 	@# ROOT always contains the files for the root under normal boot.
@@ -6426,6 +6456,7 @@
 	$(hide) $(call fs_config,$(zip_root)/RECOVERY/RAMDISK,) > $(zip_root)/META/recovery_filesystem_config.txt
 endif
 ifdef BUILDING_SYSTEM_OTHER_IMAGE
+	$(hide) $(call copy-image-and-generate-map,$(BUILT_SYSTEMOTHERIMAGE_TARGET),$(zip_root)/IMAGES)
 	$(hide) $(call fs_config,$(zip_root)/SYSTEM_OTHER,system/) > $(zip_root)/META/system_other_filesystem_config.txt
 endif
 	@# Metadata for compatibility verification.
@@ -6491,6 +6522,7 @@
             $(APKCERTS_FILE) \
             $(SOONG_APEX_KEYS_FILE) \
             $(HOST_OUT_EXECUTABLES)/fs_config \
+            $(HOST_OUT_EXECUTABLES)/map_file_generator \
             $(ADD_IMG_TO_TARGET_FILES) \
             $(MAKE_RECOVERY_PATCH) \
             $(BUILT_KERNEL_CONFIGS_FILE) \
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 6ba539c..5dba2d1 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -154,6 +154,10 @@
 $(call add_soong_config_var_value,ANDROID,avf_enabled,$(PRODUCT_AVF_ENABLED))
 endif
 
+ifdef PRODUCT_AVF_KERNEL_MODULES_ENABLED
+$(call add_soong_config_var_value,ANDROID,avf_kernel_modules_enabled,$(PRODUCT_AVF_KERNEL_MODULES_ENABLED))
+endif
+
 # 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/binary.mk b/core/binary.mk
index 579e6b5..e2e5be4 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -1572,15 +1572,10 @@
 else ifdef LOCAL_SDK_VERSION
   my_target_global_c_includes :=
   my_target_global_c_system_includes := $(my_ndk_stl_include_path) $(my_ndk_sysroot_include)
-else ifdef BOARD_VNDK_VERSION
-  my_target_global_c_includes := $(SRC_HEADERS) \
-    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES)
-  my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \
-    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES)
 else
   my_target_global_c_includes := $(SRC_HEADERS) \
     $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES)
-  my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) $(TARGET_OUT_HEADERS) \
+  my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \
     $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES)
 endif
 
@@ -1667,14 +1662,8 @@
 
 ifdef LOCAL_USE_VNDK
   imported_includes += $(call intermediates-dir-for,HEADER_LIBRARIES,device_kernel_headers,$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))
-else ifdef LOCAL_SDK_VERSION
-  # Apps shouldn't need device-specific kernel headers
-else ifdef BOARD_VNDK_VERSION
-  # For devices building with the VNDK, only the VNDK gets device-specific kernel headers by default
-  # In soong, it's entirely opt-in
 else
-  # For older non-VNDK builds, continue adding in kernel headers to everything like we used to
-  imported_includes += $(call intermediates-dir-for,HEADER_LIBRARIES,device_kernel_headers,$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))
+  # everything else should manually specify headers
 endif
 
 imported_includes := $(strip \
diff --git a/core/board_config.mk b/core/board_config.mk
index f459d83..663ec7c 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -968,27 +968,13 @@
   $(if $(wildcard $(vndk_path)/*/Android.bp),,$(error VNDK version $(1) not found))
 endef
 
-ifdef BOARD_VNDK_VERSION
-  ifeq ($(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION))
-    $(error BOARD_VNDK_VERSION is equal to PLATFORM_VNDK_VERSION; use BOARD_VNDK_VERSION := current)
-  endif
-  ifneq ($(BOARD_VNDK_VERSION),current)
-    $(call check_vndk_version,$(BOARD_VNDK_VERSION))
-  endif
-  TARGET_VENDOR_TEST_SUFFIX := /vendor
-else
-  TARGET_VENDOR_TEST_SUFFIX :=
+ifeq ($(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION))
+  $(error BOARD_VNDK_VERSION is equal to PLATFORM_VNDK_VERSION; use BOARD_VNDK_VERSION := current)
 endif
-
-# If PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY is set,
-# BOARD_VNDK_VERSION must be set because PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY
-# is a enforcement of inter-partition dependency, and it doesn't have any meaning
-# when BOARD_VNDK_VERSION isn't set.
-ifeq ($(PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY),true)
-  ifeq ($(BOARD_VNDK_VERSION),)
-    $(error BOARD_VNDK_VERSION must be set when PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY is true)
-  endif
+ifneq ($(BOARD_VNDK_VERSION),current)
+  $(call check_vndk_version,$(BOARD_VNDK_VERSION))
 endif
+TARGET_VENDOR_TEST_SUFFIX := /vendor
 
 ###########################################
 # APEXes are by default not flattened, i.e. updatable.
diff --git a/core/config.mk b/core/config.mk
index c549296..5191917 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -356,6 +356,16 @@
 endif
 -include $(ANDROID_BUILDSPEC)
 
+# Starting in Android U, non-VNDK devices not supported
+# WARNING: DO NOT CHANGE: if you are downstream of AOSP, and you change this, without
+# letting upstream know it's important to you, we may do cleanup which breaks this
+# significantly. Please let us know if you are changing this.
+ifndef BOARD_VNDK_VERSION
+# READ WARNING - DO NOT CHANGE
+BOARD_VNDK_VERSION := current
+# READ WARNING - DO NOT CHANGE
+endif
+
 # ---------------------------------------------------------------
 # Define most of the global variables.  These are the ones that
 # are specific to the user's build configuration.
@@ -775,24 +785,6 @@
   BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED ?= true
 endif
 
-# Starting in Android U, non-VNDK devices not supported
-# WARNING: DO NOT CHANGE: if you are downstream of AOSP, and you change this, without
-# letting upstream know it's important to you, we may do cleanup which breaks this
-# significantly. Please let us know if you are changing this.
-ifndef BOARD_VNDK_VERSION
-# READ WARNING - DO NOT CHANGE
-BOARD_VNDK_VERSION := current
-# READ WARNING - DO NOT CHANGE
-endif
-
-ifdef PRODUCT_PRODUCT_VNDK_VERSION
-  ifndef BOARD_VNDK_VERSION
-    # VNDK for product partition is not available unless BOARD_VNDK_VERSION
-    # defined.
-    $(error PRODUCT_PRODUCT_VNDK_VERSION cannot be defined without defining BOARD_VNDK_VERSION)
-  endif
-endif
-
 # Set BOARD_SYSTEMSDK_VERSIONS to the latest SystemSDK version starting from P-launching
 # devices if unset.
 ifndef BOARD_SYSTEMSDK_VERSIONS
diff --git a/core/config_sanitizers.mk b/core/config_sanitizers.mk
index aa638d4..d837c6e 100644
--- a/core/config_sanitizers.mk
+++ b/core/config_sanitizers.mk
@@ -249,6 +249,13 @@
   endif
 endif
 
+# Ignore SANITIZE_TARGET_DIAG=memtag_heap without SANITIZE_TARGET=memtag_heap
+# This can happen if a condition above filters out memtag_heap from
+# my_sanitize. It is easier to handle all of these cases here centrally.
+ifneq ($(filter memtag_heap,$(my_sanitize_diag)),)
+  my_sanitize_diag := $(filter-out memtag_heap,$(my_sanitize_diag))
+endif
+
 ifneq ($(filter memtag_heap,$(my_sanitize)),)
   my_cflags += -fsanitize=memtag-heap
   my_sanitize := $(filter-out memtag_heap,$(my_sanitize))
diff --git a/core/copy_headers.mk b/core/copy_headers.mk
index 054d271..c457eb0 100644
--- a/core/copy_headers.mk
+++ b/core/copy_headers.mk
@@ -18,11 +18,9 @@
 # If we're using the VNDK, only vendor modules using the VNDK may use
 # LOCAL_COPY_HEADERS. Platform libraries will not have the include path
 # present.
-ifdef BOARD_VNDK_VERSION
 ifndef LOCAL_USE_VNDK
   $(call pretty-error,Only vendor modules using LOCAL_USE_VNDK may use LOCAL_COPY_HEADERS)
 endif
-endif
 
 # Clean up LOCAL_COPY_HEADERS_TO, since soong_ui will be comparing cleaned
 # paths to figure out which headers are obsolete and should be removed.
diff --git a/core/definitions.mk b/core/definitions.mk
index 7697211..be40584 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2941,7 +2941,7 @@
 define compress-package
 $(hide) \
   mv $@ $@.uncompressed; \
-  $(MINIGZIP) -c $@.uncompressed > $@.compressed; \
+  $(MINIGZIP) -9 -c $@.uncompressed > $@.compressed; \
   rm -f $@.uncompressed; \
   mv $@.compressed $@;
 endef
diff --git a/core/dex_preopt.mk b/core/dex_preopt.mk
index 62c3ba3..86ca729 100644
--- a/core/dex_preopt.mk
+++ b/core/dex_preopt.mk
@@ -80,15 +80,40 @@
   $(foreach m,$(other_system_server_jars),\
     $(PRODUCT_OUT)/$(call word-colon,1,$(m))/framework/$(call word-colon,2,$(m)).jar)
 
+# Infix can be 'art' (ART image for testing), 'boot' (primary), or 'mainline' (mainline extension).
+# Soong creates a set of variables for Make, one or each boot image. The only reason why the ART
+# image is exposed to Make is testing (art gtests) and benchmarking (art golem benchmarks). Install
+# rules that use those variables are in dex_preopt_libart.mk. Here for dexpreopt purposes the infix
+# is always 'boot' or 'mainline'.
+DEXPREOPT_INFIX := $(if $(filter true,$(DEX_PREOPT_WITH_UPDATABLE_BCP)),mainline,boot)
+
+# The input variables are written by build/soong/java/dexpreopt_bootjars.go. Examples can be found
+# at the bottom of build/soong/java/dexpreopt_config_testing.go.
+dexpreopt_root_dir := $(dir $(patsubst %/,%,$(dir $(firstword $(bootclasspath_jars)))))
+booclasspath_arg := $(subst $(space),:,$(patsubst $(dexpreopt_root_dir)%,%,$(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)))
+booclasspath_locations_arg := $(subst $(space),:,$(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS))
+boot_images := $(subst :,$(space),$(DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE$(DEXPREOPT_INFIX)))
+boot_image_arg := $(subst $(space),:,$(patsubst /%,%,$(boot_images)))
+
+boot_zip_metadata_txt := $(dir $(boot_zip))boot_zip/METADATA.txt
+$(boot_zip_metadata_txt):
+	rm -f $@
+	echo "booclasspath = $(booclasspath_arg)" >> $@
+	echo "booclasspath-locations = $(booclasspath_locations_arg)" >> $@
+	echo "boot-image = $(boot_image_arg)" >> $@
+
+$(call dist-for-goals, droidcore, $(boot_zip_metadata_txt))
+
 $(boot_zip): PRIVATE_BOOTCLASSPATH_JARS := $(bootclasspath_jars)
 $(boot_zip): PRIVATE_SYSTEM_SERVER_JARS := $(system_server_jars)
-$(boot_zip): $(bootclasspath_jars) $(system_server_jars) $(SOONG_ZIP) $(MERGE_ZIPS) $(DEXPREOPT_IMAGE_ZIP_boot) $(DEXPREOPT_IMAGE_ZIP_art)
+$(boot_zip): $(bootclasspath_jars) $(system_server_jars) $(SOONG_ZIP) $(MERGE_ZIPS) $(DEXPREOPT_IMAGE_ZIP_boot) $(DEXPREOPT_IMAGE_ZIP_art) $(DEXPREOPT_IMAGE_ZIP_mainline) $(boot_zip_metadata_txt)
 	@echo "Create boot package: $@"
 	rm -f $@
 	$(SOONG_ZIP) -o $@.tmp \
 	  -C $(dir $(firstword $(PRIVATE_BOOTCLASSPATH_JARS)))/.. $(addprefix -f ,$(PRIVATE_BOOTCLASSPATH_JARS)) \
-	  -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_SYSTEM_SERVER_JARS))
-	$(MERGE_ZIPS) $@ $@.tmp $(DEXPREOPT_IMAGE_ZIP_boot) $(DEXPREOPT_IMAGE_ZIP_art)
+	  -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_SYSTEM_SERVER_JARS)) \
+	  -j -f $(boot_zip_metadata_txt)
+	$(MERGE_ZIPS) $@ $@.tmp $(DEXPREOPT_IMAGE_ZIP_boot) $(DEXPREOPT_IMAGE_ZIP_art) $(DEXPREOPT_IMAGE_ZIP_mainline)
 	rm -f $@.tmp
 
 $(call dist-for-goals, droidcore, $(boot_zip))
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index 14fafa1..bdd47a8 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -274,13 +274,7 @@
 my_dexpreopt_images_deps :=
 my_dexpreopt_image_locations_on_host :=
 my_dexpreopt_image_locations_on_device :=
-# Infix can be 'art', 'boot', or 'mainline'. Soong creates a set of variables
-# for Make, one or each boot image (primary, the framework extension, and the
-# mainline extension). The only reason why the primary image is exposed to Make
-# is testing (art gtests) and benchmarking (art golem benchmarks). Install rules
-# that use those variables are in dex_preopt_libart.mk. Here for dexpreopt
-# purposes the infix is always 'boot' or 'mainline'.
-my_dexpreopt_infix := $(if $(filter true,$(DEX_PREOPT_WITH_UPDATABLE_BCP)),mainline,boot)
+my_dexpreopt_infix := $(DEXPREOPT_INFIX)
 my_create_dexpreopt_config :=
 
 ifdef LOCAL_DEX_PREOPT
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 8887ddc..f5a2022 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -24,19 +24,24 @@
 #$(warning $(call find_and_earlier,A B C,C))
 #$(warning $(call find_and_earlier,A B C,D))
 
-# Runs the starlark file given in $(1), and sets all the variables in its top-level
+# Runs a starlark file, and sets all the variables in its top-level
 # variables_to_export_to_make variable as make variables.
 #
 # 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).
+#
+# Arguments:
+#  $(1): A single starlark file to use as the entrypoint
+#  $(2): An optional list of starlark files to NOT include as kati dependencies.
+#  $(3): An optional list of extra flags to pass to rbcrun
 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))
+$(KATI_shell_no_rerun mkdir -p $(OUT_DIR)/starlark_results && $(OUT_DIR)/rbcrun --mode=make $(3) $(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 $(LOADED_STARLARK_FILES))
+$(KATI_extra_file_deps $(filter-out $(2),$(LOADED_STARLARK_FILES)))
 $(eval LOADED_STARLARK_FILES :=)
 $(eval _starlark_results :=)
 endef
@@ -61,7 +66,7 @@
   $(if $(filter $(ALL_VERSIONS),$(2)),,
     $(error Invalid MAX_PLATFORM_VERSION '$(2)'))
   $(if $(filter $(ALL_VERSIONS),$(3)),,
-    $(error Invalid DEFAULT_PLATFORM_VERSION '$(3)'))
+    $(error Invalid RELEASE_PLATFORM_VERSION '$(3)'))
 
   $(eval allowed_versions_ := $(call find_and_earlier,$(ALL_VERSIONS),$(2)))
 
@@ -72,7 +77,7 @@
     $(filter-out $(call find_and_earlier,$(allowed_versions_),$(1)),$(allowed_versions_)))
 
   $(if $(filter $(allowed_versions_),$(3)),,
-    $(error DEFAULT_PLATFORM_VERSION '$(3)' must be between MIN_PLATFORM_VERSION '$(1)' and MAX_PLATFORM_VERSION '$(2)'))
+    $(error RELEASE_PLATFORM_VERSION '$(3)' must be between MIN_PLATFORM_VERSION '$(1)' and MAX_PLATFORM_VERSION '$(2)'))
 
   $(allowed_versions_))
 endef
diff --git a/core/local_vndk.mk b/core/local_vndk.mk
index befbc59..eb8f2c0 100644
--- a/core/local_vndk.mk
+++ b/core/local_vndk.mk
@@ -37,12 +37,5 @@
     $(shell echo $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): LOCAL_USE_VNDK must not be used with LOCAL_SDK_VERSION >&2)
     $(error done)
   endif
-
-  # If we're not using the VNDK, drop all restrictions
-  ifndef BOARD_VNDK_VERSION
-    LOCAL_USE_VNDK:=
-    LOCAL_USE_VNDK_VENDOR:=
-    LOCAL_USE_VNDK_PRODUCT:=
-  endif
 endif
 
diff --git a/core/product.mk b/core/product.mk
index 818aac2..6f54b78 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -389,6 +389,9 @@
 # If true, installs a full version of com.android.virt APEX.
 _product_single_value_vars += PRODUCT_AVF_ENABLED
 
+# If true, kernel with modules will be used for Microdroid VMs.
+_product_single_value_vars += PRODUCT_AVF_KERNEL_MODULES_ENABLED
+
 # List of .json files to be merged/compiled into vendor/etc/linker.config.pb
 _product_list_vars += PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS
 
diff --git a/core/release_config.bzl b/core/release_config.bzl
new file mode 100644
index 0000000..805106f
--- /dev/null
+++ b/core/release_config.bzl
@@ -0,0 +1,121 @@
+# 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",
+]
+
+ALL = ["all"]
+PRODUCT = ["product"]
+SYSTEM = ["system"]
+SYSTEM_EXT = ["system_ext"]
+VENDOR = ["vendor"]
+
+_valid_types = ["NoneType", "bool", "list", "string", "int"]
+
+def flag(name, partitions, default):
+    "Declare a flag."
+    if not partitions:
+        fail("At least 1 partition is required")
+    if not name.startswith("RELEASE_"):
+        fail("Release flag names must start with RELEASE_")
+    if " " in name or "\t" in name or "\n" in name:
+        fail("Flag names must not contain whitespace: \"" + name + "\"")
+    for partition in partitions:
+        if partition == "all":
+            if len(partitions) > 1:
+                fail("\"all\" can't be combined with other partitions: " + str(partitions))
+        elif partition not in _flag_partitions:
+            fail("Invalid partition: " + partition + ", allowed partitions: " +
+                 str(_flag_partitions))
+    if type(default) not in _valid_types:
+        fail("Invalid type of default for flag \"" + name + "\" (" + type(default) + ")")
+    return {
+        "name": name,
+        "partitions": partitions,
+        "default": default,
+    }
+
+def value(name, value):
+    "Define the flag value for a particular configuration."
+    return {
+        "name": name,
+        "value": value,
+    }
+
+def _format_value(val):
+    "Format the starlark type correctly for make"
+    if type(val) == "NoneType":
+        return ""
+    elif type(val) == "bool":
+        return "true" if val else ""
+    else:
+        return val
+
+def release_config(all_flags, all_values):
+    "Return the make variables that should be set for this release config."
+
+    # Validate flags
+    flag_names = []
+    for flag in all_flags:
+        if flag["name"] in flag_names:
+            fail(flag["declared_in"] + ": Duplicate declaration of flag " + flag["name"])
+        flag_names.append(flag["name"])
+
+    # Record which flags go on which partition
+    partitions = {}
+    for flag in all_flags:
+        for partition in flag["partitions"]:
+            if partition == "all":
+                for partition in _flag_partitions:
+                    partitions.setdefault(partition, []).append(flag["name"])
+            else:
+                partitions.setdefault(partition, []).append(flag["name"])
+
+    # Validate values
+    # TODO(joeo): Disallow duplicate values after we've split AOSP and vendor flags.
+    values = {}
+    for value in all_values:
+        if value["name"] not in flag_names:
+            fail(value["set_in"] + ": Value set for undeclared build flag: " + value["name"])
+        values[value["name"]] = value
+
+    # Collect values
+    result = {
+        "_ALL_RELEASE_FLAGS": sorted(flag_names),
+    }
+    for partition, names in partitions.items():
+        result["_ALL_RELEASE_FLAGS.PARTITIONS." + partition] = names
+    for flag in all_flags:
+        if flag["name"] in values:
+            val = values[flag["name"]]["value"]
+            set_in = values[flag["name"]]["set_in"]
+            if type(val) not in _valid_types:
+                fail("Invalid type of value for flag \"" + flag["name"] + "\" (" + type(val) + ")")
+        else:
+            val = flag["default"]
+            set_in = flag["declared_in"]
+        val = _format_value(val)
+        result[flag["name"]] = val
+        result["_ALL_RELEASE_FLAGS." + flag["name"] + ".PARTITIONS"] = flag["partitions"]
+        result["_ALL_RELEASE_FLAGS." + flag["name"] + ".DEFAULT"] = _format_value(flag["default"])
+        result["_ALL_RELEASE_FLAGS." + flag["name"] + ".VALUE"] = val
+        result["_ALL_RELEASE_FLAGS." + flag["name"] + ".DECLARED_IN"] = flag["declared_in"]
+        result["_ALL_RELEASE_FLAGS." + flag["name"] + ".SET_IN"] = set_in
+
+    return result
diff --git a/core/release_config.mk b/core/release_config.mk
index fdfc6a0..1a2d480 100644
--- a/core/release_config.mk
+++ b/core/release_config.mk
@@ -12,17 +12,30 @@
 # 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
+# -----------------------------------------------------------------
+# Release configs are defined in reflease_config_map files, which map
+# the short name (e.g. -next) used in lunch to the starlark files
+# defining the build flag values.
+#
+# (If you're thinking about aconfig flags, there is one build flag,
+# RELEASE_DEVICE_CONFIG_VALUE_SETS, that sets which device_config_value_set
+# module to use to set the aconfig flag values.)
+#
+# The short release config names *can* appear multiple times, to allow
+# for AOSP and vendor specific flags under the same name, but the
+# individual flag values must appear in exactly one config.  Vendor
+# does not override AOSP, or anything like that.  This is because
+# vendor code usually includes prebuilts, and having vendor compile
+# with different flags from AOSP increases the likelihood of flag
+# mismatch.
+
 # Do this first, because we're going to unset TARGET_RELEASE before
 # including anyone, so they don't start making conditionals based on it.
+# This logic is in make because starlark doesn't understand optional
+# vendor 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.
@@ -42,17 +55,12 @@
 # $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)))
+    $(eval _all_release_configs.$(strip $(1)).DECLARED_IN := $(_included) $(_all_release_configs.$(strip $(1)).DECLARED_IN))
+    $(eval _all_release_configs.$(strip $(1)).FILES := $(_all_release_configs.$(strip $(1)).FILES) $(strip $(2)))
 endef
 
 # Include the config map files
@@ -70,17 +78,17 @@
 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)
+    flag_value_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 :=
+flag_value_files :=
 endif
 
-# Unset variables so they can't use it
+# Unset variables so they can't use them
 define declare-release-config
 $(error declare-release-config can only be called from inside release_config_map.mk files)
 endef
@@ -96,6 +104,7 @@
 endif
 .KATI_READONLY := TARGET_RELEASE
 
+
 $(foreach config, $(_all_release_configs), \
     $(eval _all_release_configs.$(config).DECLARED_IN:= ) \
     $(eval _all_release_configs.$(config).FILES:= ) \
@@ -103,120 +112,53 @@
 _all_release_configs:=
 config_map_files:=
 
+
 # -----------------------------------------------------------------
-# Declare the flags
+# Flag declarations and values
+# -----------------------------------------------------------------
+# This part is in starlark.  We generate a root starlark file that loads
+# all of the flags declaration files that we found, and the flag_value_files
+# that we chose from the config map above.  Then we run that, and load the
+# results of that into the make environment.
 
-# $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, \
+# 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, \
         $(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) \
+            $(wildcard device/*/release/build_flags.bzl) \
+            $(wildcard device/*/*/release/build_flags.bzl) \
+            $(wildcard vendor/*/release/build_flags.bzl) \
+            $(wildcard vendor/*/*/release/build_flags.bzl) \
         ) \
     )
 
-# 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
+# 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+=$(newline)def add(d, k, v):
+_c+=$(newline)$(space)d = dict(d)
+_c+=$(newline)$(space)d[k] = v
+_c+=$(newline)$(space)return d
+_c+=$(foreach f,$(flag_declaration_files),$(newline)load("$(f)", flags_$(call filename_to_starlark,$(f)) = "flags"))
+_c+=$(newline)all_flags = [] $(foreach f,$(flag_declaration_files),+ [add(x, "declared_in", "$(f)") for x in flags_$(call filename_to_starlark,$(f))])
+_c+=$(foreach f,$(flag_value_files),$(newline)load("//$(f)", values_$(call filename_to_starlark,$(f)) = "values"))
+_c+=$(newline)all_values = [] $(foreach f,$(flag_value_files),+ [add(x, "set_in", "$(f)") for x in values_$(call filename_to_starlark,$(f))])
+_c+=$(newline)variables_to_export_to_make = release_config(all_flags, all_values)
+$(file >$(OUT_DIR)/release_config_entrypoint.bzl,$(_c))
+_c:=
+filename_to_starlark:=
 
-# No more flags from here on
-.KATI_READONLY := _ALL_RELEASE_FLAGS
+# 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.
+#
+# We also need to pass --allow_external_entrypoint to rbcrun in case the OUT_DIR is set to something
+# outside of the source tree.
+$(call run-starlark,$(OUT_DIR)/release_config_entrypoint.bzl,$(OUT_DIR)/release_config_entrypoint.bzl,--allow_external_entrypoint)
 
-# -----------------------------------------------------------------
-# 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 cf4b1f6..a2296a8 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 := $$(cat $(SOONG_OUT_DIR)/build_hostname.txt)
+    BF_BUILD_NUMBER := $(BUILD_NUMBER_FROM_FILE)
   endif
   BUILD_FINGERPRINT := $(PRODUCT_BRAND)/$(TARGET_PRODUCT)/$(TARGET_DEVICE):$(PLATFORM_VERSION)/$(BUILD_ID)/$(BF_BUILD_NUMBER):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)
 endif
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index f9175e45..4a42783 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -40,8 +40,7 @@
   include $(INTERNAL_BUILD_ID_MAKEFILE)
 endif
 
-DEFAULT_PLATFORM_VERSION := VP1A
-.KATI_READONLY := DEFAULT_PLATFORM_VERSION
+# Set release configuration. The default resides in build/release/build_flags.mk.
 MIN_PLATFORM_VERSION := UP1A
 MAX_PLATFORM_VERSION := VP1A
 
diff --git a/core/version_util.mk b/core/version_util.mk
index d4ce113..d3fcdc2 100644
--- a/core/version_util.mk
+++ b/core/version_util.mk
@@ -14,17 +14,17 @@
 # limitations under the License.
 #
 
-#
-
 ALLOWED_VERSIONS := $(call allowed-platform-versions,\
   $(MIN_PLATFORM_VERSION),\
   $(MAX_PLATFORM_VERSION),\
-  $(DEFAULT_PLATFORM_VERSION))
+  $(RELEASE_PLATFORM_VERSION))
 
-ifndef TARGET_PLATFORM_VERSION
-  TARGET_PLATFORM_VERSION := $(DEFAULT_PLATFORM_VERSION)
+ifdef TARGET_PLATFORM_VERSION
+  $(error Do not set TARGET_PLATFORM_VERSION directly. Use RELEASE_PLATFORM_VERSION. value: $(TARGET_PLATFORM_VERSION))
 endif
 
+TARGET_PLATFORM_VERSION := $(RELEASE_PLATFORM_VERSION)
+
 ifeq (,$(filter $(ALLOWED_VERSIONS), $(TARGET_PLATFORM_VERSION)))
   $(warning Invalid TARGET_PLATFORM_VERSION '$(TARGET_PLATFORM_VERSION)', must be one of)
   $(error $(ALLOWED_VERSIONS))
diff --git a/target/product/AndroidProducts.mk b/target/product/AndroidProducts.mk
index 1e0ce19..133dc73 100644
--- a/target/product/AndroidProducts.mk
+++ b/target/product/AndroidProducts.mk
@@ -35,6 +35,7 @@
 ifneq ($(TARGET_BUILD_APPS),)
 PRODUCT_MAKEFILES := \
     $(LOCAL_DIR)/aosp_arm64.mk \
+    $(LOCAL_DIR)/aosp_arm64_fullmte.mk \
     $(LOCAL_DIR)/aosp_arm.mk \
     $(LOCAL_DIR)/aosp_riscv64.mk \
     $(LOCAL_DIR)/aosp_x86_64.mk \
@@ -46,6 +47,7 @@
 PRODUCT_MAKEFILES := \
     $(LOCAL_DIR)/aosp_64bitonly_x86_64.mk \
     $(LOCAL_DIR)/aosp_arm64.mk \
+    $(LOCAL_DIR)/aosp_arm64_fullmte.mk \
     $(LOCAL_DIR)/aosp_arm.mk \
     $(LOCAL_DIR)/aosp_riscv64.mk \
     $(LOCAL_DIR)/aosp_x86_64.mk \
diff --git a/target/product/aosp_arm64_fullmte.mk b/target/product/aosp_arm64_fullmte.mk
new file mode 100644
index 0000000..ed6bd4a
--- /dev/null
+++ b/target/product/aosp_arm64_fullmte.mk
@@ -0,0 +1,27 @@
+# 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 $(SRC_TARGET_DIR)/product/fullmte.mk
+
+PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_arm64.mk)
+
+# Build modules from source if this has not been pre-configured
+MODULE_BUILD_FROM_SOURCE ?= true
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)
+
+PRODUCT_NAME := aosp_arm64_fullmte
diff --git a/target/product/gsi/Android.mk b/target/product/gsi/Android.mk
index 107c94f..86d4622 100644
--- a/target/product/gsi/Android.mk
+++ b/target/product/gsi/Android.mk
@@ -33,9 +33,6 @@
 check-vndk-list: ;
 else ifeq ($(TARGET_SKIP_CURRENT_VNDK),true)
 check-vndk-list: ;
-else ifeq ($(BOARD_VNDK_VERSION),)
-# b/143233626 do not check vndk-list when vndk libs are not built
-check-vndk-list: ;
 else
 check-vndk-list: $(check-vndk-list-timestamp)
 ifneq ($(SKIP_ABI_CHECKS),true)
@@ -172,8 +169,6 @@
 #####################################################################
 # VNDK package and snapshot.
 
-ifneq ($(BOARD_VNDK_VERSION),)
-
 include $(CLEAR_VARS)
 LOCAL_MODULE := vndk_package
 LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
@@ -214,8 +209,6 @@
 
 _vndk_versions :=
 
-endif # BOARD_VNDK_VERSION is set
-
 #####################################################################
 # skip_mount.cfg, read by init to skip mounting some partitions when GSI is used.
 
diff --git a/tests/lunch_tests.sh b/tests/lunch_tests.sh
index 4285d13..9b142ee 100755
--- a/tests/lunch_tests.sh
+++ b/tests/lunch_tests.sh
@@ -28,7 +28,7 @@
     [ "$TARGET_PLATFORM_VERSION" = "$4" ] || ( echo "lunch $1: expected TARGET_PLATFORM_VERSION='$4', got '$TARGET_PLATFORM_VERSION'" && exit 1 )
 )
 
-default_version=$(get_build_var DEFAULT_PLATFORM_VERSION)
+default_version=$(get_build_var RELEASE_PLATFORM_VERSION)
 
 # lunch tests
 check_lunch "aosp_arm64"                                "aosp_arm64" "eng"       ""
diff --git a/tools/aconfig/Android.bp b/tools/aconfig/Android.bp
index 9617e0e..25424c5 100644
--- a/tools/aconfig/Android.bp
+++ b/tools/aconfig/Android.bp
@@ -2,6 +2,8 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+// host binary: aconfig
+
 rust_protobuf_host {
     name: "libaconfig_protos",
     protos: ["protos/aconfig.proto"],
@@ -35,4 +37,51 @@
 rust_test_host {
     name: "aconfig.test",
     defaults: ["aconfig.defaults"],
+    rustlibs: [
+        "libitertools",
+    ],
+}
+
+// integration tests: java
+
+device_config_definitions {
+    name: "aconfig.test.flags",
+    namespace: "com.android.aconfig.test",
+    srcs: ["tests/test.aconfig"],
+}
+
+device_config_values {
+    name: "aconfig.test.flag.values",
+    namespace: "com.android.aconfig.test",
+    srcs: [
+        "tests/first.values",
+        "tests/second.values",
+    ],
+}
+
+device_config_value_set {
+    name: "aconfig.test.flag.value_set",
+    values: [
+        "aconfig.test.flag.values",
+    ],
+}
+
+java_device_config_definitions_library {
+    name: "aconfig_test_java",
+    device_config_definitions: "aconfig.test.flags",
+}
+
+android_test {
+    name: "aconfig.test.java",
+    srcs: [
+        "tests/**/*.java",
+    ],
+    manifest: "tests/AndroidManifest.xml",
+    certificate: "platform",
+    static_libs: [
+        "androidx.test.rules",
+        "testng",
+        "aconfig_test_java",
+    ],
+    test_suites: ["device-tests"],
 }
diff --git a/tools/aconfig/Cargo.toml b/tools/aconfig/Cargo.toml
index 8517dd2..b3c73b8 100644
--- a/tools/aconfig/Cargo.toml
+++ b/tools/aconfig/Cargo.toml
@@ -18,3 +18,6 @@
 
 [build-dependencies]
 protobuf-codegen = "3.2.0"
+
+[dev-dependencies]
+itertools = "0.10.5"
diff --git a/tools/aconfig/protos/aconfig.proto b/tools/aconfig/protos/aconfig.proto
index 9d36a9e..9f6424f 100644
--- a/tools/aconfig/protos/aconfig.proto
+++ b/tools/aconfig/protos/aconfig.proto
@@ -36,16 +36,17 @@
 
 message flag_declaration {
   required string name = 1;
-  required string description = 2;
+  required string namespace = 2;
+  required string description = 3;
 };
 
 message flag_declarations {
-  required string namespace = 1;
+  required string package = 1;
   repeated flag_declaration flag = 2;
 };
 
 message flag_value {
-  required string namespace = 1;
+  required string package = 1;
   required string name = 2;
   required flag_state state = 3;
   required flag_permission permission = 4;
@@ -65,12 +66,13 @@
 }
 
 message parsed_flag {
-  required string namespace = 1;
+  required string package = 1;
   required string name = 2;
-  required string description = 3;
-  required flag_state state = 4;
-  required flag_permission permission = 5;
-  repeated tracepoint trace = 6;
+  required string namespace = 3;
+  required string description = 4;
+  required flag_state state = 5;
+  required flag_permission permission = 6;
+  repeated tracepoint trace = 7;
 }
 
 message parsed_flags {
diff --git a/tools/aconfig/src/aconfig.rs b/tools/aconfig/src/aconfig.rs
index b9fa324..5e7c861 100644
--- a/tools/aconfig/src/aconfig.rs
+++ b/tools/aconfig/src/aconfig.rs
@@ -81,6 +81,7 @@
 #[derive(Debug, PartialEq, Eq)]
 pub struct FlagDeclaration {
     pub name: String,
+    pub namespace: String,
     pub description: String,
 }
 
@@ -100,16 +101,19 @@
         let Some(name) = proto.name else {
             bail!("missing 'name' field");
         };
+        let Some(namespace) = proto.namespace else {
+            bail!("missing 'namespace' field");
+        };
         let Some(description) = proto.description else {
             bail!("missing 'description' field");
         };
-        Ok(FlagDeclaration { name, description })
+        Ok(FlagDeclaration { name, namespace, description })
     }
 }
 
 #[derive(Debug, PartialEq, Eq)]
 pub struct FlagDeclarations {
-    pub namespace: String,
+    pub package: String,
     pub flags: Vec<FlagDeclaration>,
 }
 
@@ -117,20 +121,20 @@
     pub fn try_from_text_proto(text_proto: &str) -> Result<FlagDeclarations> {
         let proto: ProtoFlagDeclarations = crate::protos::try_from_text_proto(text_proto)
             .with_context(|| text_proto.to_owned())?;
-        let Some(namespace) = proto.namespace else {
-            bail!("missing 'namespace' field");
+        let Some(package) = proto.package else {
+            bail!("missing 'package' field");
         };
         let mut flags = vec![];
         for proto_flag in proto.flag.into_iter() {
             flags.push(proto_flag.try_into()?);
         }
-        Ok(FlagDeclarations { namespace, flags })
+        Ok(FlagDeclarations { package, flags })
     }
 }
 
 #[derive(Debug, PartialEq, Eq)]
 pub struct FlagValue {
-    pub namespace: String,
+    pub package: String,
     pub name: String,
     pub state: FlagState,
     pub permission: Permission,
@@ -153,8 +157,8 @@
     type Error = Error;
 
     fn try_from(proto: ProtoFlagValue) -> Result<Self, Self::Error> {
-        let Some(namespace) = proto.namespace else {
-            bail!("missing 'namespace' field");
+        let Some(package) = proto.package else {
+            bail!("missing 'package' field");
         };
         let Some(name) = proto.name else {
             bail!("missing 'name' field");
@@ -167,7 +171,7 @@
             bail!("missing 'permission' field");
         };
         let permission = proto_permission.try_into()?;
-        Ok(FlagValue { namespace, name, state, permission })
+        Ok(FlagValue { package, name, state, permission })
     }
 }
 
@@ -184,8 +188,9 @@
 impl From<Item> for ProtoParsedFlag {
     fn from(item: Item) -> Self {
         let mut proto = crate::protos::ProtoParsedFlag::new();
-        proto.set_namespace(item.namespace.to_owned());
+        proto.set_package(item.package.to_owned());
         proto.set_name(item.name.clone());
+        proto.set_namespace(item.namespace.clone());
         proto.set_description(item.description.clone());
         proto.set_state(item.state.into());
         proto.set_permission(item.permission.into());
@@ -214,11 +219,13 @@
     fn test_flag_try_from_text_proto() {
         let expected = FlagDeclaration {
             name: "1234".to_owned(),
+            namespace: "ns".to_owned(),
             description: "Description of the flag".to_owned(),
         };
 
         let s = r#"
         name: "1234"
+        namespace: "ns"
         description: "Description of the flag"
         "#;
         let actual = FlagDeclaration::try_from_text_proto(s).unwrap();
@@ -242,23 +249,33 @@
     }
 
     #[test]
-    fn test_namespace_try_from_text_proto() {
+    fn test_package_try_from_text_proto() {
         let expected = FlagDeclarations {
-            namespace: "ns".to_owned(),
+            package: "com.example".to_owned(),
             flags: vec![
-                FlagDeclaration { name: "a".to_owned(), description: "A".to_owned() },
-                FlagDeclaration { name: "b".to_owned(), description: "B".to_owned() },
+                FlagDeclaration {
+                    name: "a".to_owned(),
+                    namespace: "ns".to_owned(),
+                    description: "A".to_owned(),
+                },
+                FlagDeclaration {
+                    name: "b".to_owned(),
+                    namespace: "ns".to_owned(),
+                    description: "B".to_owned(),
+                },
             ],
         };
 
         let s = r#"
-        namespace: "ns"
+        package: "com.example"
         flag {
             name: "a"
+            namespace: "ns"
             description: "A"
         }
         flag {
             name: "b"
+            namespace: "ns"
             description: "B"
         }
         "#;
@@ -270,14 +287,14 @@
     #[test]
     fn test_flag_declaration_try_from_text_proto_list() {
         let expected = FlagValue {
-            namespace: "ns".to_owned(),
+            package: "com.example".to_owned(),
             name: "1234".to_owned(),
             state: FlagState::Enabled,
             permission: Permission::ReadOnly,
         };
 
         let s = r#"
-        namespace: "ns"
+        package: "com.example"
         name: "1234"
         state: ENABLED
         permission: READ_ONLY
diff --git a/tools/aconfig/src/cache.rs b/tools/aconfig/src/cache.rs
index 972ba41..dd54480 100644
--- a/tools/aconfig/src/cache.rs
+++ b/tools/aconfig/src/cache.rs
@@ -34,12 +34,13 @@
 
 #[derive(Serialize, Deserialize, Debug)]
 pub struct Item {
-    // TODO: duplicating the Cache.namespace as Item.namespace makes the internal representation
+    // TODO: duplicating the Cache.package as Item.package makes the internal representation
     // closer to the proto message `parsed_flag`; hopefully this will enable us to replace the Item
-    // struct and use a newtype instead once aconfig has matured. Until then, namespace should
+    // struct and use a newtype instead once aconfig has matured. Until then, package should
     // really be a Cow<String>.
-    pub namespace: String,
+    pub package: String,
     pub name: String,
+    pub namespace: String,
     pub description: String,
     pub state: FlagState,
     pub permission: Permission,
@@ -48,7 +49,7 @@
 
 #[derive(Serialize, Deserialize, Debug)]
 pub struct Cache {
-    namespace: String,
+    package: String,
     items: Vec<Item>,
 }
 
@@ -96,9 +97,9 @@
         self.items.into_iter()
     }
 
-    pub fn namespace(&self) -> &str {
-        debug_assert!(!self.namespace.is_empty());
-        &self.namespace
+    pub fn package(&self) -> &str {
+        debug_assert!(!self.package.is_empty());
+        &self.package
     }
 }
 
@@ -108,9 +109,9 @@
 }
 
 impl CacheBuilder {
-    pub fn new(namespace: String) -> Result<CacheBuilder> {
-        ensure!(codegen::is_valid_identifier(&namespace), "bad namespace");
-        let cache = Cache { namespace, items: vec![] };
+    pub fn new(package: String) -> Result<CacheBuilder> {
+        ensure!(codegen::is_valid_package_ident(&package), "bad package");
+        let cache = Cache { package, items: vec![] };
         Ok(CacheBuilder { cache })
     }
 
@@ -119,7 +120,8 @@
         source: Source,
         declaration: FlagDeclaration,
     ) -> Result<&mut CacheBuilder> {
-        ensure!(codegen::is_valid_identifier(&declaration.name), "bad flag name");
+        ensure!(codegen::is_valid_name_ident(&declaration.name), "bad flag name");
+        ensure!(codegen::is_valid_name_ident(&declaration.namespace), "bad namespace");
         ensure!(!declaration.description.is_empty(), "empty flag description");
         ensure!(
             self.cache.items.iter().all(|item| item.name != declaration.name),
@@ -128,8 +130,9 @@
             source
         );
         self.cache.items.push(Item {
-            namespace: self.cache.namespace.clone(),
+            package: self.cache.package.clone(),
             name: declaration.name.clone(),
+            namespace: declaration.namespace.clone(),
             description: declaration.description,
             state: DEFAULT_FLAG_STATE,
             permission: DEFAULT_FLAG_PERMISSION,
@@ -147,18 +150,18 @@
         source: Source,
         value: FlagValue,
     ) -> Result<&mut CacheBuilder> {
-        ensure!(codegen::is_valid_identifier(&value.namespace), "bad flag namespace");
-        ensure!(codegen::is_valid_identifier(&value.name), "bad flag name");
+        ensure!(codegen::is_valid_package_ident(&value.package), "bad flag package");
+        ensure!(codegen::is_valid_name_ident(&value.name), "bad flag name");
         ensure!(
-            value.namespace == self.cache.namespace,
-            "failed to set values for flag {}/{} from {}: expected namespace {}",
-            value.namespace,
+            value.package == self.cache.package,
+            "failed to set values for flag {}/{} from {}: expected package {}",
+            value.package,
             value.name,
             source,
-            self.cache.namespace
+            self.cache.package
         );
         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);
+            bail!("failed to set values for flag {}/{} from {}: flag not declared", value.package, value.name, source);
         };
         existing_item.state = value.state;
         existing_item.permission = value.permission;
@@ -182,17 +185,25 @@
 
     #[test]
     fn test_add_flag_declaration() {
-        let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
+        let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
         builder
             .add_flag_declaration(
                 Source::File("first.txt".to_string()),
-                FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+                FlagDeclaration {
+                    name: "foo".to_string(),
+                    namespace: "ns".to_string(),
+                    description: "desc".to_string(),
+                },
             )
             .unwrap();
         let error = builder
             .add_flag_declaration(
                 Source::File("second.txt".to_string()),
-                FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+                FlagDeclaration {
+                    name: "foo".to_string(),
+                    namespace: "ns".to_string(),
+                    description: "desc".to_string(),
+                },
             )
             .unwrap_err();
         assert_eq!(
@@ -202,7 +213,11 @@
         builder
             .add_flag_declaration(
                 Source::File("first.txt".to_string()),
-                FlagDeclaration { name: "bar".to_string(), description: "desc".to_string() },
+                FlagDeclaration {
+                    name: "bar".to_string(),
+                    namespace: "ns".to_string(),
+                    description: "desc".to_string(),
+                },
             )
             .unwrap();
 
@@ -217,12 +232,12 @@
 
     #[test]
     fn test_add_flag_value() {
-        let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
+        let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
         let error = builder
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
-                    namespace: "ns".to_string(),
+                    package: "com.example".to_string(),
                     name: "foo".to_string(),
                     state: FlagState::Enabled,
                     permission: Permission::ReadOnly,
@@ -231,13 +246,17 @@
             .unwrap_err();
         assert_eq!(
             &format!("{:?}", error),
-            "failed to set values for flag ns/foo from <memory>: flag not declared"
+            "failed to set values for flag com.example/foo from <memory>: flag not declared"
         );
 
         builder
             .add_flag_declaration(
                 Source::File("first.txt".to_string()),
-                FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+                FlagDeclaration {
+                    name: "foo".to_string(),
+                    namespace: "ns".to_string(),
+                    description: "desc".to_string(),
+                },
             )
             .unwrap();
 
@@ -245,7 +264,7 @@
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
-                    namespace: "ns".to_string(),
+                    package: "com.example".to_string(),
                     name: "foo".to_string(),
                     state: FlagState::Disabled,
                     permission: Permission::ReadOnly,
@@ -257,7 +276,7 @@
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
-                    namespace: "ns".to_string(),
+                    package: "com.example".to_string(),
                     name: "foo".to_string(),
                     state: FlagState::Enabled,
                     permission: Permission::ReadWrite,
@@ -265,19 +284,19 @@
             )
             .unwrap();
 
-        // different namespace -> no-op
+        // different package -> no-op
         let error = builder
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
-                    namespace: "some_other_namespace".to_string(),
+                    package: "some_other_package".to_string(),
                     name: "foo".to_string(),
                     state: FlagState::Enabled,
                     permission: Permission::ReadOnly,
                 },
             )
             .unwrap_err();
-        assert_eq!(&format!("{:?}", error), "failed to set values for flag some_other_namespace/foo from <memory>: expected namespace ns");
+        assert_eq!(&format!("{:?}", error), "failed to set values for flag some_other_package/foo from <memory>: expected package com.example");
 
         let cache = builder.build();
         let item = cache.iter().find(|&item| item.name == "foo").unwrap();
@@ -286,18 +305,22 @@
     }
 
     #[test]
-    fn test_reject_empty_cache_namespace() {
+    fn test_reject_empty_cache_package() {
         CacheBuilder::new("".to_string()).unwrap_err();
     }
 
     #[test]
     fn test_reject_empty_flag_declaration_fields() {
-        let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
+        let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
 
         let error = builder
             .add_flag_declaration(
                 Source::Memory,
-                FlagDeclaration { name: "".to_string(), description: "Description".to_string() },
+                FlagDeclaration {
+                    name: "".to_string(),
+                    namespace: "ns".to_string(),
+                    description: "Description".to_string(),
+                },
             )
             .unwrap_err();
         assert_eq!(&format!("{:?}", error), "bad flag name");
@@ -305,7 +328,11 @@
         let error = builder
             .add_flag_declaration(
                 Source::Memory,
-                FlagDeclaration { name: "foo".to_string(), description: "".to_string() },
+                FlagDeclaration {
+                    name: "foo".to_string(),
+                    namespace: "ns".to_string(),
+                    description: "".to_string(),
+                },
             )
             .unwrap_err();
         assert_eq!(&format!("{:?}", error), "empty flag description");
@@ -313,11 +340,15 @@
 
     #[test]
     fn test_reject_empty_flag_value_files() {
-        let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
+        let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
         builder
             .add_flag_declaration(
                 Source::Memory,
-                FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+                FlagDeclaration {
+                    name: "foo".to_string(),
+                    namespace: "ns".to_string(),
+                    description: "desc".to_string(),
+                },
             )
             .unwrap();
 
@@ -325,20 +356,20 @@
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
-                    namespace: "".to_string(),
+                    package: "".to_string(),
                     name: "foo".to_string(),
                     state: FlagState::Enabled,
                     permission: Permission::ReadOnly,
                 },
             )
             .unwrap_err();
-        assert_eq!(&format!("{:?}", error), "bad flag namespace");
+        assert_eq!(&format!("{:?}", error), "bad flag package");
 
         let error = builder
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
-                    namespace: "ns".to_string(),
+                    package: "com.example".to_string(),
                     name: "".to_string(),
                     state: FlagState::Enabled,
                     permission: Permission::ReadOnly,
diff --git a/tools/aconfig/src/codegen.rs b/tools/aconfig/src/codegen.rs
index b60ec51..fea9961 100644
--- a/tools/aconfig/src/codegen.rs
+++ b/tools/aconfig/src/codegen.rs
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-pub fn is_valid_identifier(s: &str) -> bool {
+use anyhow::{ensure, Result};
+
+pub fn is_valid_name_ident(s: &str) -> bool {
     // Identifiers must match [a-z][a-z0-9_]*
     let mut chars = s.chars();
     let Some(first) = chars.next() else {
@@ -26,18 +28,54 @@
     chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
 }
 
+pub fn is_valid_package_ident(s: &str) -> bool {
+    s.split('.').all(is_valid_name_ident)
+}
+
+pub fn create_device_config_ident(package: &str, flag_name: &str) -> Result<String> {
+    ensure!(is_valid_package_ident(package), "bad package");
+    ensure!(is_valid_package_ident(flag_name), "bad flag name");
+    Ok(format!("{}.{}", package, flag_name))
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
 
     #[test]
-    fn test_is_valid_identifier() {
-        assert!(is_valid_identifier("foo"));
-        assert!(is_valid_identifier("foo_bar_123"));
+    fn test_is_valid_name_ident() {
+        assert!(is_valid_name_ident("foo"));
+        assert!(is_valid_name_ident("foo_bar_123"));
 
-        assert!(!is_valid_identifier(""));
-        assert!(!is_valid_identifier("123_foo"));
-        assert!(!is_valid_identifier("foo-bar"));
-        assert!(!is_valid_identifier("foo-b\u{00e5}r"));
+        assert!(!is_valid_name_ident(""));
+        assert!(!is_valid_name_ident("123_foo"));
+        assert!(!is_valid_name_ident("foo-bar"));
+        assert!(!is_valid_name_ident("foo-b\u{00e5}r"));
+    }
+
+    #[test]
+    fn test_is_valid_package_ident() {
+        assert!(is_valid_package_ident("foo"));
+        assert!(is_valid_package_ident("foo_bar_123"));
+        assert!(is_valid_package_ident("foo.bar"));
+        assert!(is_valid_package_ident("foo.bar.a123"));
+
+        assert!(!is_valid_package_ident(""));
+        assert!(!is_valid_package_ident("123_foo"));
+        assert!(!is_valid_package_ident("foo-bar"));
+        assert!(!is_valid_package_ident("foo-b\u{00e5}r"));
+        assert!(!is_valid_package_ident("foo.bar.123"));
+        assert!(!is_valid_package_ident(".foo.bar"));
+        assert!(!is_valid_package_ident("foo.bar."));
+        assert!(!is_valid_package_ident("."));
+        assert!(!is_valid_package_ident("foo..bar"));
+    }
+
+    #[test]
+    fn test_create_device_config_ident() {
+        assert_eq!(
+            "com.foo.bar.some_flag",
+            create_device_config_ident("com.foo.bar", "some_flag").unwrap()
+        );
     }
 }
diff --git a/tools/aconfig/src/codegen_cpp.rs b/tools/aconfig/src/codegen_cpp.rs
index 2aeea6a..37b058d 100644
--- a/tools/aconfig/src/codegen_cpp.rs
+++ b/tools/aconfig/src/codegen_cpp.rs
@@ -14,29 +14,42 @@
  * limitations under the License.
  */
 
-use anyhow::Result;
+use anyhow::{ensure, Result};
 use serde::Serialize;
 use tinytemplate::TinyTemplate;
 
 use crate::aconfig::{FlagState, Permission};
 use crate::cache::{Cache, Item};
+use crate::codegen;
 use crate::commands::OutputFile;
 
 pub fn generate_cpp_code(cache: &Cache) -> Result<OutputFile> {
-    let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
+    let package = cache.package();
+    let class_elements: Vec<ClassElement> =
+        cache.iter().map(|item| create_class_element(package, item)).collect();
     let readwrite = class_elements.iter().any(|item| item.readwrite);
-    let namespace = cache.namespace().to_lowercase();
-    let context = Context { namespace: namespace.clone(), readwrite, class_elements };
+    let header = package.replace('.', "_");
+    let cpp_namespace = package.replace('.', "::");
+    ensure!(codegen::is_valid_name_ident(&header));
+    let context = Context {
+        header: header.clone(),
+        cpp_namespace,
+        package: package.to_string(),
+        readwrite,
+        class_elements,
+    };
     let mut template = TinyTemplate::new();
     template.add_template("cpp_code_gen", include_str!("../templates/cpp.template"))?;
     let contents = template.render("cpp_code_gen", &context)?;
-    let path = ["aconfig", &(namespace + ".h")].iter().collect();
+    let path = ["aconfig", &(header + ".h")].iter().collect();
     Ok(OutputFile { contents: contents.into(), path })
 }
 
 #[derive(Serialize)]
 struct Context {
-    pub namespace: String,
+    pub header: String,
+    pub cpp_namespace: String,
+    pub package: String,
     pub readwrite: bool,
     pub class_elements: Vec<ClassElement>,
 }
@@ -46,9 +59,11 @@
     pub readwrite: bool,
     pub default_value: String,
     pub flag_name: String,
+    pub device_config_namespace: String,
+    pub device_config_flag: String,
 }
 
-fn create_class_element(item: &Item) -> ClassElement {
+fn create_class_element(package: &str, item: &Item) -> ClassElement {
     ClassElement {
         readwrite: item.permission == Permission::ReadWrite,
         default_value: if item.state == FlagState::Enabled {
@@ -57,6 +72,9 @@
             "false".to_string()
         },
         flag_name: item.name.clone(),
+        device_config_namespace: item.namespace.to_string(),
+        device_config_flag: codegen::create_device_config_ident(package, &item.name)
+            .expect("values checked at cache creation time"),
     }
 }
 
@@ -69,13 +87,14 @@
 
     #[test]
     fn test_cpp_codegen_build_time_flag_only() {
-        let namespace = "my_namespace";
-        let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
+        let package = "com.example";
+        let mut builder = CacheBuilder::new(package.to_string()).unwrap();
         builder
             .add_flag_declaration(
                 Source::File("aconfig_one.txt".to_string()),
                 FlagDeclaration {
                     name: "my_flag_one".to_string(),
+                    namespace: "ns".to_string(),
                     description: "buildtime disable".to_string(),
                 },
             )
@@ -83,7 +102,7 @@
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
-                    namespace: namespace.to_string(),
+                    package: package.to_string(),
                     name: "my_flag_one".to_string(),
                     state: FlagState::Disabled,
                     permission: Permission::ReadOnly,
@@ -94,6 +113,7 @@
                 Source::File("aconfig_two.txt".to_string()),
                 FlagDeclaration {
                     name: "my_flag_two".to_string(),
+                    namespace: "ns".to_string(),
                     description: "buildtime enable".to_string(),
                 },
             )
@@ -101,7 +121,7 @@
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
-                    namespace: namespace.to_string(),
+                    package: package.to_string(),
                     name: "my_flag_two".to_string(),
                     state: FlagState::Enabled,
                     permission: Permission::ReadOnly,
@@ -109,31 +129,24 @@
             )
             .unwrap();
         let cache = builder.build();
-        let expect_content = r#"#ifndef my_namespace_HEADER_H
-        #define my_namespace_HEADER_H
-        #include "my_namespace.h"
+        let expect_content = r#"#ifndef com_example_HEADER_H
+        #define com_example_HEADER_H
 
-        namespace my_namespace {
+        namespace com::example {
 
-            class my_flag_one {
-                public:
-                    virtual const bool value() {
-                        return false;
-                    }
+            static const bool my_flag_one() {
+                return false;
             }
 
-            class my_flag_two {
-                public:
-                    virtual const bool value() {
-                        return true;
-                    }
+            static const bool my_flag_two() {
+                return true;
             }
 
         }
         #endif
         "#;
         let file = generate_cpp_code(&cache).unwrap();
-        assert_eq!("aconfig/my_namespace.h", file.path.to_str().unwrap());
+        assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
         assert_eq!(
             expect_content.replace(' ', ""),
             String::from_utf8(file.contents).unwrap().replace(' ', "")
@@ -142,13 +155,14 @@
 
     #[test]
     fn test_cpp_codegen_runtime_flag() {
-        let namespace = "my_namespace";
-        let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
+        let package = "com.example";
+        let mut builder = CacheBuilder::new(package.to_string()).unwrap();
         builder
             .add_flag_declaration(
                 Source::File("aconfig_one.txt".to_string()),
                 FlagDeclaration {
                     name: "my_flag_one".to_string(),
+                    namespace: "ns".to_string(),
                     description: "buildtime disable".to_string(),
                 },
             )
@@ -157,6 +171,7 @@
                 Source::File("aconfig_two.txt".to_string()),
                 FlagDeclaration {
                     name: "my_flag_two".to_string(),
+                    namespace: "ns".to_string(),
                     description: "runtime enable".to_string(),
                 },
             )
@@ -164,7 +179,7 @@
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
-                    namespace: namespace.to_string(),
+                    package: package.to_string(),
                     name: "my_flag_two".to_string(),
                     state: FlagState::Enabled,
                     permission: Permission::ReadWrite,
@@ -172,43 +187,39 @@
             )
             .unwrap();
         let cache = builder.build();
-        let expect_content = r#"#ifndef my_namespace_HEADER_H
-        #define my_namespace_HEADER_H
-        #include "my_namespace.h"
+        let expect_content = r#"#ifndef com_example_HEADER_H
+        #define com_example_HEADER_H
 
         #include <server_configurable_flags/get_flags.h>
         using namespace server_configurable_flags;
 
-        namespace my_namespace {
+        namespace com::example {
 
-            class my_flag_one {
-                public:
-                    virtual const bool value() {
-                        return GetServerConfigurableFlag(
-                            "my_namespace",
-                            "my_flag_one",
-                            "false") == "true";
-                    }
+            static const bool my_flag_one() {
+                return GetServerConfigurableFlag(
+                    "ns",
+                    "com.example.my_flag_one",
+                    "false") == "true";
             }
 
-            class my_flag_two {
-                public:
-                    virtual const bool value() {
-                        return GetServerConfigurableFlag(
-                            "my_namespace",
-                            "my_flag_two",
-                            "true") == "true";
-                    }
+            static const bool my_flag_two() {
+                return GetServerConfigurableFlag(
+                    "ns",
+                    "com.example.my_flag_two",
+                    "true") == "true";
             }
 
         }
         #endif
         "#;
         let file = generate_cpp_code(&cache).unwrap();
-        assert_eq!("aconfig/my_namespace.h", file.path.to_str().unwrap());
+        assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
         assert_eq!(
-            expect_content.replace(' ', ""),
-            String::from_utf8(file.contents).unwrap().replace(' ', "")
+            None,
+            crate::test::first_significant_code_diff(
+                expect_content,
+                &String::from_utf8(file.contents).unwrap()
+            )
         );
     }
 }
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index 98288e7..cf025cb 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -21,17 +21,19 @@
 
 use crate::aconfig::{FlagState, Permission};
 use crate::cache::{Cache, Item};
+use crate::codegen;
 use crate::commands::OutputFile;
 
 pub fn generate_java_code(cache: &Cache) -> Result<OutputFile> {
-    let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
+    let package = cache.package();
+    let class_elements: Vec<ClassElement> =
+        cache.iter().map(|item| create_class_element(package, item)).collect();
     let readwrite = class_elements.iter().any(|item| item.readwrite);
-    let namespace = cache.namespace();
-    let context = Context { namespace: namespace.to_string(), readwrite, class_elements };
+    let context = Context { package: package.to_string(), readwrite, class_elements };
     let mut template = TinyTemplate::new();
     template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
     let contents = template.render("java_code_gen", &context)?;
-    let mut path: PathBuf = ["aconfig", namespace].iter().collect();
+    let mut path: PathBuf = package.split('.').collect();
     // TODO: Allow customization of the java class name
     path.push("Flags.java");
     Ok(OutputFile { contents: contents.into(), path })
@@ -39,7 +41,7 @@
 
 #[derive(Serialize)]
 struct Context {
-    pub namespace: String,
+    pub package: String,
     pub readwrite: bool,
     pub class_elements: Vec<ClassElement>,
 }
@@ -49,21 +51,23 @@
     pub method_name: String,
     pub readwrite: bool,
     pub default_value: String,
-    pub feature_name: String,
-    pub flag_name: String,
+    pub device_config_namespace: String,
+    pub device_config_flag: String,
 }
 
-fn create_class_element(item: &Item) -> ClassElement {
+fn create_class_element(package: &str, item: &Item) -> ClassElement {
+    let device_config_flag = codegen::create_device_config_ident(package, &item.name)
+        .expect("values checked at cache creation time");
     ClassElement {
-        method_name: item.name.clone(),
+        method_name: item.name.replace('-', "_"),
         readwrite: item.permission == Permission::ReadWrite,
         default_value: if item.state == FlagState::Enabled {
             "true".to_string()
         } else {
             "false".to_string()
         },
-        feature_name: item.name.clone(),
-        flag_name: item.name.clone(),
+        device_config_namespace: item.namespace.clone(),
+        device_config_flag,
     }
 }
 
@@ -76,13 +80,14 @@
 
     #[test]
     fn test_generate_java_code() {
-        let namespace = "example";
-        let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
+        let package = "com.example";
+        let mut builder = CacheBuilder::new(package.to_string()).unwrap();
         builder
             .add_flag_declaration(
                 Source::File("test.txt".to_string()),
                 FlagDeclaration {
                     name: "test".to_string(),
+                    namespace: "ns".to_string(),
                     description: "buildtime enable".to_string(),
                 },
             )
@@ -91,6 +96,7 @@
                 Source::File("test2.txt".to_string()),
                 FlagDeclaration {
                     name: "test2".to_string(),
+                    namespace: "ns".to_string(),
                     description: "runtime disable".to_string(),
                 },
             )
@@ -98,7 +104,7 @@
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
-                    namespace: namespace.to_string(),
+                    package: package.to_string(),
                     name: "test".to_string(),
                     state: FlagState::Disabled,
                     permission: Permission::ReadOnly,
@@ -106,7 +112,7 @@
             )
             .unwrap();
         let cache = builder.build();
-        let expect_content = r#"package aconfig.example;
+        let expect_content = r#"package com.example;
 
         import android.provider.DeviceConfig;
 
@@ -118,8 +124,8 @@
 
             public static boolean test2() {
                 return DeviceConfig.getBoolean(
-                    "example",
-                    "test2__test2",
+                    "ns",
+                    "com.example.test2",
                     false
                 );
             }
@@ -127,10 +133,13 @@
         }
         "#;
         let file = generate_java_code(&cache).unwrap();
-        assert_eq!("aconfig/example/Flags.java", file.path.to_str().unwrap());
+        assert_eq!("com/example/Flags.java", file.path.to_str().unwrap());
         assert_eq!(
-            expect_content.replace(' ', ""),
-            String::from_utf8(file.contents).unwrap().replace(' ', "")
+            None,
+            crate::test::first_significant_code_diff(
+                expect_content,
+                &String::from_utf8(file.contents).unwrap()
+            )
         );
     }
 }
diff --git a/tools/aconfig/src/codegen_rust.rs b/tools/aconfig/src/codegen_rust.rs
index a48e464..98caeae 100644
--- a/tools/aconfig/src/codegen_rust.rs
+++ b/tools/aconfig/src/codegen_rust.rs
@@ -20,13 +20,18 @@
 
 use crate::aconfig::{FlagState, Permission};
 use crate::cache::{Cache, Item};
+use crate::codegen;
 use crate::commands::OutputFile;
 
 pub fn generate_rust_code(cache: &Cache) -> Result<OutputFile> {
-    let namespace = cache.namespace();
+    let package = cache.package();
     let parsed_flags: Vec<TemplateParsedFlag> =
-        cache.iter().map(|item| create_template_parsed_flag(namespace, item)).collect();
-    let context = TemplateContext { namespace: namespace.to_string(), parsed_flags };
+        cache.iter().map(|item| TemplateParsedFlag::new(package, item)).collect();
+    let context = TemplateContext {
+        package: package.to_string(),
+        parsed_flags,
+        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"))?;
     let contents = template.render("rust_code_gen", &context)?;
@@ -36,14 +41,16 @@
 
 #[derive(Serialize)]
 struct TemplateContext {
-    pub namespace: String,
+    pub package: String,
     pub parsed_flags: Vec<TemplateParsedFlag>,
+    pub modules: Vec<String>,
 }
 
 #[derive(Serialize)]
 struct TemplateParsedFlag {
     pub name: String,
-    pub fn_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
@@ -52,28 +59,32 @@
     pub is_read_write: bool,
 }
 
-#[allow(clippy::nonminimal_bool)]
-fn create_template_parsed_flag(namespace: &str, item: &Item) -> TemplateParsedFlag {
-    let template = TemplateParsedFlag {
-        name: item.name.clone(),
-        fn_name: format!("{}_{}", namespace, &item.name),
-        is_read_only_enabled: item.permission == Permission::ReadOnly
-            && item.state == FlagState::Enabled,
-        is_read_only_disabled: item.permission == Permission::ReadOnly
-            && item.state == FlagState::Disabled,
-        is_read_write: item.permission == Permission::ReadWrite,
-    };
-    #[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
+impl TemplateParsedFlag {
+    #[allow(clippy::nonminimal_bool)]
+    fn new(package: &str, item: &Item) -> Self {
+        let template = TemplateParsedFlag {
+            name: item.name.clone(),
+            device_config_namespace: item.namespace.to_string(),
+            device_config_flag: codegen::create_device_config_ident(package, &item.name)
+                .expect("values checked at cache creation time"),
+            is_read_only_enabled: item.permission == Permission::ReadOnly
+                && item.state == FlagState::Enabled,
+            is_read_only_disabled: item.permission == Permission::ReadOnly
+                && item.state == FlagState::Disabled,
+            is_read_write: item.permission == Permission::ReadWrite,
+        };
+        #[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
+    }
 }
 
 #[cfg(test)]
@@ -86,26 +97,41 @@
         let generated = generate_rust_code(&cache).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 {
 #[inline(always)]
-pub const fn r#test_disabled_ro() -> bool {
+pub const fn r#disabled_ro() -> bool {
     false
 }
 
 #[inline(always)]
-pub fn r#test_disabled_rw() -> bool {
-    flags_rust::GetServerConfigurableFlag("test", "disabled_rw", "false") == "true"
+pub fn r#disabled_rw() -> bool {
+    flags_rust::GetServerConfigurableFlag("aconfig_test", "com.android.aconfig.test.disabled_rw", "false") == "true"
 }
 
 #[inline(always)]
-pub const fn r#test_enabled_ro() -> bool {
+pub const fn r#enabled_ro() -> bool {
     true
 }
 
 #[inline(always)]
-pub fn r#test_enabled_rw() -> bool {
-    flags_rust::GetServerConfigurableFlag("test", "enabled_rw", "false") == "true"
+pub fn r#enabled_rw() -> bool {
+    flags_rust::GetServerConfigurableFlag("aconfig_test", "com.android.aconfig.test.enabled_rw", "false") == "true"
+}
+
+}
+}
+}
 }
 "#;
-        assert_eq!(expected.trim(), String::from_utf8(generated.contents).unwrap().trim());
+        assert_eq!(
+            None,
+            crate::test::first_significant_code_diff(
+                expected,
+                &String::from_utf8(generated.contents).unwrap()
+            )
+        );
     }
 }
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index 3ae72c6..586ba04 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -55,12 +55,8 @@
     pub contents: Vec<u8>,
 }
 
-pub fn create_cache(
-    namespace: &str,
-    declarations: Vec<Input>,
-    values: Vec<Input>,
-) -> Result<Cache> {
-    let mut builder = CacheBuilder::new(namespace.to_owned())?;
+pub fn create_cache(package: &str, declarations: Vec<Input>, values: Vec<Input>) -> Result<Cache> {
+    let mut builder = CacheBuilder::new(package.to_owned())?;
 
     for mut input in declarations {
         let mut contents = String::new();
@@ -68,11 +64,11 @@
         let dec_list = FlagDeclarations::try_from_text_proto(&contents)
             .with_context(|| format!("Failed to parse {}", input.source))?;
         ensure!(
-            namespace == dec_list.namespace,
-            "Failed to parse {}: expected namespace {}, got {}",
+            package == dec_list.package,
+            "Failed to parse {}: expected package {}, got {}",
             input.source,
-            namespace,
-            dec_list.namespace
+            package,
+            dec_list.package
         );
         for d in dec_list.flags.into_iter() {
             builder.add_flag_declaration(input.source.clone(), d)?;
@@ -110,8 +106,9 @@
     for item in sort_and_iter_items(caches).filter(|item| item.permission == Permission::ReadWrite)
     {
         let line = format!(
-            "{}/{}:{}\n",
+            "{}:{}.{}={}\n",
             item.namespace,
+            item.package,
             item.name,
             match item.state {
                 FlagState::Enabled => "enabled",
@@ -129,7 +126,7 @@
     {
         let line = format!(
             "persist.device_config.{}.{}={}\n",
-            item.namespace,
+            item.package,
             item.name,
             match item.state {
                 FlagState::Enabled => "true",
@@ -155,7 +152,7 @@
             for item in sort_and_iter_items(caches) {
                 let line = format!(
                     "{}/{}: {:?} {:?}\n",
-                    item.namespace, item.name, item.state, item.permission
+                    item.package, item.name, item.state, item.permission
                 );
                 output.extend_from_slice(line.as_bytes());
             }
@@ -181,7 +178,7 @@
 }
 
 fn sort_and_iter_caches(mut caches: Vec<Cache>) -> impl Iterator<Item = Cache> {
-    caches.sort_by_cached_key(|cache| cache.namespace().to_string());
+    caches.sort_by_cached_key(|cache| cache.package().to_string());
     caches.into_iter()
 }
 
@@ -190,55 +187,58 @@
     use super::*;
     use crate::aconfig::{FlagState, Permission};
 
-    fn create_test_cache_ns1() -> Cache {
+    fn create_test_cache_com_example() -> Cache {
         let s = r#"
-        namespace: "ns1"
+        package: "com.example"
         flag {
             name: "a"
+            namespace: "ns"
             description: "Description of a"
         }
         flag {
             name: "b"
+            namespace: "ns"
             description: "Description of b"
         }
         "#;
         let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
         let o = r#"
         flag_value {
-            namespace: "ns1"
+            package: "com.example"
             name: "a"
             state: DISABLED
             permission: READ_ONLY
         }
         "#;
         let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
-        create_cache("ns1", declarations, values).unwrap()
+        create_cache("com.example", declarations, values).unwrap()
     }
 
-    fn create_test_cache_ns2() -> Cache {
+    fn create_test_cache_com_other() -> Cache {
         let s = r#"
-        namespace: "ns2"
+        package: "com.other"
         flag {
             name: "c"
+            namespace: "ns"
             description: "Description of c"
         }
         "#;
         let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
         let o = r#"
         flag_value {
-            namespace: "ns2"
+            package: "com.other"
             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()
+        create_cache("com.other", declarations, values).unwrap()
     }
 
     #[test]
     fn test_create_cache() {
-        let caches = create_test_cache_ns1(); // calls create_cache
+        let caches = create_test_cache_com_example(); // 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);
@@ -249,7 +249,7 @@
         let caches = vec![crate::test::create_cache()];
         let bytes = create_device_config_defaults(caches).unwrap();
         let text = std::str::from_utf8(&bytes).unwrap();
-        assert_eq!("test/disabled_rw:disabled\ntest/enabled_rw:enabled\n", text);
+        assert_eq!("aconfig_test:com.android.aconfig.test.disabled_rw=disabled\naconfig_test:com.android.aconfig.test.enabled_rw=enabled\n", text);
     }
 
     #[test]
@@ -257,12 +257,12 @@
         let caches = vec![crate::test::create_cache()];
         let bytes = create_device_config_sysprops(caches).unwrap();
         let text = std::str::from_utf8(&bytes).unwrap();
-        assert_eq!("persist.device_config.test.disabled_rw=false\npersist.device_config.test.enabled_rw=true\n", text);
+        assert_eq!("persist.device_config.com.android.aconfig.test.disabled_rw=false\npersist.device_config.com.android.aconfig.test.enabled_rw=true\n", text);
     }
 
     #[test]
     fn test_dump_text_format() {
-        let caches = vec![create_test_cache_ns1()];
+        let caches = vec![create_test_cache_com_example()];
         let bytes = dump_cache(caches, DumpFormat::Text).unwrap();
         let text = std::str::from_utf8(&bytes).unwrap();
         assert!(text.contains("a: Disabled"));
@@ -273,7 +273,7 @@
         use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint};
         use protobuf::Message;
 
-        let caches = vec![create_test_cache_ns1()];
+        let caches = vec![create_test_cache_com_example()];
         let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
         let actual = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
 
@@ -284,7 +284,7 @@
 
         let item =
             actual.parsed_flag.iter().find(|item| item.name == Some("b".to_string())).unwrap();
-        assert_eq!(item.namespace(), "ns1");
+        assert_eq!(item.package(), "com.example");
         assert_eq!(item.name(), "b");
         assert_eq!(item.description(), "Description of b");
         assert_eq!(item.state(), ProtoFlagState::DISABLED);
@@ -298,18 +298,22 @@
 
     #[test]
     fn test_dump_multiple_caches() {
-        let caches = vec![create_test_cache_ns1(), create_test_cache_ns2()];
+        let caches = vec![create_test_cache_com_example(), create_test_cache_com_other()];
         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()))
+                .map(|parsed_flag| format!("{}/{}", parsed_flag.package(), parsed_flag.name()))
                 .collect::<Vec<_>>(),
-            vec!["ns1/a".to_string(), "ns1/b".to_string(), "ns2/c".to_string()]
+            vec![
+                "com.example/a".to_string(),
+                "com.example/b".to_string(),
+                "com.other/c".to_string()
+            ]
         );
 
-        let caches = vec![create_test_cache_ns2(), create_test_cache_ns1()];
+        let caches = vec![create_test_cache_com_other(), create_test_cache_com_example()];
         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 dab0191..5a820d9 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -44,7 +44,7 @@
         .subcommand_required(true)
         .subcommand(
             Command::new("create-cache")
-                .arg(Arg::new("namespace").long("namespace").required(true))
+                .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("cache").long("cache").required(true)),
@@ -134,10 +134,10 @@
     let matches = cli().get_matches();
     match matches.subcommand() {
         Some(("create-cache", sub_matches)) => {
-            let namespace = get_required_arg::<String>(sub_matches, "namespace")?;
+            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 cache = commands::create_cache(namespace, declarations, values)?;
+            let cache = commands::create_cache(package, declarations, values)?;
             let path = get_required_arg::<String>(sub_matches, "cache")?;
             let file = fs::File::create(path)?;
             cache.write_to_writer(file)?;
diff --git a/tools/aconfig/src/test.rs b/tools/aconfig/src/test.rs
index 621381a..76ef005 100644
--- a/tools/aconfig/src/test.rs
+++ b/tools/aconfig/src/test.rs
@@ -18,27 +18,76 @@
 pub mod test_utils {
     use crate::cache::Cache;
     use crate::commands::{Input, Source};
+    use itertools;
 
     pub fn create_cache() -> Cache {
         crate::commands::create_cache(
-            "test",
+            "com.android.aconfig.test",
             vec![Input {
-                source: Source::File("testdata/test.aconfig".to_string()),
-                reader: Box::new(include_bytes!("../testdata/test.aconfig").as_slice()),
+                source: Source::File("tests/test.aconfig".to_string()),
+                reader: Box::new(include_bytes!("../tests/test.aconfig").as_slice()),
             }],
             vec![
                 Input {
-                    source: Source::File("testdata/first.values".to_string()),
-                    reader: Box::new(include_bytes!("../testdata/first.values").as_slice()),
+                    source: Source::File("tests/first.values".to_string()),
+                    reader: Box::new(include_bytes!("../tests/first.values").as_slice()),
                 },
                 Input {
-                    source: Source::File("testdata/test.aconfig".to_string()),
-                    reader: Box::new(include_bytes!("../testdata/second.values").as_slice()),
+                    source: Source::File("tests/test.aconfig".to_string()),
+                    reader: Box::new(include_bytes!("../tests/second.values").as_slice()),
                 },
             ],
         )
         .unwrap()
     }
+
+    pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> {
+        let a = a.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
+        let b = b.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
+        match itertools::diff_with(a, b, |left, right| left == right) {
+            Some(itertools::Diff::FirstMismatch(_, mut left, mut right)) => {
+                Some(format!("'{}' vs '{}'", left.next().unwrap(), right.next().unwrap()))
+            }
+            Some(itertools::Diff::Shorter(_, mut left)) => {
+                Some(format!("LHS trailing data: '{}'", left.next().unwrap()))
+            }
+            Some(itertools::Diff::Longer(_, mut right)) => {
+                Some(format!("RHS trailing data: '{}'", right.next().unwrap()))
+            }
+            None => None,
+        }
+    }
+
+    #[test]
+    fn test_first_significant_code_diff() {
+        assert!(first_significant_code_diff("", "").is_none());
+        assert!(first_significant_code_diff("   a", "\n\na\n").is_none());
+        let a = r#"
+        public class A {
+            private static final String FOO = "FOO";
+            public static void main(String[] args) {
+                System.out.println("FOO=" + FOO);
+            }
+        }
+        "#;
+        let b = r#"
+        public class A {
+            private static final String FOO = "BAR";
+            public static void main(String[] args) {
+                System.out.println("foo=" + FOO);
+            }
+        }
+        "#;
+        assert_eq!(Some(r#"'private static final String FOO = "FOO";' vs 'private static final String FOO = "BAR";'"#.to_string()), first_significant_code_diff(a, b));
+        assert_eq!(
+            Some("LHS trailing data: 'b'".to_string()),
+            first_significant_code_diff("a\nb", "a")
+        );
+        assert_eq!(
+            Some("RHS trailing data: 'b'".to_string()),
+            first_significant_code_diff("a", "a\nb")
+        );
+    }
 }
 
 #[cfg(test)]
diff --git a/tools/aconfig/templates/cpp.template b/tools/aconfig/templates/cpp.template
index ae8b59f..aa36d94 100644
--- a/tools/aconfig/templates/cpp.template
+++ b/tools/aconfig/templates/cpp.template
@@ -1,24 +1,20 @@
-#ifndef {namespace}_HEADER_H
-#define {namespace}_HEADER_H
-#include "{namespace}.h"
+#ifndef {header}_HEADER_H
+#define {header}_HEADER_H
 {{ if readwrite }}
 #include <server_configurable_flags/get_flags.h>
 using namespace server_configurable_flags;
 {{ endif }}
-namespace {namespace} \{
+namespace {cpp_namespace} \{
     {{ for item in class_elements}}
-    class {item.flag_name} \{
-        public:
-            virtual const bool value() \{
-                {{ if item.readwrite- }}
-                return GetServerConfigurableFlag(
-                    "{namespace}",
-                    "{item.flag_name}",
-                    "{item.default_value}") == "true";
-                {{ -else- }}
-                return {item.default_value};
-                {{ -endif }}
-            }
+    static const bool {item.flag_name}() \{
+        {{ if item.readwrite- }}
+        return GetServerConfigurableFlag(
+            "{item.device_config_namespace}",
+            "{item.device_config_flag}",
+            "{item.default_value}") == "true";
+        {{ -else- }}
+            return {item.default_value};
+        {{ -endif }}
     }
     {{ endfor }}
 }
diff --git a/tools/aconfig/templates/java.template b/tools/aconfig/templates/java.template
index 30c7ad7..a3d3319 100644
--- a/tools/aconfig/templates/java.template
+++ b/tools/aconfig/templates/java.template
@@ -1,4 +1,4 @@
-package aconfig.{namespace};
+package {package};
 {{ if readwrite }}
 import android.provider.DeviceConfig;
 {{ endif }}
@@ -7,8 +7,8 @@
     public static boolean {item.method_name}() \{
         {{ if item.readwrite- }}
         return DeviceConfig.getBoolean(
-            "{namespace}",
-            "{item.feature_name}__{item.flag_name}",
+            "{item.device_config_namespace}",
+            "{item.device_config_flag}",
             {item.default_value}
         );
         {{ -else- }}
diff --git a/tools/aconfig/templates/rust.template b/tools/aconfig/templates/rust.template
index d7f4e8d..d914943 100644
--- a/tools/aconfig/templates/rust.template
+++ b/tools/aconfig/templates/rust.template
@@ -1,23 +1,29 @@
+{{- for mod in modules -}}
+pub mod {mod} \{
+{{ endfor -}}
 {{- for parsed_flag in parsed_flags -}}
 {{- if parsed_flag.is_read_only_disabled -}}
 #[inline(always)]
-pub const fn r#{parsed_flag.fn_name}() -> bool \{
+pub const fn r#{parsed_flag.name}() -> bool \{
     false
 }
 
 {{ endif -}}
 {{- if parsed_flag.is_read_only_enabled -}}
 #[inline(always)]
-pub const fn r#{parsed_flag.fn_name}() -> bool \{
+pub const fn r#{parsed_flag.name}() -> bool \{
     true
 }
 
 {{ endif -}}
 {{- if parsed_flag.is_read_write -}}
 #[inline(always)]
-pub fn r#{parsed_flag.fn_name}() -> bool \{
-    flags_rust::GetServerConfigurableFlag("{namespace}", "{parsed_flag.name}", "false") == "true"
+pub fn r#{parsed_flag.name}() -> bool \{
+    flags_rust::GetServerConfigurableFlag("{parsed_flag.device_config_namespace}", "{parsed_flag.device_config_flag}", "false") == "true"
 }
 
 {{ endif -}}
 {{- endfor -}}
+{{- for mod in modules -}}
+}
+{{ endfor -}}
diff --git a/tools/aconfig/tests/AconfigTest.java b/tools/aconfig/tests/AconfigTest.java
new file mode 100644
index 0000000..5db490b
--- /dev/null
+++ b/tools/aconfig/tests/AconfigTest.java
@@ -0,0 +1,37 @@
+import static com.android.aconfig.test.Flags.disabled_ro;
+import static com.android.aconfig.test.Flags.disabled_rw;
+import static com.android.aconfig.test.Flags.enabled_ro;
+import static com.android.aconfig.test.Flags.enabled_rw;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AconfigTest {
+    @Test
+    public void testDisabledReadOnlyFlag() {
+        assertFalse(disabled_ro());
+    }
+
+    @Test
+    public void testEnabledReadOnlyFlag() {
+        // TODO: change to assertTrue(enabled_ro()) when the build supports reading tests/*.values
+        // (currently all flags are assigned the default READ_ONLY + DISABLED)
+        assertFalse(enabled_ro());
+    }
+
+    @Test
+    public void testDisabledReadWriteFlag() {
+        assertFalse(disabled_rw());
+    }
+
+    @Test
+    public void testEnabledReadWriteFlag() {
+        // TODO: change to assertTrue(enabled_rw()) when the build supports reading tests/*.values
+        // (currently all flags are assigned the default READ_ONLY + DISABLED)
+        assertFalse(enabled_rw());
+    }
+}
diff --git a/tools/aconfig/tests/AndroidManifest.xml b/tools/aconfig/tests/AndroidManifest.xml
new file mode 100644
index 0000000..04002e6
--- /dev/null
+++ b/tools/aconfig/tests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="aconfig.test.java">
+
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+
+    <application>
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="aconfig.test.java"
+        android:label="aconfig integration tests (java)" />
+</manifest>
diff --git a/tools/aconfig/testdata/first.values b/tools/aconfig/tests/first.values
similarity index 67%
rename from tools/aconfig/testdata/first.values
rename to tools/aconfig/tests/first.values
index 3c49111..e524404 100644
--- a/tools/aconfig/testdata/first.values
+++ b/tools/aconfig/tests/first.values
@@ -1,17 +1,17 @@
 flag_value {
-    namespace: "test"
+    package: "com.android.aconfig.test"
     name: "disabled_ro"
     state: DISABLED
     permission: READ_ONLY
 }
 flag_value {
-    namespace: "test"
+    package: "com.android.aconfig.test"
     name: "enabled_ro"
     state: DISABLED
     permission: READ_WRITE
 }
 flag_value {
-    namespace: "test"
+    package: "com.android.aconfig.test"
     name: "enabled_rw"
     state: ENABLED
     permission: READ_WRITE
diff --git a/tools/aconfig/testdata/second.values b/tools/aconfig/tests/second.values
similarity index 67%
rename from tools/aconfig/testdata/second.values
rename to tools/aconfig/tests/second.values
index 3fe11ab..aa09cf6 100644
--- a/tools/aconfig/testdata/second.values
+++ b/tools/aconfig/tests/second.values
@@ -1,5 +1,5 @@
 flag_value {
-    namespace: "test"
+    package: "com.android.aconfig.test"
     name: "enabled_ro"
     state: ENABLED
     permission: READ_ONLY
diff --git a/tools/aconfig/testdata/test.aconfig b/tools/aconfig/tests/test.aconfig
similarity index 84%
rename from tools/aconfig/testdata/test.aconfig
rename to tools/aconfig/tests/test.aconfig
index 986a526..d09396a 100644
--- a/tools/aconfig/testdata/test.aconfig
+++ b/tools/aconfig/tests/test.aconfig
@@ -1,10 +1,11 @@
-namespace: "test"
+package: "com.android.aconfig.test"
 
 # This flag's final value is calculated from:
 # - test.aconfig: DISABLED + READ_WRITE (default)
 # - first.values: DISABLED + READ_ONLY
 flag {
     name: "disabled_ro"
+    namespace: "aconfig_test"
     description: "This flag is DISABLED + READ_ONLY"
 }
 
@@ -12,6 +13,7 @@
 # - test.aconfig: DISABLED + READ_WRITE (default)
 flag {
     name: "disabled_rw"
+    namespace: "aconfig_test"
     description: "This flag is DISABLED + READ_WRITE"
 }
 
@@ -21,6 +23,7 @@
 # - second.values: ENABLED + READ_ONLY
 flag {
     name: "enabled_ro"
+    namespace: "aconfig_test"
     description: "This flag is ENABLED + READ_ONLY"
 }
 
@@ -29,5 +32,6 @@
 # - first.values: ENABLED + READ_WRITE
 flag {
     name: "enabled_rw"
+    namespace: "aconfig_test"
     description: "This flag is ENABLED + READ_WRITE"
 }
diff --git a/tools/rbcrun/host.go b/tools/rbcrun/host.go
index a0fb9e1..1d68d43 100644
--- a/tools/rbcrun/host.go
+++ b/tools/rbcrun/host.go
@@ -34,9 +34,10 @@
 	ExecutionModeMake ExecutionMode = iota
 )
 
+const allowExternalEntrypointKey = "allowExternalEntrypoint"
 const callerDirKey = "callerDir"
-const shellKey = "shell"
 const executionModeKey = "executionMode"
+const shellKey = "shell"
 
 type modentry struct {
 	globals starlark.StringDict
@@ -64,7 +65,7 @@
 
 // Takes a module name (the first argument to the load() function) and returns the path
 // it's trying to load, stripping out leading //, and handling leading :s.
-func cleanModuleName(moduleName string, callerDir string) (string, error) {
+func cleanModuleName(moduleName string, callerDir string, allowExternalPaths bool) (string, error) {
 	if strings.Count(moduleName, ":") > 1 {
 		return "", fmt.Errorf("at most 1 colon must be present in starlark path: %s", moduleName)
 	}
@@ -82,7 +83,7 @@
 	} else if strings.HasPrefix(moduleName, ":") {
 		moduleName = moduleName[1:]
 		localLoad = true
-	} else {
+	} else if !allowExternalPaths {
 		return "", fmt.Errorf("load path must start with // or :")
 	}
 
@@ -93,11 +94,13 @@
 	if filepath.Clean(moduleName) != moduleName {
 		return "", fmt.Errorf("load path must be clean, found: %s, expected: %s", moduleName, filepath.Clean(moduleName))
 	}
-	if strings.HasPrefix(moduleName, "../") {
-		return "", fmt.Errorf("load path must not start with ../: %s", moduleName)
-	}
-	if strings.HasPrefix(moduleName, "/") {
-		return "", fmt.Errorf("load path starts with /, use // for a absolute path: %s", moduleName)
+	if !allowExternalPaths {
+		if strings.HasPrefix(moduleName, "../") {
+			return "", fmt.Errorf("load path must not start with ../: %s", moduleName)
+		}
+		if strings.HasPrefix(moduleName, "/") {
+			return "", fmt.Errorf("load path starts with /, use // for a absolute path: %s", moduleName)
+		}
 	}
 
 	if localLoad {
@@ -114,17 +117,18 @@
 // bound to None if file is missing.
 func loader(thread *starlark.Thread, module string) (starlark.StringDict, error) {
 	mode := thread.Local(executionModeKey).(ExecutionMode)
+	allowExternalEntrypoint := thread.Local(allowExternalEntrypointKey).(bool)
 	var defaultSymbol string
 	mustLoad := true
 	if mode == ExecutionModeRbc {
 		pipePos := strings.LastIndex(module, "|")
-		mustLoad = pipePos < 0
-		if !mustLoad {
+		if pipePos >= 0 {
+			mustLoad = false
 			defaultSymbol = module[pipePos+1:]
 			module = module[:pipePos]
 		}
 	}
-	modulePath, err := cleanModuleName(module, thread.Local(callerDirKey).(string))
+	modulePath, err := cleanModuleName(module, thread.Local(callerDirKey).(string), allowExternalEntrypoint)
 	if err != nil {
 		return nil, err
 	}
@@ -155,9 +159,11 @@
 				childThread.SetLocal(testReporterKey, v)
 			}
 
+			// Only the entrypoint starlark file allows external loads.
+			childThread.SetLocal(allowExternalEntrypointKey, false)
 			childThread.SetLocal(callerDirKey, filepath.Dir(modulePath))
-			childThread.SetLocal(shellKey, thread.Local(shellKey))
 			childThread.SetLocal(executionModeKey, mode)
+			childThread.SetLocal(shellKey, thread.Local(shellKey))
 			if mode == ExecutionModeRbc {
 				globals, err := starlark.ExecFile(childThread, modulePath, nil, rbcBuiltins)
 				e = &modentry{globals, err}
@@ -318,7 +324,7 @@
 // * src is an optional source of bytes to use instead of filename
 //   (it can be a string, or a byte array, or an io.Reader instance)
 // Returns the top-level starlark variables, the list of starlark files loaded, and an error
-func Run(filename string, src interface{}, mode ExecutionMode) (starlark.StringDict, []string, error) {
+func Run(filename string, src interface{}, mode ExecutionMode, allowExternalEntrypoint bool) (starlark.StringDict, []string, error) {
 	// NOTE(asmundak): OS-specific. Behave similar to Linux `system` call,
 	// which always uses /bin/sh to run the command
 	shellPath := "/bin/sh"
@@ -347,7 +353,7 @@
 		if err != nil {
 			return nil, nil, err
 		}
-		if strings.HasPrefix(filename, "../") {
+		if !allowExternalEntrypoint && strings.HasPrefix(filename, "../") {
 			return nil, nil, fmt.Errorf("path could not be made relative to workspace root: %s", filename)
 		}
 	} else {
@@ -358,9 +364,10 @@
 	moduleCache[filename] = nil
 
 	var results starlark.StringDict
+	mainThread.SetLocal(allowExternalEntrypointKey, allowExternalEntrypoint)
 	mainThread.SetLocal(callerDirKey, filepath.Dir(filename))
-	mainThread.SetLocal(shellKey, shellPath)
 	mainThread.SetLocal(executionModeKey, mode)
+	mainThread.SetLocal(shellKey, shellPath)
 	if mode == ExecutionModeRbc {
 		results, err = starlark.ExecFile(mainThread, filename, src, rbcBuiltins)
 	} else if mode == ExecutionModeMake {
diff --git a/tools/rbcrun/host_test.go b/tools/rbcrun/host_test.go
index 10cac62..10ce55e 100644
--- a/tools/rbcrun/host_test.go
+++ b/tools/rbcrun/host_test.go
@@ -125,6 +125,7 @@
 	if err := os.Chdir(filepath.Dir(dir)); err != nil {
 		t.Fatal(err)
 	}
+	thread.SetLocal(allowExternalEntrypointKey, false)
 	thread.SetLocal(callerDirKey, dir)
 	thread.SetLocal(executionModeKey, ExecutionModeRbc)
 	if _, err := starlark.ExecFile(thread, "testdata/load.star", nil, rbcBuiltins); err != nil {
diff --git a/tools/rbcrun/rbcrun/rbcrun.go b/tools/rbcrun/rbcrun/rbcrun.go
index b5182f0..a15b867 100644
--- a/tools/rbcrun/rbcrun/rbcrun.go
+++ b/tools/rbcrun/rbcrun/rbcrun.go
@@ -26,6 +26,7 @@
 )
 
 var (
+	allowExternalEntrypoint = flag.Bool("allow_external_entrypoint", false, "allow the entrypoint starlark file to be outside of the source tree")
 	modeFlag  = flag.String("mode", "", "the general behavior of rbcrun. Can be \"rbc\" or \"make\". Required.")
 	rootdir  = flag.String("d", ".", "the value of // for load paths")
 	perfFile = flag.String("perf", "", "save performance data")
@@ -159,7 +160,7 @@
 			quit("%s\n", err)
 		}
 	}
-	variables, loadedStarlarkFiles, err := rbcrun.Run(filename, nil, mode)
+	variables, loadedStarlarkFiles, err := rbcrun.Run(filename, nil, mode, *allowExternalEntrypoint)
 	rc := 0
 	if *perfFile != "" {
 		if err2 := starlark.StopProfile(); err2 != nil {
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index afbe81a..4c0d391 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -1063,6 +1063,8 @@
       # ZIP_STORED.
       common.ZipWriteStr(output_zip, care_map_name, care_map_data,
                          compress_type=zipfile.ZIP_STORED)
+      # break here to avoid going into else when care map has been handled
+      break
     else:
       logger.warning("Cannot find care map file in target_file package")
 
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index 63a863e..466cafb 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -846,6 +846,11 @@
     if os.path.exists(dynamic_partition_info):
       cmd.extend(["--dynamic_partition_info_file", dynamic_partition_info])
 
+    apex_info = os.path.join(
+      target_dir, "META", "apex_info.pb")
+    if os.path.exists(apex_info):
+      cmd.extend(["--apex_info_file", apex_info])
+
     major_version, minor_version = ParseUpdateEngineConfig(
         os.path.join(target_dir, "META", "update_engine_config.txt"))
     if source_file: