Merge "Keep track of which test infos use which target" into main
diff --git a/ci/Android.bp b/ci/Android.bp
index 3f28be4..757767c 100644
--- a/ci/Android.bp
+++ b/ci/Android.bp
@@ -35,11 +35,6 @@
     data: [
         ":py3-cmd",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
 }
 
 // This test is only intended to be run locally since it's slow, not hermetic,
@@ -64,11 +59,6 @@
     test_options: {
         unit_test: false,
     },
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
 }
 
 python_test_host {
@@ -88,11 +78,6 @@
     data: [
         ":py3-cmd",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
 }
 
 python_binary_host {
diff --git a/ci/test_discovery_agent.py b/ci/test_discovery_agent.py
index 008ee47..6812b07 100644
--- a/ci/test_discovery_agent.py
+++ b/ci/test_discovery_agent.py
@@ -30,6 +30,10 @@
 
   _TRADEFED_TEST_ZIP_REGEXES_LIST_KEY = "TestZipRegexes"
 
+  _TRADEFED_TEST_MODULES_LIST_KEY = "TestModules"
+
+  _TRADEFED_TEST_DEPENDENCIES_LIST_KEY = "TestDependencies"
+
   _TRADEFED_DISCOVERY_OUTPUT_FILE_NAME = "test_discovery_agent.txt"
 
   def __init__(
@@ -49,7 +53,7 @@
       A list of test zip regexes that TF is going to try to pull files from.
     """
     test_discovery_output_file_name = os.path.join(
-        os.environ.get('TOP'), 'out', self._TRADEFED_DISCOVERY_OUTPUT_FILE_NAME
+        os.environ.get("TOP"), "out", self._TRADEFED_DISCOVERY_OUTPUT_FILE_NAME
     )
     with open(
         test_discovery_output_file_name, mode="w+t"
@@ -89,14 +93,58 @@
         raise TestDiscoveryError("No test zip regexes returned")
       return data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY]
 
-  def discover_test_modules(self) -> list[str]:
-    """Discover test modules from TradeFed.
+  def discover_test_mapping_test_modules(self) -> (list[str], list[str]):
+    """Discover test mapping test modules and dependencies from TradeFed.
 
     Returns:
-      A list of test modules that TradeFed is going to execute based on the
+      A tuple that contains a list of test modules and a list of test
+      dependencies that TradeFed is going to execute based on the
       TradeFed test args.
     """
-    return []
+    test_discovery_output_file_name = os.path.join(
+        os.environ.get("TOP"), "out", self._TRADEFED_DISCOVERY_OUTPUT_FILE_NAME
+    )
+    with open(
+        test_discovery_output_file_name, mode="w+t"
+    ) as test_discovery_output_file:
+      java_args = []
+      java_args.append("prebuilts/jdk/jdk21/linux-x86/bin/java")
+      java_args.append("-cp")
+      java_args.append(
+          self.create_classpath(self.tradefed_jar_relevant_files_path)
+      )
+      java_args.append(
+          "com.android.tradefed.observatory.TestMappingDiscoveryAgent"
+      )
+      java_args.extend(self.tradefed_args)
+      env = os.environ.copy()
+      env.update({"TF_TEST_MAPPING_ZIP_FILE": self.test_mapping_zip_path})
+      env.update({"DISCOVERY_OUTPUT_FILE": test_discovery_output_file.name})
+      logging.info(f"Calling test discovery with args: {java_args}")
+      try:
+        result = subprocess.run(args=java_args, env=env, text=True, check=True)
+        logging.info(f"Test discovery agent output: {result.stdout}")
+      except subprocess.CalledProcessError as e:
+        raise TestDiscoveryError(
+            f"Failed to run test discovery, strout: {e.stdout}, strerr:"
+            f" {e.stderr}, returncode: {e.returncode}"
+        )
+      data = json.loads(test_discovery_output_file.read())
+      logging.info(f"Test discovery result file content: {data}")
+      if (
+          self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY in data
+          and data[self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY]
+      ):
+        raise TestDiscoveryError("No possible test discovery")
+      if (
+          data[self._TRADEFED_TEST_MODULES_LIST_KEY] is None
+          or data[self._TRADEFED_TEST_MODULES_LIST_KEY] is []
+      ):
+        raise TestDiscoveryError("No test modules returned")
+      return (
+          data[self._TRADEFED_TEST_MODULES_LIST_KEY],
+          data[self._TRADEFED_TEST_DEPENDENCIES_LIST_KEY],
+      )
 
   def create_classpath(self, directory):
     """Creates a classpath string from all .jar files in the given directory.
diff --git a/core/Makefile b/core/Makefile
index d0e2f15..1bf25e4 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -84,21 +84,6 @@
 endif
 
 
-###########################################################
-# Get the module names suitable for ALL_MODULES.* variables that are installed
-# for a given partition
-#
-# $(1): Partition
-###########################################################
-define register-names-for-partition
-$(sort $(foreach m,$(product_MODULES),\
-	$(if $(filter $(PRODUCT_OUT)/$(strip $(1))/%, $(ALL_MODULES.$(m).INSTALLED)), \
-		$(m)
-	) \
-))
-endef
-
-
 # Release & Aconfig Flags
 # -----------------------------------------------------------------
 include $(BUILD_SYSTEM)/packaging/flags.mk
@@ -790,7 +775,13 @@
 intermediates := \
 	$(call intermediates-dir-for,PACKAGING,apkcerts)
 APKCERTS_FILE := $(intermediates)/$(name).txt
-all_apkcerts_files := $(sort $(foreach p,$(PACKAGES),$(PACKAGES.$(p).APKCERTS_FILE)))
+ifeq ($(RELEASE_APKCERTS_INSTALL_ONLY), true)
+  all_apkcerts_packages := $(filter $(call product-installed-modules,$(INTERNAL_PRODUCT)),$(PACKAGES))
+else
+  all_apkcerts_packages := $(PACKAGES)
+endif
+all_apkcerts_files := $(sort $(foreach p,$(all_apkcerts_packages),$(PACKAGES.$(p).APKCERTS_FILE)))
+
 $(APKCERTS_FILE): $(all_apkcerts_files)
 # We don't need to really build all the modules.
 # TODO: rebuild APKCERTS_FILE if any app change its cert.
@@ -798,7 +789,7 @@
 	@echo APK certs list: $@
 	@mkdir -p $(dir $@)
 	@rm -f $@
-	$(foreach p,$(sort $(PACKAGES)),\
+	$(foreach p,$(sort $(all_apkcerts_packages)),\
 	  $(if $(PACKAGES.$(p).APKCERTS_FILE),\
 	    $(call _apkcerts_merge,$(PACKAGES.$(p).APKCERTS_FILE), $@),\
 	    $(if $(PACKAGES.$(p).EXTERNAL_KEY),\
@@ -1926,15 +1917,6 @@
 # need no associated notice file on the device UI.
 exclude_target_dirs := apex
 
-# TODO(b/69865032): Make PRODUCT_NOTICE_SPLIT the default behavior.
-ifneq ($(PRODUCT_NOTICE_SPLIT),true)
-#target_notice_file_html := $(TARGET_OUT_INTERMEDIATES)/NOTICE.html
-target_notice_file_html_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE.html.gz
-installed_notice_html_or_xml_gz := $(TARGET_OUT)/etc/NOTICE.html.gz
-
-$(call declare-0p-target,$(target_notice_file_html_gz))
-$(call declare-0p-target,$(installed_notice_html_or_xml_gz))
-else
 # target_notice_file_xml := $(TARGET_OUT_INTERMEDIATES)/NOTICE.xml
 target_notice_file_xml_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE.xml.gz
 installed_notice_html_or_xml_gz := $(TARGET_OUT)/etc/NOTICE.xml.gz
@@ -2054,8 +2036,6 @@
 system_notice_file_message := "Notices for files contained in the system filesystem image in this directory:"
 endif
 
-endif # PRODUCT_NOTICE_SPLIT
-
 ALL_DEFAULT_INSTALLED_MODULES += $(installed_notice_html_or_xml_gz)
 
 need_vendor_notice:=false
@@ -5239,7 +5219,9 @@
 # apexd_host scans/activates APEX files and writes /apex/apex-info-list.xml
 # Note that `@echo $(PRIVATE_APEX_FILES)` line is added to trigger the rule when the APEX list is changed.
 $(APEX_INFO_FILE): PRIVATE_APEX_FILES := $(apex_files)
-$(APEX_INFO_FILE): $(HOST_OUT_EXECUTABLES)/apexd_host $(apex_files)
+$(APEX_INFO_FILE): $(HOST_OUT_EXECUTABLES)/apexd_host \
+    $(HOST_OUT_EXECUTABLES)/deapexer $(HOST_OUT_EXECUTABLES)/debugfs $(HOST_OUT_EXECUTABLES)/fsck.erofs \
+    $(apex_files)
 	@echo "Extracting apexes..."
 	@echo $(PRIVATE_APEX_FILES) > /dev/null
 	@rm -rf $(APEX_OUT)
@@ -5354,7 +5336,7 @@
     lz4:$(HOST_OUT_EXECUTABLES)/lz4 \
 
 
-# BOARD_KERNEL_CONFIG_FILE and BOARD_KERNEL_VERSION can be used to override the values extracted
+# BOARD_KERNEL_VERSION can be used to override the values extracted
 # from INSTALLED_KERNEL_TARGET.
 ifdef BOARD_KERNEL_VERSION
 $(BUILT_KERNEL_VERSION_FILE): PRIVATE_DECOMPRESS_TOOLS := $(my_decompress_tools)
@@ -5366,15 +5348,8 @@
     echo "Specified kernel version '$(BOARD_KERNEL_VERSION)' does not match actual kernel version '$$KERNEL_RELEASE' " ; exit 1; fi;
 	echo '$(BOARD_KERNEL_VERSION)' > $@
 
-ifdef BOARD_KERNEL_CONFIG_FILE
-$(BUILT_KERNEL_CONFIGS_FILE): $(BOARD_KERNEL_CONFIG_FILE)
-	cp $< $@
-
-$(call declare-license-metadata,$(BUILT_KERNEL_CONFIGS_FILE),SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,"Kernel",kernel)
 $(call declare-license-metadata,$(BUILT_KERNEL_VERSION_FILE),SPDX-license-identifier-GPL-2.0-only,restricted,$(BUILD_SYSTEM)/LINUX_KERNEL_COPYING,"Kernel",kernel)
 
-my_board_extracted_kernel := true
-endif # BOARD_KERNEL_CONFIG_FILE
 endif # BOARD_KERNEL_VERSION
 
 
@@ -5885,7 +5860,10 @@
 endif # BOARD_AVB_ENABLE
 ifneq (,$(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)))
 	$(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
-	  echo "flash $(partition)" >> $@;)
+		$(if $(BOARD_$(call to-upper,$(partition))_IMAGE_NO_FLASHALL),, \
+	      echo "flash $(partition)" >> $@; \
+		) \
+	)
 endif
 	$(hide) echo "reboot fastboot" >> $@
 	$(hide) echo "update-super" >> $@
@@ -6141,9 +6119,6 @@
 ifneq ($(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST),)
 	$(hide) echo "partial_ota_update_partitions_list=$(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)" >> $@
 endif
-ifeq ($(BUILDING_WITH_VSDK),true)
-	$(hide) echo "building_with_vsdk=true" >> $@
-endif
 
 $(call declare-0p-target,$(INSTALLED_FASTBOOT_INFO_TARGET))
 
@@ -6301,13 +6276,13 @@
   $(foreach device,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES), \
     echo "super_$(device)_device_size=$(BOARD_SUPER_PARTITION_$(call to-upper,$(device))_DEVICE_SIZE)" >> $(1);)
   $(if $(BOARD_SUPER_PARTITION_PARTITION_LIST), \
-    echo "dynamic_partition_list=$(call filter-out-missing-partitions,$(BOARD_SUPER_PARTITION_PARTITION_LIST))" >> $(1))
+    echo "dynamic_partition_list=$(sort $(call filter-out-missing-partitions,$(BOARD_SUPER_PARTITION_PARTITION_LIST)))" >> $(1))
   $(if $(BOARD_SUPER_PARTITION_GROUPS),
     echo "super_partition_groups=$(BOARD_SUPER_PARTITION_GROUPS)" >> $(1))
   $(foreach group,$(BOARD_SUPER_PARTITION_GROUPS), \
     echo "super_$(group)_group_size=$(BOARD_$(call to-upper,$(group))_SIZE)" >> $(1); \
     $(if $(BOARD_$(call to-upper,$(group))_PARTITION_LIST), \
-      echo "super_$(group)_partition_list=$(call filter-out-missing-partitions,$(BOARD_$(call to-upper,$(group))_PARTITION_LIST))" >> $(1);))
+      echo "super_$(group)_partition_list=$(strip $(call filter-out-missing-partitions,$(BOARD_$(call to-upper,$(group))_PARTITION_LIST)))" >> $(1);))
   $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_EXT_DISABLED)), \
     echo "build_non_sparse_super_partition=true" >> $(1))
   $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_F2FS_DISABLED)), \
@@ -7178,22 +7153,6 @@
 	$(hide) find $(PRODUCT_OUT)/appcompat | sort >$(PRIVATE_LIST_FILE)
 	$(hide) $(SOONG_ZIP) -d -o $@ -C $(PRODUCT_OUT)/appcompat -l $(PRIVATE_LIST_FILE)
 
-# The mac build doesn't build dex2oat, so create the zip file only if the build OS is linux.
-ifeq ($(BUILD_OS),linux)
-ifneq ($(DEX2OAT),)
-dexpreopt_tools_deps := $(DEXPREOPT_GEN_DEPS) $(DEXPREOPT_GEN)
-dexpreopt_tools_deps += $(HOST_OUT_EXECUTABLES)/dexdump
-dexpreopt_tools_deps += $(HOST_OUT_EXECUTABLES)/oatdump
-DEXPREOPT_TOOLS_ZIP := $(PRODUCT_OUT)/dexpreopt_tools.zip
-$(DEXPREOPT_TOOLS_ZIP): $(dexpreopt_tools_deps)
-$(DEXPREOPT_TOOLS_ZIP): PRIVATE_DEXPREOPT_TOOLS_DEPS := $(dexpreopt_tools_deps)
-$(DEXPREOPT_TOOLS_ZIP): $(SOONG_ZIP)
-	$(hide) mkdir -p $(dir $@)
-	$(hide) $(SOONG_ZIP) -d -o $@ -j $(addprefix -f ,$(PRIVATE_DEXPREOPT_TOOLS_DEPS)) -f $$(realpath $(DEX2OAT))
-$(call declare-1p-target,$(DEXPREOPT_TOOLS_ZIP),)
-endif # DEX2OAT is set
-endif # BUILD_OS == linux
-
 DEXPREOPT_CONFIG_ZIP := $(PRODUCT_OUT)/dexpreopt_config.zip
 
 $(DEXPREOPT_CONFIG_ZIP): $(INSTALLED_SYSTEMIMAGE_TARGET) \
@@ -7227,6 +7186,12 @@
 $(call declare-1p-target,$(DEXPREOPT_CONFIG_ZIP),)
 
 # -----------------------------------------------------------------
+# Zips of the symbols directory per test suites
+#
+
+$(foreach suite,$(ALL_COMPATIBILITY_SUITES),$(eval $(call create-suite-symbols-map,$(suite))))
+
+# -----------------------------------------------------------------
 # A zip of the symbols directory.  Keep the full paths to make it
 # more obvious where these files came from.
 # Also produces a textproto containing mappings from elf IDs to symbols
@@ -7244,29 +7209,37 @@
 # The path to a file containing mappings from elf IDs to filenames.
 SYMBOLS_MAPPING := $(PRODUCT_OUT)/$(name)-symbols-mapping.textproto
 .KATI_READONLY := SYMBOLS_ZIP SYMBOLS_MAPPING
-# For apps_only build we'll establish the dependency later in build/make/core/main.mk.
+
 ifeq (,$(TARGET_BUILD_UNBUNDLED))
-$(SYMBOLS_ZIP): $(INTERNAL_ALLIMAGES_FILES) $(updater_dep)
+  _symbols_zip_modules := $(call product-installed-modules,$(INTERNAL_PRODUCT))
+  $(SYMBOLS_ZIP): $(updater_dep)
+else
+  _symbols_zip_modules := $(unbundled_build_modules)
 endif
-$(SYMBOLS_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,symbols)/filelist
-$(SYMBOLS_ZIP): PRIVATE_MAPPING_PACKAGING_DIR := $(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)
-$(SYMBOLS_ZIP): $(SOONG_ZIP) $(SYMBOLS_MAP)
+
+_symbols_zip_modules_symbols_files := $(foreach m,$(_symbols_zip_modules),$(ALL_MODULES.$(m).SYMBOLIC_OUTPUT_PATH))
+_symbols_zip_modules_mapping_files := $(foreach m,$(_symbols_zip_modules),$(ALL_MODULES.$(m).ELF_SYMBOL_MAPPING_PATH))
+
+$(SYMBOLS_ZIP): PRIVATE_SYMBOLS_MODULES_FILES := $(_symbols_zip_modules_symbols_files)
+$(SYMBOLS_ZIP): PRIVATE_SYMBOLS_MODULES_MAPPING_FILES := $(_symbols_zip_modules_mapping_files)
+$(SYMBOLS_ZIP): $(SOONG_ZIP) $(SYMBOLS_MAP) $(_symbols_zip_modules_symbols_files) $(_symbols_zip_modules_mapping_files)
 	@echo "Package symbols: $@"
-	$(hide) rm -rf $@ $(PRIVATE_LIST_FILE)
-	$(hide) mkdir -p $(TARGET_OUT_UNSTRIPPED) $(dir $(PRIVATE_LIST_FILE)) $(PRIVATE_MAPPING_PACKAGING_DIR)
-	# Find all of the files in the symbols directory and zip them into the symbols zip.
-	$(hide) find -L $(TARGET_OUT_UNSTRIPPED) -type f | sort >$(PRIVATE_LIST_FILE)
-	$(hide) $(SOONG_ZIP) --ignore_missing_files -d -o $@ -C $(OUT_DIR)/.. -l $(PRIVATE_LIST_FILE)
-	# Find all of the files in the symbols mapping directory and merge them into the symbols mapping textproto.
-	$(hide) find -L $(PRIVATE_MAPPING_PACKAGING_DIR) -type f | sort >$(PRIVATE_LIST_FILE)
-	$(hide) $(SYMBOLS_MAP) -merge $(SYMBOLS_MAPPING) -ignore_missing_files @$(PRIVATE_LIST_FILE)
+	$(hide) rm -rf $@ $@.symbols_list $@.mapping_list
+	# Find all installed files in the symbols directory and zip them into the symbols zip.
+	echo "$(PRIVATE_SYMBOLS_MODULES_FILES)" | tr " " "\n" | sort > $@.symbols_list
+	$(hide) $(SOONG_ZIP) -d -o $@ -l $@.symbols_list
+	# Find all installed files in the symbols mapping directory and merge them into the symbols mapping textproto.
+	echo "$(PRIVATE_SYMBOLS_MODULES_MAPPING_FILES)" | tr " " "\n" | sort > $@.mapping_list
+	$(hide) $(SYMBOLS_MAP) -merge $(SYMBOLS_MAPPING) @$@.mapping_list
 $(SYMBOLS_ZIP): .KATI_IMPLICIT_OUTPUTS := $(SYMBOLS_MAPPING)
 
 $(call declare-1p-container,$(SYMBOLS_ZIP),)
 ifeq (,$(TARGET_BUILD_UNBUNDLED))
-$(call declare-container-license-deps,$(SYMBOLS_ZIP),$(INTERNAL_ALLIMAGES_FILES) $(updater_dep),$(PRODUCT_OUT)/:/)
+$(call declare-container-license-deps,$(SYMBOLS_ZIP),$(PRIVATE_SYMBOLS_MODULES_FILES) $(updater_dep),$(PRODUCT_OUT)/:/)
 endif
 
+_symbols_zip_modules_symbols_files :=
+_symbols_zip_modules_mapping_files :=
 # -----------------------------------------------------------------
 # A zip of the coverage directory.
 #
@@ -7358,6 +7331,72 @@
   _proguard_dict_zip_modules := $(unbundled_build_modules)
 endif
 
+# Filter out list to avoid uncessary proguard related file generation
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
+filter_out_proguard_dict_zip_modules :=
+# product.img
+ifndef BUILDING_PRODUCT_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/product/%
+endif
+# system.img
+ifndef BUILDING_SYSTEM_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system/%
+endif
+# system_dlkm.img
+ifndef BUILDING_SYSTEM_DLKM_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_dlkm/%
+endif
+# system_ext.img
+ifndef BUILDING_SYSTEM_EXT_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_ext/%
+endif
+# system_other.img
+ifndef BUILDING_SYSTEM_OTHER_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_other/%
+endif
+# odm.img
+ifndef BUILDING_ODM_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/odm/%
+endif
+# odm_dlkm.img
+ifndef BUILDING_ODM_DLKM_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/odm_dlkm/%
+endif
+# vendor.img
+ifndef BUILDING_VENDOR_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/vendor/%
+endif
+# vendor_dlkm.img
+ifndef BUILDING_VENDOR_DLKM_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/vendor_dlkm/%
+endif
+# cache.img
+ifndef BUILDING_CACHE_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/cache/%
+endif
+# ramdisk.img
+ifndef BUILDING_RAMDISK_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/ramdisk/%
+endif
+# recovery.img
+ifndef INSTALLED_RECOVERYIMAGE_TARGET
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/recovery/%
+endif
+# userdata.img
+ifndef BUILDING_USERDATA_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/data/%
+endif
+
+# Check the installed files of each module and return the module name
+# or return empty if none of the files remain to be installed
+define filter-out-proguard-modules
+$(if $(filter-out $(filter_out_proguard_dict_zip_modules),$(call module-installed-files,$(1))),$(1))
+endef
+
+# Filter out proguard dict zip modules those are not installed at the built image
+_proguard_dict_zip_modules := $(foreach m,$(_proguard_dict_zip_modules),$(strip $(call filter-out-proguard-modules,$(m))))
+endif
+
 # The path to the zip file containing proguard dictionaries.
 PROGUARD_DICT_ZIP :=$= $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict.zip
 $(PROGUARD_DICT_ZIP): PRIVATE_SOONG_ZIP_ARGUMENTS := $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_DICTIONARY_SOONG_ZIP_ARGUMENTS))
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index f57ec81..c2f0aa7 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -157,8 +157,6 @@
 
 $(call add_soong_config_var_value,ANDROID,release_libpower_no_lock_binder_txn,$(RELEASE_LIBPOWER_NO_LOCK_BINDER_TXN))
 
-$(call add_soong_config_var_value,ANDROID,release_package_libandroid_runtime_punch_holes,$(RELEASE_PACKAGE_LIBANDROID_RUNTIME_PUNCH_HOLES))
-
 $(call add_soong_config_var_value,ANDROID,release_selinux_data_data_ignore,$(RELEASE_SELINUX_DATA_DATA_IGNORE))
 ifneq (,$(filter eng userdebug,$(TARGET_BUILD_VARIANT)))
     # write appcompat system properties on userdebug and eng builds
@@ -346,3 +344,12 @@
 ifneq ($(filter arm x86 true,$(TARGET_ARCH) $(TARGET_2ND_ARCH) $(TARGET_ENABLE_MEDIADRM_64)),)
   $(call soong_config_set_bool,ci_tests,uses_widevine_tests, true)
 endif
+
+# Flags used in GTVS prebuilt apps
+$(call soong_config_set_bool,GTVS,GTVS_COMPRESSED_PREBUILTS,$(if $(findstring $(GTVS_COMPRESSED_PREBUILTS),true yes),true,false))
+$(call soong_config_set_bool,GTVS,GTVS_GMSCORE_BETA,$(if $(findstring $(GTVS_GMSCORE_BETA),true yes),true,false))
+$(call soong_config_set_bool,GTVS,GTVS_SETUPWRAITH_BETA,$(if $(findstring $(GTVS_SETUPWRAITH_BETA),true yes),true,false))
+$(call soong_config_set_bool,GTVS,PRODUCT_USE_PREBUILT_GTVS,$(if $(findstring $(PRODUCT_USE_PREBUILT_GTVS),true yes),true,false))
+
+# Flags used in GTVS_GTV prebuilt apps
+$(call soong_config_set_bool,GTVS_GTV,PRODUCT_USE_PREBUILT_GTVS_GTV,$(if $(findstring $(PRODUCT_USE_PREBUILT_GTVS_GTV),true yes),true,false))
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 9ffe518..604fe06 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -214,6 +214,22 @@
   actual_partition_tag := $(if $(partition_tag),data,system)
 endif
 endif
+
+# if this is a soong module, verify that LOCAL_COMPATIBILITY_SUITE (legacy) matches
+# LOCAL_SOONG_PROVIDER_TEST_SUITES (new, via TestSuiteInfoProvider instead of AndroidMk stuff),
+# modulo "null-sute", "mts", and "mcts". mts/mcts are automatically added if there's a different
+# suite starting with "m(c)ts-". null-suite seems useless and is sometimes automatically added
+# if no other suites are added.
+ifneq (,$(filter $(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)))
+  a := $(filter-out null-suite mts mcts,$(sort $(LOCAL_COMPATIBILITY_SUITE)))
+  b := $(filter-out null-suite mts mcts,$(sort $(LOCAL_SOONG_PROVIDER_TEST_SUITES)))
+  ifneq ($(a),$(b))
+    $(error $(LOCAL_MODULE): LOCAL_COMPATIBILITY_SUITE did not match LOCAL_SOONG_PROVIDER_TEST_SUITES$(newline)  LOCAL_COMPATIBILITY_SUITE: $(a)$(newline)  LOCAL_SOONG_PROVIDER_TEST_SUITES: $(b)$(newline))
+  endif
+  a :=
+  b :=
+endif
+
 # For test modules that lack a suite tag, set null-suite as the default.
 # We only support adding a default suite to native tests, native benchmarks, and instrumentation tests.
 # This is because they are the only tests we currently auto-generate test configs for.
diff --git a/core/board_config.mk b/core/board_config.mk
index 16cf863..cf01c84 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -290,7 +290,7 @@
     $(error Valid values of $(var) are "true", "false", and "". Not "$($(var))")))
 
 include $(BUILD_SYSTEM)/board_config_wifi.mk
-include $(BUILD_SYSTEM)/board_config_wpa_supplicant.mk
+-include external/wpa_supplicant_8/board_config_wpa_supplicant.mk
 
 # Set up soong config for "soong_config_value_variable".
 -include hardware/interfaces/configstore/1.1/default/surfaceflinger.mk
diff --git a/core/board_config_wpa_supplicant.mk b/core/board_config_wpa_supplicant.mk
deleted file mode 100644
index c03f94a..0000000
--- a/core/board_config_wpa_supplicant.mk
+++ /dev/null
@@ -1,96 +0,0 @@
-#
-# Copyright (C) 2024 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# ###############################################################
-# This file adds wpa_supplicant_8 variables into soong config namespace (`wpa_supplicant_8`)
-# ###############################################################
-
-ifdef BOARD_HOSTAPD_DRIVER
-$(call soong_config_set_bool,wpa_supplicant_8,wpa_build_hostapd,true)
-ifneq ($(BOARD_HOSTAPD_DRIVER),NL80211)
-    $(error BOARD_HOSTAPD_DRIVER set to $(BOARD_HOSTAPD_DRIVER) but current soong expected it should be NL80211 only!)
-endif
-endif
-
-ifdef BOARD_WPA_SUPPLICANT_DRIVER
-ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),NL80211)
-    $(error BOARD_WPA_SUPPLICANT_DRIVER set to $(BOARD_WPA_SUPPLICANT_DRIVER) but current soong expected it should be NL80211 only!)
-endif
-endif
-
-# This is for CONFIG_DRIVER_NL80211_BRCM, CONFIG_DRIVER_NL80211_SYNA, CONFIG_DRIVER_NL80211_QCA
-# And it is only used for a cflags setting in driver.
-$(call soong_config_set,wpa_supplicant_8,board_wlan_device,$(BOARD_WLAN_DEVICE))
-
-# Belong to CONFIG_IEEE80211AX definition
-ifeq ($(WIFI_FEATURE_HOSTAPD_11AX),true)
-$(call soong_config_set_bool,wpa_supplicant_8,hostapd_11ax,true)
-endif
-
-ifeq ($(WIFI_FEATURE_SUPPLICANT_11AX),true)
-$(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_11ax,true)
-endif
-
-# Belong to CONFIG_IEEE80211BE definition
-ifeq ($(WIFI_FEATURE_HOSTAPD_11BE),true)
-$(call soong_config_set_bool,wpa_supplicant_8,hostapd_11be,true)
-endif
-
-ifeq ($(WIFI_FEATURE_SUPPLICANT_11BE),true)
-$(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_11be,true)
-endif
-
-# PLATFORM_VERSION
-$(call soong_config_set,wpa_supplicant_8,platform_version,$(PLATFORM_VERSION))
-
-# BOARD_HOSTAPD_PRIVATE_LIB
-ifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
-$(call soong_config_set_bool,wpa_supplicant_8,hostapd_use_stub_lib,true)
-else
-$(call soong_config_set,wpa_supplicant_8,board_hostapd_private_lib,$(BOARD_HOSTAPD_PRIVATE_LIB))
-endif
-
-ifeq ($(BOARD_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL),true)
-$(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_config_80211w_mfp_optional,true)
-endif
-
-ifneq ($(BOARD_HOSTAPD_PRIVATE_LIB_EVENT),)
-$(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_private_lib_event,true)
-endif
-
-# BOARD_WPA_SUPPLICANT_PRIVATE_LIB
-ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
-$(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_use_stub_lib,true)
-else
-$(call soong_config_set,wpa_supplicant_8,board_wpa_supplicant_private_lib,$(BOARD_WPA_SUPPLICANT_PRIVATE_LIB))
-endif
-
-ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB_EVENT),)
-$(call soong_config_set_bool,wpa_supplicant_8,board_wpa_supplicant_private_lib_event,true)
-endif
-
-ifeq ($(WIFI_PRIV_CMD_UPDATE_MBO_CELL_STATUS), enabled)
-$(call soong_config_set_bool,wpa_supplicant_8,wifi_priv_cmd_update_mbo_cell_status,true)
-endif
-
-ifeq ($(WIFI_HIDL_UNIFIED_SUPPLICANT_SERVICE_RC_ENTRY), true)
-$(call soong_config_set_bool,wpa_supplicant_8,wifi_hidl_unified_supplicant_service_rc_entry,true)
-endif
-
-# New added in internal main
-ifeq ($(WIFI_BRCM_OPEN_SOURCE_MULTI_AKM), enabled)
-$(call soong_config_set_bool,wpa_supplicant_8,wifi_brcm_open_source_multi_akm,true)
-endif
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 2e67aff..8a98c13 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -271,6 +271,7 @@
 LOCAL_SOONG_MODULE_TYPE :=
 LOCAL_SOONG_PROGUARD_DICT :=
 LOCAL_SOONG_PROGUARD_USAGE_ZIP :=
+LOCAL_SOONG_PROVIDER_TEST_SUITES :=
 LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=
 LOCAL_SOONG_TRANSITIVE_RES_PACKAGES :=
 LOCAL_SOONG_DEVICE_RRO_DIRS :=
diff --git a/core/combo/arch/arm64/armv9-3a.mk b/core/combo/arch/arm64/armv9-3a.mk
new file mode 100644
index 0000000..0f2c620
--- /dev/null
+++ b/core/combo/arch/arm64/armv9-3a.mk
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2025 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.
+#
+
+# .mk file required to support build for the ARMv9.3-A arch variant.
+# The file just needs to be present, it does not need to contain anything.
diff --git a/core/combo/arch/arm64/armv9-4a.mk b/core/combo/arch/arm64/armv9-4a.mk
new file mode 100644
index 0000000..6ab3bed
--- /dev/null
+++ b/core/combo/arch/arm64/armv9-4a.mk
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2025 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.
+#
+
+# .mk file required to support build for the ARMv9.4-A arch variant.
+# The file just needs to be present, it does not need to contain anything.
diff --git a/core/config.mk b/core/config.mk
index b892924..fafdfe1 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -598,10 +598,7 @@
 DISABLE_PREOPT_BOOT_IMAGES :=
 ifneq (,$(TARGET_BUILD_APPS)$(TARGET_BUILD_UNBUNDLED_IMAGE))
   DISABLE_PREOPT := true
-  # VSDK builds perform dexpreopt during merge_target_files build step.
-  ifneq (true,$(BUILDING_WITH_VSDK))
-    DISABLE_PREOPT_BOOT_IMAGES := true
-  endif
+  DISABLE_PREOPT_BOOT_IMAGES := true
 endif
 ifeq (true,$(TARGET_BUILD_UNBUNDLED))
   ifneq (true,$(UNBUNDLED_BUILD_SDKS_FROM_SOURCE))
@@ -765,7 +762,6 @@
 
 # TODO: remove all code referencing these, and remove override variables
 PRODUCT_FULL_TREBLE := true
-PRODUCT_NOTICE_SPLIT := true
 PRODUCT_TREBLE_LINKER_NAMESPACES := true
 PRODUCT_ENFORCE_VINTF_MANIFEST := true
 
@@ -774,7 +770,6 @@
     PRODUCT_FULL_TREBLE \
     PRODUCT_TREBLE_LINKER_NAMESPACES \
     PRODUCT_ENFORCE_VINTF_MANIFEST \
-    PRODUCT_NOTICE_SPLIT \
 
 # TODO(b/114488870): remove all sets of these everwhere, and disallow them to be used
 $(KATI_obsolete_var PRODUCT_TREBLE_LINKER_NAMESPACES_OVERRIDE,Deprecated.)
diff --git a/core/cxx_stl_setup.mk b/core/cxx_stl_setup.mk
index 0d557c7..5e8ca7f 100644
--- a/core/cxx_stl_setup.mk
+++ b/core/cxx_stl_setup.mk
@@ -78,7 +78,7 @@
         my_static_libraries += libc++demangle
 
         ifeq ($(my_link_type),static)
-            my_static_libraries += libm libc libunwind
+            my_static_libraries += libm libc libunwind libstatic_rustlibs_for_make
         endif
     endif
 else ifeq ($(my_cxx_stl),ndk)
diff --git a/core/definitions.mk b/core/definitions.mk
index 40f5af0..ea151fa 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -836,18 +836,6 @@
 endef
 
 ###########################################################
-## Declare that non-module targets copied from project $(1) and
-## optionally ending in $(2) are non-copyrightable files.
-##
-## e.g. an information-only file merely listing other files.
-###########################################################
-define declare-0p-copy-files
-$(strip \
-  $(foreach _pair,$(filter $(1)%$(2),$(PRODUCT_COPY_FILES)),$(eval $(call declare-0p-target,$(PRODUCT_OUT)/$(call word-colon,2,$(_pair))))) \
-)
-endef
-
-###########################################################
 ## Declare non-module target $(1) to have a first-party license
 ## (Android Apache 2.0)
 ##
@@ -3434,9 +3422,9 @@
 # a hash mapping to the mapping directory.
 # $(1): unstripped intermediates file
 # $(2): path in symbols directory
+# $(3): path in elf_symbol_mapping packaging directory
 define copy-unstripped-elf-file-with-mapping
-$(call _copy-symbols-file-with-mapping,$(1),$(2),\
-  elf,$(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(2).textproto))
+$(call _copy-symbols-file-with-mapping,$(1),$(2),elf,$(3))
 endef
 
 # Copy an R8 dictionary to the packaging directory while also extracting
@@ -3701,6 +3689,32 @@
     $(sort $(foreach suite,$(LOCAL_COMPATIBILITY_SUITE),$(my_compat_dist_config_$(suite))))))
 endef
 
+# Define symbols.zip and symbols-mapping.textproto build rule per test suite
+#
+# $(1): Name of the test suite to create the zip and mapping build rules
+define create-suite-symbols-map
+_suite_symbols_zip := $$(subst -tests-,-tests_-,$$(PRODUCT_OUT)/$(1)-symbols.zip)
+_suite_symbols_mapping := $$(subst -tests-,-tests_-,$$(PRODUCT_OUT)/$(1)-symbols-mapping.textproto)
+_suite_modules_symbols_files := $$(foreach m,$$(COMPATIBILITY.$(1).MODULES),$$(ALL_MODULES.$$(m).SYMBOLIC_OUTPUT_PATH))
+_suite_modules_mapping_files := $$(foreach m,$$(COMPATIBILITY.$(1).MODULES),$$(ALL_MODULES.$$(m).ELF_SYMBOL_MAPPING_PATH))
+
+$$(_suite_symbols_zip): PRIVATE_SUITE_SYMBOLS_MAPPING := $$(_suite_symbols_mapping)
+$$(_suite_symbols_zip): PRIVATE_SUITE_MODULES_SYMBOLS_FILES := $$(_suite_modules_symbols_files)
+$$(_suite_symbols_zip): PRIVATE_SUITE_MODULES_MAPPING_FILES := $$(_suite_modules_mapping_files)
+$$(_suite_symbols_zip): $$(SOONG_ZIP) $$(SYMBOLS_MAP) $$(_suite_modules_symbols_files) $$(_suite_modules_mapping_files)
+	@echo "Package $(1) symbols: $$@"
+	$(hide) rm -rf $$@ $$@.symbols_list $$@.mapping_list
+	echo "$$(PRIVATE_SUITE_MODULES_SYMBOLS_FILES)" | tr " " "\n" | sort > $$@.symbols_list
+	$(hide) $$(SOONG_ZIP) -d -o $$@ -l $$@.symbols_list
+	echo "$$(PRIVATE_SUITE_MODULES_MAPPING_FILES)" | tr " " "\n" | sort > $$@.mapping_list
+	$(hide) $$(SYMBOLS_MAP) -merge $$(PRIVATE_SUITE_SYMBOLS_MAPPING) @$$@.mapping_list
+$$(_suite_symbols_zip): .KATI_IMPLICIT_OUTPUTS := $$(_suite_symbols_mapping)
+
+.PHONY: $(1)
+$(1): $$(_suite_symbols_zip) $$(_suite_symbols_mapping)
+$$(call dist-for-goals-with-filenametag,$(1), $$(_suite_symbols_zip) $$(_suite_symbols_mapping))
+endef
+
 ###########################################################
 ## Path Cleaning
 ###########################################################
diff --git a/core/dex_preopt.mk b/core/dex_preopt.mk
index 9b5e1db..b78c10c 100644
--- a/core/dex_preopt.mk
+++ b/core/dex_preopt.mk
@@ -13,34 +13,10 @@
 install-on-system-other = $(filter-out $(PRODUCT_DEXPREOPT_SPEED_APPS) $(PRODUCT_SYSTEM_SERVER_APPS),$(basename $(notdir $(filter $(foreach f,$(SYSTEM_OTHER_ODEX_FILTER),$(TARGET_OUT)/$(f)),$(1)))))
 endif
 
-# Build the boot.zip which contains the boot jars and their compilation output
-# We can do this only if preopt is enabled and if the product uses libart config (which sets the
-# default properties for preopting).
-# At the time of writing, this is only for ART Cloud.
 ifeq ($(WITH_DEXPREOPT), true)
 ifneq ($(WITH_DEXPREOPT_ART_BOOT_IMG_ONLY), true)
 ifeq ($(PRODUCT_USES_DEFAULT_ART_CONFIG), true)
 
-boot_zip := $(PRODUCT_OUT)/boot.zip
-bootclasspath_jars := $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)
-
-# TODO remove system_server_jars usages from boot.zip and depend directly on system_server.zip file.
-
-# Use "/system" path for JARs with "platform:" prefix.
-# These JARs counterintuitively use "platform" prefix but they will
-# be actually installed to /system partition.
-platform_system_server_jars = $(filter platform:%, $(PRODUCT_SYSTEM_SERVER_JARS))
-system_server_jars := \
-  $(foreach m,$(platform_system_server_jars),\
-    $(PRODUCT_OUT)/system/framework/$(call word-colon,2,$(m)).jar)
-
-# For the remaining system server JARs use the partition signified by the prefix.
-# For example, prefix "system_ext:" will use "/system_ext" path.
-other_system_server_jars = $(filter-out $(platform_system_server_jars), $(PRODUCT_SYSTEM_SERVER_JARS))
-system_server_jars += \
-  $(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
@@ -48,40 +24,6 @@
 # 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)))))
-bootclasspath_arg := $(subst $(space),:,$(patsubst $(dexpreopt_root_dir)%,%,$(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)))
-bootclasspath_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)))
-uffd_gc_flag_txt := $(OUT_DIR)/soong/dexpreopt/uffd_gc_flag.txt
-
-boot_zip_metadata_txt := $(dir $(boot_zip))boot_zip/METADATA.txt
-$(boot_zip_metadata_txt): $(uffd_gc_flag_txt)
-$(boot_zip_metadata_txt):
-	rm -f $@
-	echo "bootclasspath = $(bootclasspath_arg)" >> $@
-	echo "bootclasspath-locations = $(bootclasspath_locations_arg)" >> $@
-	echo "boot-image = $(boot_image_arg)" >> $@
-	echo "extra-args = `cat $(uffd_gc_flag_txt)`" >> $@
-
-$(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) $(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)) \
-	  -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))
-
 endif  #PRODUCT_USES_DEFAULT_ART_CONFIG
 endif  #WITH_DEXPREOPT_ART_BOOT_IMG_ONLY
 endif  #WITH_DEXPREOPT
diff --git a/core/dynamic_binary.mk b/core/dynamic_binary.mk
index 0d2cd7f..878989d 100644
--- a/core/dynamic_binary.mk
+++ b/core/dynamic_binary.mk
@@ -55,7 +55,12 @@
 endif
 symbolic_input := $(inject_module)
 symbolic_output := $(my_unstripped_path)/$(my_installed_module_stem)
-$(eval $(call copy-unstripped-elf-file-with-mapping,$(symbolic_input),$(symbolic_output)))
+elf_mapping_path := $(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(symbolic_output).textproto)
+
+ALL_MODULES.$(my_register_name).SYMBOLIC_OUTPUT_PATH := $(symbolic_output)
+ALL_MODULES.$(my_register_name).ELF_SYMBOL_MAPPING_PATH := $(elf_mapping_path)
+
+$(eval $(call copy-unstripped-elf-file-with-mapping,$(symbolic_input),$(symbolic_output),$(elf_mapping_path)))
 
 ###########################################################
 ## Store breakpad symbols
diff --git a/core/main.mk b/core/main.mk
index 9710dc8..aed3fa2 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -299,6 +299,8 @@
 
 $(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))
 
+-include device/generic/goldfish/tasks/emu_img_zip.mk
+
 # Build bootloader.img/radio.img, and unpack the partitions.
 -include vendor/google_devices/$(TARGET_SOC)/prebuilts/misc_bins/update_bootloader_radio_image.mk
 
@@ -993,6 +995,7 @@
 define auto-included-modules
   $(foreach vndk_ver,$(PRODUCT_EXTRA_VNDK_VERSIONS),com.android.vndk.v$(vndk_ver)) \
   llndk.libraries.txt \
+  $(if $(DEVICE_MANIFEST_FILE),vendor_manifest.xml) \
   $(if $(DEVICE_MANIFEST_SKUS),$(foreach sku, $(DEVICE_MANIFEST_SKUS),vendor_manifest_$(sku).xml)) \
   $(if $(ODM_MANIFEST_FILES),odm_manifest.xml) \
   $(if $(ODM_MANIFEST_SKUS),$(foreach sku, $(ODM_MANIFEST_SKUS),odm_manifest_$(sku).xml)) \
@@ -1555,7 +1558,6 @@
   $(call dist-for-goals, droidcore, \
     $(BUILT_OTATOOLS_PACKAGE) \
     $(APPCOMPAT_ZIP) \
-    $(DEXPREOPT_TOOLS_ZIP) \
   )
 
   # We dist the following targets for droidcore-unbundled (and droidcore since
@@ -1602,12 +1604,7 @@
     $(INSTALLED_FILES_JSON_SYSTEMOTHER) \
     $(INSTALLED_FILES_FILE_RECOVERY) \
     $(INSTALLED_FILES_JSON_RECOVERY) \
-    $(if $(BUILDING_SYSTEM_IMAGE), $(INSTALLED_BUILD_PROP_TARGET):build.prop) \
     $(if $(BUILDING_VENDOR_IMAGE), $(INSTALLED_VENDOR_BUILD_PROP_TARGET):build.prop-vendor) \
-    $(if $(BUILDING_PRODUCT_IMAGE), $(INSTALLED_PRODUCT_BUILD_PROP_TARGET):build.prop-product) \
-    $(if $(BUILDING_ODM_IMAGE), $(INSTALLED_ODM_BUILD_PROP_TARGET):build.prop-odm) \
-    $(if $(BUILDING_SYSTEM_EXT_IMAGE), $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET):build.prop-system_ext) \
-    $(if $(BUILDING_RAMDISK_IMAGE), $(INSTALLED_RAMDISK_BUILD_PROP_TARGET):build.prop-ramdisk) \
     $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
     $(INSTALLED_MISC_INFO_TARGET) \
     $(INSTALLED_RAMDISK_TARGET) \
@@ -1701,7 +1698,6 @@
 ALL_SDK_TARGETS := $(INTERNAL_SDK_TARGET)
 sdk: $(ALL_SDK_TARGETS)
 $(call dist-for-goals-with-filenametag,sdk,$(ALL_SDK_TARGETS))
-$(call dist-for-goals,sdk,$(INSTALLED_BUILD_PROP_TARGET))
 endif
 
 # umbrella targets to assit engineers in verifying builds
@@ -1896,7 +1892,7 @@
 	  $(eval _is_partition_compat_symlink := $(if $(findstring $f,$(PARTITION_COMPAT_SYMLINKS)),Y)) \
 	  $(eval _is_flags_file := $(if $(findstring $f, $(ALL_FLAGS_FILES)),Y)) \
 	  $(eval _is_rootdir_symlink := $(if $(findstring $f, $(ALL_ROOTDIR_SYMLINKS)),Y)) \
-	  $(eval _is_platform_generated := $(_is_build_prop)$(_is_notice_file)$(_is_product_system_other_avbkey)$(_is_event_log_tags_file)$(_is_system_other_odex_marker)$(_is_kernel_modules_blocklist)$(_is_fsverity_build_manifest_apk)$(_is_linker_config)$(_is_partition_compat_symlink)$(_is_flags_file)$(_is_rootdir_symlink)) \
+	  $(eval _is_platform_generated := $(if $(_is_soong_module),,$(_is_build_prop)$(_is_notice_file)$(_is_product_system_other_avbkey)$(_is_event_log_tags_file)$(_is_system_other_odex_marker)$(_is_kernel_modules_blocklist)$(_is_fsverity_build_manifest_apk)$(_is_linker_config)$(_is_partition_compat_symlink)$(_is_flags_file)$(_is_rootdir_symlink))) \
 	  $(eval _static_libs := $(if $(_is_soong_module),,$(ALL_INSTALLED_FILES.$f.STATIC_LIBRARIES))) \
 	  $(eval _whole_static_libs := $(if $(_is_soong_module),,$(ALL_INSTALLED_FILES.$f.WHOLE_STATIC_LIBRARIES))) \
 	  $(eval _license_text := $(if $(filter $(_build_output_path),$(ALL_NON_MODULES)),$(ALL_NON_MODULES.$(_build_output_path).NOTICES),\
diff --git a/core/os_licensing.mk b/core/os_licensing.mk
index 97e55a7..bebaca1 100644
--- a/core/os_licensing.mk
+++ b/core/os_licensing.mk
@@ -7,12 +7,6 @@
 
 SYSTEM_NOTICE_DEPS += $(UNMOUNTED_NOTICE_DEPS) $(UNMOUNTED_NOTICE_VENDOR_DEPS)
 
-ifneq ($(PRODUCT_NOTICE_SPLIT),true)
-$(eval $(call html-notice-rule,$(target_notice_file_html_gz),"System image",$(system_notice_file_message),$(SYSTEM_NOTICE_DEPS),$(SYSTEM_NOTICE_DEPS)))
-
-$(installed_notice_html_or_xml_gz): $(target_notice_file_html_gz)
-	$(copy-file-to-target)
-else
 $(eval $(call xml-notice-rule,$(target_notice_file_xml_gz),"System image",$(system_notice_file_message),$(SYSTEM_NOTICE_DEPS),$(SYSTEM_NOTICE_DEPS)))
 
 $(eval $(call text-notice-rule,$(target_notice_file_txt),"System image",$(system_notice_file_message),$(SYSTEM_NOTICE_DEPS),$(SYSTEM_NOTICE_DEPS)))
@@ -21,7 +15,6 @@
 $(installed_notice_html_or_xml_gz): $(target_notice_file_xml_gz)
 	$(copy-file-to-target)
 endif
-endif
 
 $(call declare-1p-target,$(target_notice_file_xml_gz))
 ifneq ($(PRODUCT_USE_SOONG_NOTICE_XML),true)
diff --git a/core/packaging/flags.mk b/core/packaging/flags.mk
index a371a00..19068f4 100644
--- a/core/packaging/flags.mk
+++ b/core/packaging/flags.mk
@@ -17,9 +17,8 @@
 # the combined flags files.
 #
 
-# TODO: Should we do all of the images in $(IMAGES_TO_BUILD)?
-_FLAG_PARTITIONS := product system vendor
-
+# TODO: Should we do all of the images?
+_FLAG_PARTITIONS := product system system_ext vendor
 
 # -----------------------------------------------------------------
 # Aconfig Flags
@@ -62,28 +61,38 @@
 $(call copy-one-file, $(1), $(2))
 endef
 
+define out-dir-for-partition
+$(TARGET_COPY_OUT_$(call to-upper,$(1)))
+endef
+
+# Get the module names suitable for ALL_MODULES.* variables that are installed
+# for a given container
+# $(1): container
+define register-names-for-container
+$(sort $(foreach m,$(product_MODULES),\
+	$(if $(filter $(PRODUCT_OUT)/$(call out-dir-for-partition,$(strip $(1)))/%, $(ALL_MODULES.$(m).INSTALLED)), \
+		$(m)
+	) \
+))
+endef
+
 $(foreach partition, $(_FLAG_PARTITIONS), \
-	$(eval aconfig_flag_summaries_protobuf.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig_flags.pb) \
+	$(eval aconfig_flag_summaries_protobuf.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig_flags.pb) \
 	$(eval $(call generate-partition-aconfig-flag-file, \
 			$(TARGET_OUT_FLAGS)/$(partition)/aconfig_flags.pb, \
 			$(aconfig_flag_summaries_protobuf.$(partition)), \
 			$(partition), \
 			$(sort \
-				$(foreach m, $(call register-names-for-partition, $(partition)), \
+				$(foreach m, $(call register-names-for-container, $(partition)), \
 					$(ALL_MODULES.$(m).ACONFIG_FILES) \
 				) \
-				$(if $(filter system, $(partition)), \
-					$(foreach m, $(call register-names-for-partition, system_ext), \
-						$(ALL_MODULES.$(m).ACONFIG_FILES) \
-					) \
-				) \
 			) \
 	)) \
 )
 
 # Collect the on-device flags into a single file, similar to all_aconfig_declarations.
 required_aconfig_flags_files := \
-		$(sort $(foreach partition, $(filter $(IMAGES_TO_BUILD), $(_FLAG_PARTITIONS)), \
+		$(sort $(foreach partition, $(_FLAG_PARTITIONS), \
 			$(aconfig_flag_summaries_protobuf.$(partition)) \
 		))
 
@@ -158,10 +167,10 @@
 
 ifeq ($(RELEASE_CREATE_ACONFIG_STORAGE_FILE),true)
 $(foreach partition, $(_FLAG_PARTITIONS), \
-	$(eval aconfig_storage_package_map.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig/package.map) \
-	$(eval aconfig_storage_flag_map.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig/flag.map) \
-	$(eval aconfig_storage_flag_val.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig/flag.val) \
-	$(eval aconfig_storage_flag_info.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig/flag.info) \
+	$(eval aconfig_storage_package_map.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/package.map) \
+	$(eval aconfig_storage_flag_map.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/flag.map) \
+	$(eval aconfig_storage_flag_val.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/flag.val) \
+	$(eval aconfig_storage_flag_info.$(partition) := $(PRODUCT_OUT)/$(call out-dir-for-partition,$(partition))/etc/aconfig/flag.info) \
 	$(eval $(call generate-partition-aconfig-storage-file, \
 				$(TARGET_OUT_FLAGS)/$(partition)/package.map, \
 				$(TARGET_OUT_FLAGS)/$(partition)/flag.map, \
@@ -180,7 +189,7 @@
 # -----------------------------------------------------------------
 # Install the ones we need for the configured product
 required_flags_files := \
-		$(sort $(foreach partition, $(filter $(IMAGES_TO_BUILD), $(_FLAG_PARTITIONS)), \
+		$(sort $(foreach partition, $(_FLAG_PARTITIONS), \
 			$(build_flag_summaries.$(partition)) \
 			$(aconfig_flag_summaries_protobuf.$(partition)) \
 			$(aconfig_storage_package_map.$(partition)) \
@@ -198,6 +207,8 @@
 
 
 # Clean up
+out-dir-for-partition:=
+register-names-for-container:=
 required_flags_files:=
 required_aconfig_flags_files:=
 $(foreach partition, $(_FLAG_PARTITIONS), \
diff --git a/core/soong_app_prebuilt.mk b/core/soong_app_prebuilt.mk
index ab9227f..62b5d5b 100644
--- a/core/soong_app_prebuilt.mk
+++ b/core/soong_app_prebuilt.mk
@@ -142,7 +142,21 @@
 # install symbol files of JNI libraries
 my_jni_lib_symbols_copy_files := $(foreach f,$(LOCAL_SOONG_JNI_LIBS_SYMBOLS),\
   $(call word-colon,1,$(f)):$(patsubst $(PRODUCT_OUT)/%,$(TARGET_OUT_UNSTRIPPED)/%,$(call word-colon,2,$(f))))
-$(LOCAL_BUILT_MODULE): | $(call copy-many-files, $(my_jni_lib_symbols_copy_files))
+
+$(foreach f, $(my_jni_lib_symbols_copy_files), \
+  $(eval $(call copy-unstripped-elf-file-with-mapping, \
+    $(call word-colon,1,$(f)), \
+    $(call word-colon,2,$(f)), \
+    $(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(call word-colon,2,$(f)).textproto)\
+  ))\
+)
+
+symbolic_outputs := $(foreach f,$(my_jni_lib_symbols_copy_files),$(call word-colon,2,$(f)))
+symbolic_mappings := $(foreach f,$(symbolic_outputs),$(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(f).textproto))
+ALL_MODULES.$(my_register_name).SYMBOLIC_OUTPUT_PATH := $(symbolic_outputs)
+ALL_MODULES.$(my_register_name).ELF_SYMBOL_MAPPING_PATH := $(symbolic_mappings)
+
+$(LOCAL_BUILT_MODULE): | $(symbolic_outputs)
 
 # embedded JNI will already have been handled by soong
 my_embed_jni :=
diff --git a/core/soong_cc_rust_prebuilt.mk b/core/soong_cc_rust_prebuilt.mk
index da60832..9ea24f7 100644
--- a/core/soong_cc_rust_prebuilt.mk
+++ b/core/soong_cc_rust_prebuilt.mk
@@ -190,7 +190,12 @@
       # drop /root as /root is mounted as /
       my_unstripped_path := $(patsubst $(TARGET_OUT_UNSTRIPPED)/root/%,$(TARGET_OUT_UNSTRIPPED)/%, $(my_unstripped_path))
       symbolic_output := $(my_unstripped_path)/$(my_installed_module_stem)
-      $(eval $(call copy-unstripped-elf-file-with-mapping,$(LOCAL_SOONG_UNSTRIPPED_BINARY),$(symbolic_output)))
+      elf_symbol_mapping_path := $(patsubst $(TARGET_OUT_UNSTRIPPED)/%,$(call intermediates-dir-for,PACKAGING,elf_symbol_mapping)/%,$(symbolic_output).textproto)
+
+      ALL_MODULES.$(my_register_name).SYMBOLIC_OUTPUT_PATH := $(symbolic_output)
+      ALL_MODULES.$(my_register_name).ELF_SYMBOL_MAPPING_PATH := $(elf_symbol_mapping_path)
+
+      $(eval $(call copy-unstripped-elf-file-with-mapping,$(LOCAL_SOONG_UNSTRIPPED_BINARY),$(symbolic_output),$(elf_symbol_mapping_path)))
       $(LOCAL_BUILT_MODULE): | $(symbolic_output)
 
       ifeq ($(BREAKPAD_GENERATE_SYMBOLS),true)
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 42401b8..c786737 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -468,6 +468,7 @@
   $(call add_json_bool, ProductUseDynamicPartitions, $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITIONS)))
   $(call add_json_bool, ProductRetrofitDynamicPartitions, $(filter true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)))
   $(call add_json_bool, ProductBuildSuperPartition, $(filter true,$(PRODUCT_BUILD_SUPER_PARTITION)))
+  $(call add_json_bool, BuildingSuperEmptyImage, $(filter true,$(BUILDING_SUPER_EMPTY_IMAGE)))
   $(call add_json_str, BoardSuperPartitionSize, $(BOARD_SUPER_PARTITION_SIZE))
   $(call add_json_str, BoardSuperPartitionMetadataDevice, $(BOARD_SUPER_PARTITION_METADATA_DEVICE))
   $(call add_json_list, BoardSuperPartitionBlockDevices, $(BOARD_SUPER_PARTITION_BLOCK_DEVICES))
@@ -489,6 +490,7 @@
   $(call add_json_list, AbOtaPartitions, $(AB_OTA_PARTITIONS))
   $(call add_json_list, AbOtaKeys, $(PRODUCT_OTA_PUBLIC_KEYS))
   $(call add_json_list, AbOtaPostInstallConfig, $(AB_OTA_POSTINSTALL_CONFIG))
+  $(call add_json_bool, BoardSuperImageInUpdatePackage, $(filter true,$(BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE)))
 
   # Avb (android verified boot) stuff
   $(call add_json_bool, BoardAvbEnable, $(filter true,$(BOARD_AVB_ENABLE)))
@@ -569,6 +571,9 @@
 
   $(call add_json_str, ReleaseToolsExtensionDir, $(firstword $(TARGET_RELEASETOOLS_EXTENSIONS) $($(TARGET_DEVICE_DIR)/../common)))
 
+  # Fastboot
+  $(call add_json_str, BoardFastbootInfoFile, $(TARGET_BOARD_FASTBOOT_INFO_FILE))
+
 $(call end_json_map)
 
 # For converting vintf_data
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index 7fe6551..c7b5cad 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -165,6 +165,7 @@
 cts-v-host-api-map-xml-report := $(api_map_out)/cts-v-host-api-map.xml
 cts-combined-api-map-xml-report := $(api_map_out)/cts-combined-api-map.xml
 cts-combined-api-map-html-report := $(api_map_out)/cts-combined-api-map.html
+cts-combined-api-inherit-xml-report := $(api_map_out)/cts-combined-api-inherit.xml
 
 cts_api_map_dependencies := $(cts_api_map_exe) $(combined_api_xml_description) $(cts_jar_files)
 cts_v_host_api_map_dependencies := $(cts_api_map_exe) $(combined_api_xml_description) $(cts_v_host_jar_files)
@@ -276,6 +277,13 @@
 	$(call generate-api-map-report-cts,"CTS Combined API MAP Report - HTML",\
 			$(PRIVATE_JAR_FILES),html)
 
+$(cts-combined-api-inherit-xml-report): PRIVATE_CTS_API_MAP_EXE := $(cts_api_map_exe)
+$(cts-combined-api-inherit-xml-report): PRIVATE_API_XML_DESC := $(combined_api_xml_description)
+$(cts-combined-api-inherit-xml-report): PRIVATE_JAR_FILES := $(cts_all_jar_files)
+$(cts-combined-api-inherit-xml-report) : $(verifier_zip) $(android_cts_zip) $(cts_combined_api_map_dependencies) | $(ACP)
+	$(call generate-api-inherit-report-cts,"CTS Combined API Inherit Report - XML",\
+			$(PRIVATE_JAR_FILES),xml)
+
 .PHONY: cts-api-map-xml
 cts-api-map-xml : $(cts-api-map-xml-report)
 
@@ -285,14 +293,13 @@
 .PHONY: cts-combined-api-map-xml
 cts-combined-api-map-xml : $(cts-combined-api-map-xml-report)
 
+.PHONY: cts-combined-api-inherit-xml
+cts-combined-api-inherit-xml : $(cts-combined-api-inherit-xml-report)
+
 .PHONY: cts-api-map-all
 
 # Put the test coverage report in the dist dir if "cts-api-coverage" is among the build goals.
-$(call dist-for-goals, cts-api-coverage, $(cts-test-coverage-report):cts-test-coverage-report.html)
-$(call dist-for-goals, cts-api-coverage, $(cts-system-api-coverage-report):cts-system-api-coverage-report.html)
 $(call dist-for-goals, cts-api-coverage, $(cts-system-api-xml-coverage-report):cts-system-api-coverage-report.xml)
-$(call dist-for-goals, cts-api-coverage, $(cts-verifier-coverage-report):cts-verifier-coverage-report.html)
-$(call dist-for-goals, cts-api-coverage, $(cts-combined-coverage-report):cts-combined-coverage-report.html)
 $(call dist-for-goals, cts-api-coverage, $(cts-combined-xml-coverage-report):cts-combined-coverage-report.xml)
 
 ALL_TARGETS.$(cts-test-coverage-report).META_LIC:=$(module_license_metadata)
@@ -304,12 +311,13 @@
 
 # Put the test api map report in the dist dir if "cts-api-map-all" is among the build goals.
 $(call dist-for-goals, cts-api-map-all, $(cts-combined-api-map-xml-report):cts-api-map-report.xml)
-$(call dist-for-goals, cts-api-map-all, $(cts-combined-api-map-html-report):cts-api-map-report.html)
+$(call dist-for-goals, cts-api-map-all, $(cts-combined-api-inherit-xml-report):cts-api-inherit-report.xml)
 
 ALL_TARGETS.$(cts-api-map-xml-report).META_LIC:=$(module_license_metadata)
 ALL_TARGETS.$(cts-v-host-api-map-xml-report).META_LIC:=$(module_license_metadata)
 ALL_TARGETS.$(cts-combined-api-map-xml-report).META_LIC:=$(module_license_metadata)
 ALL_TARGETS.$(cts-combined-api-map-html-report).META_LIC:=$(module_license_metadata)
+ALL_TARGETS.$(cts-combined-api-map-inherit-report).META_LIC:=$(module_license_metadata)
 
 # Arguments;
 #  1 - Name of the report printed out on the screen
@@ -327,7 +335,18 @@
 #  3 - Format of the report
 define generate-api-map-report-cts
 	$(hide) mkdir -p $(dir $@)
-	$(hide) $(PRIVATE_CTS_API_MAP_EXE) -j 8 -a $(shell echo "$(PRIVATE_API_XML_DESC)" | tr ' ' ',') -i $(2) -f $(3) -o $@
+	$(hide) $(PRIVATE_CTS_API_MAP_EXE) -j 8 -m api_map -m xts_annotation -a $(shell echo "$(PRIVATE_API_XML_DESC)" | tr ' ' ',') -i $(2) -f $(3) -o $@
+	@ echo $(1): file://$$(cd $(dir $@); pwd)/$(notdir $@)
+endef
+
+
+# Arguments;
+#  1 - Name of the report printed out on the screen
+#  2 - A file containing list of files that to be analyzed
+#  3 - Format of the report
+define generate-api-inherit-report-cts
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(PRIVATE_CTS_API_MAP_EXE) -j 8 -m xts_api_inherit -a $(shell echo "$(PRIVATE_API_XML_DESC)" | tr ' ' ',') -i $(2) -f $(3) -o $@
 	@ echo $(1): file://$$(cd $(dir $@); pwd)/$(notdir $@)
 endef
 
@@ -347,6 +366,7 @@
 cts-v-host-api-map-xml-report :=
 cts-combined-api-map-xml-report :=
 cts-combined-api-map-html-report :=
+cts-combined-api-map-inherit-report :=
 api_xml_description :=
 api_text_description :=
 system_api_xml_description :=
diff --git a/core/tasks/device-tests.mk b/core/tasks/device-tests.mk
index 062ac23..209bd3e 100644
--- a/core/tasks/device-tests.mk
+++ b/core/tasks/device-tests.mk
@@ -14,6 +14,7 @@
 
 
 .PHONY: device-tests
+.PHONY: device-tests-files-list
 
 device-tests-zip := $(PRODUCT_OUT)/device-tests.zip
 # Create an artifact to include a list of test config files in device-tests.
@@ -21,6 +22,7 @@
 # Create an artifact to include all test config files in device-tests.
 device-tests-configs-zip := $(PRODUCT_OUT)/device-tests_configs.zip
 my_host_shared_lib_for_device_tests := $(call copy-many-files,$(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES))
+device_tests_files_list := $(PRODUCT_OUT)/device-tests_files
 
 $(device-tests-zip) : .KATI_IMPLICIT_OUTPUTS := $(device-tests-list-zip) $(device-tests-configs-zip)
 $(device-tests-zip) : PRIVATE_device_tests_list := $(PRODUCT_OUT)/device-tests_list
@@ -45,7 +47,14 @@
 	rm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \
 		$(PRIVATE_device_tests_list)
 
+$(device_tests_files_list) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_device_tests)
+$(device_tests_files_list) :
+	echo $(sort $(COMPATIBILITY.device-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr " " "\n" > $@.full_list
+	grep $(HOST_OUT_TESTCASES) $@.full_list > $@ || true
+	grep $(TARGET_OUT_TESTCASES) $@.full_list >> $@ || true
+
 device-tests: $(device-tests-zip)
+device-tests-files-list: $(device_tests_files_list)
 
 $(call dist-for-goals, device-tests, $(device-tests-zip) $(device-tests-list-zip) $(device-tests-configs-zip))
 
diff --git a/core/tasks/general-tests.mk b/core/tasks/general-tests.mk
index dcfcfad..44476cb 100644
--- a/core/tasks/general-tests.mk
+++ b/core/tasks/general-tests.mk
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 .PHONY: general-tests
+.PHONY: general-tests-files-list
 
 general_tests_tools := \
     $(HOST_OUT_JAVA_LIBRARIES)/cts-tradefed.jar \
@@ -63,6 +64,10 @@
         $(eval _cmf_dest := $(word 3,$(_cmf_tuple))) \
         $(_cmf_dest)))
 
+general_tests_files_list := $(PRODUCT_OUT)/general-tests_files
+general_tests_host_files_list := $(PRODUCT_OUT)/general-tests_host_files
+general_tests_target_files_list := $(PRODUCT_OUT)/general-tests_target_files
+
 $(general_tests_zip) : PRIVATE_general_tests_list_zip := $(general_tests_list_zip)
 $(general_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(general_tests_list_zip) $(general_tests_configs_zip)
 $(general_tests_zip) : PRIVATE_TOOLS := $(general_tests_tools)
@@ -100,7 +105,16 @@
 	grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list
 	$(SOONG_ZIP) -d -o $(PRIVATE_general_tests_list_zip) -C $(PRIVATE_INTERMEDIATES_DIR) -f $(PRIVATE_INTERMEDIATES_DIR)/general-tests_list
 
+$(general_tests_files_list) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
+$(general_tests_files_list) : PRIVATE_general_tests_host_files_list := $(general_tests_host_files_list)
+$(general_tests_files_list) : PRIVATE_general_tests_target_files_list := $(general_tests_target_files_list)
+$(general_tests_files_list) :
+	echo $(sort $(COMPATIBILITY.general-tests.FILES) $(COMPATIBILITY.device-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr " " "\n" > $@
+	grep $(HOST_OUT_TESTCASES) $@ > $(PRIVATE_general_tests_host_files_list) || true
+	grep $(TARGET_OUT_TESTCASES) $@ >> $(PRIVATE_general_tests_target_files_list) || true
+
 general-tests: $(general_tests_zip)
+general-tests-files-list: $(general_tests_files_list)
 $(call dist-for-goals, general-tests, $(general_tests_zip) $(general_tests_list_zip) $(general_tests_configs_zip) $(general_tests_shared_libs_zip))
 
 $(call declare-1p-container,$(general_tests_zip),)
diff --git a/core/tasks/meta-lic.mk b/core/tasks/meta-lic.mk
index 620b1e2..0675a90 100644
--- a/core/tasks/meta-lic.mk
+++ b/core/tasks/meta-lic.mk
@@ -30,59 +30,6 @@
 $(eval $(call declare-1p-copy-files,device/google/atv,atv-component-overrides.xml))
 $(eval $(call declare-1p-copy-files,device/google/atv,tv_core_hardware.xml))
 
-# Moved here from device/google/bramble/Android.mk
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,p2p_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/bramble,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-
-$(eval $(call declare-1p-copy-files,device/google/bramble,audio_policy_configuration.xml))
-
-# Moved here from device/google/barbet/Android.mk
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,p2p_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/barbet,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-
-$(eval $(call declare-1p-copy-files,device/google/barbet,audio_policy_configuration.xml))
-
-# Moved here from device/google/coral/Android.mk
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,p2p_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/coral,display_19261132550654593.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-
-$(eval $(call declare-1p-copy-files,device/google/coral,audio_policy_configuration.xml))
-$(eval $(call declare-1p-copy-files,device/google/coral,display_19260504575090817.xml))
-
 # Moved here from device/google/cuttlefish/Android.mk
 $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,.idc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
 $(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
@@ -152,23 +99,6 @@
 
 $(eval $(call declare-1p-copy-files,device/google/raviole,audio_policy_configuration.xml))
 
-# Moved here from device/google/redfin/Android.mk
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,p2p_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-$(eval $(call declare-copy-files-license-metadata,device/google/redfin,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
-
-$(eval $(call declare-1p-copy-files,device/google/redfin,audio_policy_configuration.xml))
-
 # Moved here from device/sample/Android.mk
 $(eval $(call declare-1p-copy-files,device/sample,))
 
diff --git a/core/tasks/tools/package-modules.mk b/core/tasks/tools/package-modules.mk
index 4ec5520..4d7b0ee 100644
--- a/core/tasks/tools/package-modules.mk
+++ b/core/tasks/tools/package-modules.mk
@@ -96,7 +96,7 @@
 $(my_package_zip): PRIVATE_COPY_PAIRS := $(my_copy_pairs)
 $(my_package_zip): PRIVATE_STAGING_DIR := $(my_staging_dir)
 $(my_package_zip): PRIVATE_PICKUP_FILES := $(my_pickup_files)
-$(my_package_zip) : $(my_built_modules)
+$(my_package_zip) : $(my_built_modules) $(SOONG_ZIP)
 	@echo "Package $@"
 	@rm -rf $(PRIVATE_STAGING_DIR) && mkdir -p $(PRIVATE_STAGING_DIR)
 	$(foreach p, $(PRIVATE_COPY_PAIRS),\
@@ -105,7 +105,7 @@
 	  cp -Rf $(word 1,$(pair)) $(word 2,$(pair)) && ) true
 	$(hide) $(foreach f, $(PRIVATE_PICKUP_FILES),\
 	  cp -RfL $(f) $(PRIVATE_STAGING_DIR) && ) true
-	$(hide) cd $(PRIVATE_STAGING_DIR) && zip -rqX ../$(notdir $@) *
+	$(hide) $(SOONG_ZIP) -o $@ -C $(PRIVATE_STAGING_DIR) -D $(PRIVATE_STAGING_DIR)
 	rm -rf $(PRIVATE_STAGING_DIR)
 
 my_makefile :=
diff --git a/shell_utils.sh b/shell_utils.sh
index 3124db5..61b0ebc 100644
--- a/shell_utils.sh
+++ b/shell_utils.sh
@@ -97,8 +97,11 @@
   local out_dir=$(getoutdir)
   local top=$(gettop)
 
-  # return early if out dir is already a symlink
+  # return early if out dir is already a symlink.
   if [[ -L "$out_dir" ]]; then
+    destination=$(readlink "$out_dir")
+    # ensure the destination exists.
+    mkdir -p "$destination"
     return 0
   fi
 
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 8458e26..8b84485 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -253,7 +253,6 @@
     preinstalled-packages-asl-files.xml \
     preinstalled-packages-platform.xml \
     preinstalled-packages-strict-signature.xml \
-    printflags \
     privapp-permissions-platform.xml \
     prng_seeder \
     recovery-persist \
@@ -386,6 +385,9 @@
 ifeq ($(RELEASE_MEMORY_MANAGEMENT_DAEMON),true)
   PRODUCT_PACKAGES += \
         mm_daemon
+else
+  PRODUCT_PACKAGES += \
+        init-mmd-prop.rc
 endif
 
 # VINTF data for system image
@@ -393,10 +395,6 @@
     system_manifest.xml \
     system_compatibility_matrix.xml \
 
-# Base modules when shipping api level is less than or equal to 34
-PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 += \
-    android.hidl.memory@1.0-impl \
-
 # hwservicemanager is now installed on system_ext, but apexes might be using
 # old libraries that are expecting it to be installed on system. This allows
 # those apexes to continue working. The symlink can be removed once we are sure
@@ -581,3 +579,6 @@
 $(call soong_config_set, Aconfig, read_from_new_storage, $(RELEASE_READ_FROM_NEW_STORAGE))
 $(call soong_config_set, SettingsLib, legacy_avatar_picker_app_enabled, $(if $(RELEASE_AVATAR_PICKER_APP),,true))
 $(call soong_config_set, appsearch, enable_isolated_storage, $(RELEASE_APPSEARCH_ENABLE_ISOLATED_STORAGE))
+
+# Enable AppSearch Isolated Storage per BUILD flag
+PRODUCT_PRODUCT_PROPERTIES += appsearch.feature.enable_isolated_storage=$(RELEASE_APPSEARCH_ENABLE_ISOLATED_STORAGE)
diff --git a/target/product/base_system_ext.mk b/target/product/base_system_ext.mk
index 6767b9a..ad6828a 100644
--- a/target/product/base_system_ext.mk
+++ b/target/product/base_system_ext.mk
@@ -30,6 +30,7 @@
 PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 += \
     hwservicemanager \
     android.hidl.allocator@1.0-service \
+    android.hidl.memory@1.0-impl \
 
 # AppFunction Extensions
 ifneq (,$(RELEASE_APPFUNCTION_SIDECAR))
diff --git a/target/product/base_vendor.mk b/target/product/base_vendor.mk
index 16fc7fd..b4e450e 100644
--- a/target/product/base_vendor.mk
+++ b/target/product/base_vendor.mk
@@ -106,7 +106,6 @@
 # VINTF data for vendor image
 PRODUCT_PACKAGES += \
     vendor_compatibility_matrix.xml \
-    vendor_manifest.xml \
 
 # Base modules and settings for the debug ramdisk, which is then packed
 # into a boot-debug.img and a vendor_boot-debug.img.
diff --git a/target/product/build_variables.mk b/target/product/build_variables.mk
index c936911..7c54258 100644
--- a/target/product/build_variables.mk
+++ b/target/product/build_variables.mk
@@ -32,5 +32,8 @@
 # Use the configured MessageQueue implementation
 $(call soong_config_set, messagequeue, release_package_messagequeue_implementation, $(RELEASE_PACKAGE_MESSAGEQUEUE_IMPLEMENTATION))
 
+# Use the configured version of Cronet
+$(call soong_config_set,cronet,enable_cronet_tot,$(RELEASE_ENABLE_TOT_CRONET))
+
 # Use the configured version of WebView
 $(call soong_config_set, webview, release_package_webview_version, $(RELEASE_PACKAGE_WEBVIEW_VERSION))
diff --git a/target/product/generic/Android.bp b/target/product/generic/Android.bp
index b3dd6e8..c90c61c 100644
--- a/target/product/generic/Android.bp
+++ b/target/product/generic/Android.bp
@@ -640,7 +640,6 @@
         "preinstalled-packages-platform.xml", // base_system
         "preinstalled-packages-strict-signature.xml", // base_system
         "preloaded-classes", // ok
-        "printflags", // base_system
         "privapp-permissions-platform.xml", // base_system
         "prng_seeder", // base_system
         "public.libraries.android.txt",
@@ -699,7 +698,9 @@
         true: [
             "mm_daemon", // base_system (RELEASE_MEMORY_MANAGEMENT_DAEMON)
         ],
-        default: [],
+        default: [
+            "init-mmd-prop.rc", // base_system
+        ],
     }) + select(product_variable("debuggable"), {
         true: [
             "alloctop",
diff --git a/target/product/virtual_ab_ota/vabc_features.mk b/target/product/virtual_ab_ota/vabc_features.mk
index d092699..0339ebd 100644
--- a/target/product/virtual_ab_ota/vabc_features.mk
+++ b/target/product/virtual_ab_ota/vabc_features.mk
@@ -42,6 +42,7 @@
 # device's .mk file improve performance for low mem devices.
 #
 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.read_ahead_size=16
+# warning: enabling o_direct on devices with low CMA could lead to failures
 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.o_direct.enabled=true
 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.merge_thread_priority=19
 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.worker_thread_priority=0
@@ -52,6 +53,16 @@
 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.verify_threshold_size=1073741824
 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.verify_block_size=1048576
 
+
+# Enabling this property will skip verification post OTA reboot.
+# Verification allows the device to safely roll back if any boot failures
+# are detected.  If the verification is disabled, update_verifier to will
+# try to verify using bufferred read if care_map.pb is present in
+# /metadata/ota/. This will increase the boot time and may also impact
+# memory usage as all the blocks in dynamic partitions are read into page-cache.
+# If care_map.pb isn't present, update-verifier will skip the verification.
+# PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.skip_verification =true
+
 # Enabling this property, will improve OTA install time
 # but will use an additional CPU core
 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.threads=true
diff --git a/teams/Android.bp b/teams/Android.bp
index 6fc45bf..7946a3d 100644
--- a/teams/Android.bp
+++ b/teams/Android.bp
@@ -2520,7 +2520,7 @@
 }
 
 team {
-    name: "trendy_team_xr_framework",
+    name: "trendy_team_virtual_device_framework",
 
     // go/trendy/manage/engineers/4798040542445568
     trendy_team_id: "4798040542445568",
diff --git a/tools/Android.bp b/tools/Android.bp
index 243cb56..f1ff1c4 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -85,11 +85,6 @@
     srcs: [
         "list_files.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
 }
 
 python_test_host {
@@ -109,11 +104,6 @@
 python_binary_host {
     name: "characteristics_rro_generator",
     srcs: ["characteristics_rro_generator.py"],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
 }
 
 python_binary_host {
diff --git a/tools/aconfig/Cargo.toml b/tools/aconfig/Cargo.toml
index a031b7f..cb8377e 100644
--- a/tools/aconfig/Cargo.toml
+++ b/tools/aconfig/Cargo.toml
@@ -8,8 +8,8 @@
     "aconfig_storage_read_api",
     "aconfig_storage_write_api",
     "aflags",
-    "printflags",
-    "convert_finalized_flags"
+    "convert_finalized_flags",
+    "exported_flag_check",
 ]
 
 resolver = "2"
diff --git a/tools/aconfig/OWNERS b/tools/aconfig/OWNERS
index c92fc7c..0c31938 100644
--- a/tools/aconfig/OWNERS
+++ b/tools/aconfig/OWNERS
@@ -1,6 +1,5 @@
 dzshen@google.com
 opg@google.com
-tedbauer@google.com
 zhidou@google.com
 
 amhk@google.com  #{LAST_RESORT_SUGGESTION}
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index 6e53018..b1cc602 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -43,10 +43,6 @@
       "name": "aflags.test"
     },
     {
-      // printflags unit tests
-      "name": "printflags.test"
-    },
-    {
       // aconfig_protos unit tests
       "name": "aconfig_protos.test"
     },
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index 4b670a0..e9c95fd 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -59,7 +59,8 @@
     let runtime_lookup_required =
         flag_elements.iter().any(|elem| elem.is_read_write) || library_exported;
     let container = (flag_elements.first().expect("zero template flags").container).to_string();
-    let is_platform_container = matches!(container.as_str(), "system" | "product" | "vendor");
+    let is_platform_container =
+        matches!(container.as_str(), "system" | "system_ext" | "product" | "vendor");
     let context = Context {
         flag_elements,
         namespace_flags,
@@ -641,7 +642,6 @@
         package com.android.aconfig.test;
         // TODO(b/303773055): Remove the annotation after access issue is resolved.
         import android.compat.annotation.UnsupportedAppUsage;
-        import android.os.Build;
         import android.os.flagging.PlatformAconfigPackageInternal;
         import android.util.Log;
         /** @hide */
@@ -796,6 +796,7 @@
 
         let expect_flags_content = r#"
         package com.android.aconfig.test;
+        import android.os.Build;
         /** @hide */
         public final class Flags {
             /** @hide */
@@ -892,12 +893,16 @@
         package com.android.aconfig.test;
 
         import java.util.Arrays;
+        import java.util.HashMap;
+        import java.util.Map;
         import java.util.HashSet;
         import java.util.List;
         import java.util.Set;
         import java.util.function.BiPredicate;
         import java.util.function.Predicate;
 
+        import android.os.Build;
+
         /** @hide */
         public class CustomFeatureFlags implements FeatureFlags {
 
@@ -940,6 +945,19 @@
                     ""
                 )
             );
+
+            private Map<String, Integer> mFinalizedFlags = new HashMap<>(
+                Map.ofEntries(
+                    Map.entry("", Integer.MAX_VALUE)
+                )
+            );
+
+            public boolean isFlagFinalized(String flagName) {
+                if (!mFinalizedFlags.containsKey(flagName)) {
+                    return false;
+                }
+                return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName);
+            }
         }
     "#;
 
@@ -1001,6 +1019,7 @@
 
         let expect_flags_content = r#"
         package com.android.aconfig.test;
+        import android.os.Build;
         /** @hide */
         public final class Flags {
             /** @hide */
@@ -1087,11 +1106,14 @@
         package com.android.aconfig.test;
 
         import java.util.Arrays;
+        import java.util.HashMap;
+        import java.util.Map;
         import java.util.HashSet;
         import java.util.List;
         import java.util.Set;
         import java.util.function.BiPredicate;
         import java.util.function.Predicate;
+        import android.os.Build;
 
         /** @hide */
         public class CustomFeatureFlags implements FeatureFlags {
@@ -1135,6 +1157,19 @@
                     ""
                 )
             );
+
+            private Map<String, Integer> mFinalizedFlags = new HashMap<>(
+                Map.ofEntries(
+                    Map.entry("", Integer.MAX_VALUE)
+                )
+            );
+
+            public boolean isFlagFinalized(String flagName) {
+                if (!mFinalizedFlags.containsKey(flagName)) {
+                    return false;
+                }
+                return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName);
+            }
         }
     "#;
 
@@ -1204,6 +1239,7 @@
 
         let expect_flags_content = r#"
         package com.android.aconfig.test;
+        import android.os.Build;
         /** @hide */
         public final class Flags {
             /** @hide */
@@ -1213,6 +1249,9 @@
             /** @hide */
             public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported";
             public static boolean disabledRwExported() {
+                if (Build.VERSION.SDK_INT >= 36) {
+                  return true;
+                }
                 return FEATURE_FLAGS.disabledRwExported();
             }
             public static boolean enabledFixedRoExported() {
@@ -1290,11 +1329,14 @@
         package com.android.aconfig.test;
 
         import java.util.Arrays;
+        import java.util.HashMap;
+        import java.util.Map;
         import java.util.HashSet;
         import java.util.List;
         import java.util.Set;
         import java.util.function.BiPredicate;
         import java.util.function.Predicate;
+        import android.os.Build;
 
         /** @hide */
         public class CustomFeatureFlags implements FeatureFlags {
@@ -1338,6 +1380,20 @@
                     ""
                 )
             );
+
+            private Map<String, Integer> mFinalizedFlags = new HashMap<>(
+                Map.ofEntries(
+                    Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, 36),
+                    Map.entry("", Integer.MAX_VALUE)
+                )
+            );
+
+            public boolean isFlagFinalized(String flagName) {
+                if (!mFinalizedFlags.containsKey(flagName)) {
+                    return false;
+                }
+                return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName);
+            }
         }
     "#;
 
@@ -1373,6 +1429,56 @@
         assert!(file_set.is_empty());
     }
 
+    // Test that the SDK check isn't added unless the library is exported (even
+    // if the flag is present in finalized_flags).
+    #[test]
+    fn test_generate_java_code_flags_with_sdk_check() {
+        let parsed_flags = crate::test::parse_test_flags();
+        let mode = CodegenMode::Production;
+        let modified_parsed_flags =
+            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
+        let flag_ids =
+            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
+        let mut finalized_flags = FinalizedFlagMap::new();
+        finalized_flags.insert_if_new(
+            ApiLevel(36),
+            FinalizedFlag {
+                flag_name: "disabled_rw".to_string(),
+                package_name: "com.android.aconfig.test".to_string(),
+            },
+        );
+        let config = JavaCodegenConfig {
+            codegen_mode: mode,
+            flag_ids,
+            allow_instrumentation: true,
+            package_fingerprint: 5801144784618221668,
+            new_exported: true,
+            single_exported_file: false,
+            finalized_flags,
+        };
+        let generated_files = generate_java_code(
+            crate::test::TEST_PACKAGE,
+            modified_parsed_flags.into_iter(),
+            config,
+        )
+        .unwrap();
+
+        let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
+            + r#"
+        private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
+        }"#;
+
+        let file = generated_files.iter().find(|f| f.path.ends_with("Flags.java")).unwrap();
+        assert_eq!(
+            None,
+            crate::test::first_significant_code_diff(
+                &expect_flags_content,
+                &String::from_utf8(file.contents.clone()).unwrap()
+            ),
+            "Flags content is not correct"
+        );
+    }
+
     #[test]
     fn test_generate_java_code_test() {
         let parsed_flags = crate::test::parse_test_flags();
@@ -1788,6 +1894,109 @@
     }
 
     #[test]
+    fn test_generate_java_code_exported_flags() {
+        let parsed_flags = crate::test::parse_test_flags();
+        let mode = CodegenMode::Exported;
+        let modified_parsed_flags =
+            crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
+        let flag_ids =
+            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
+        let mut finalized_flags = FinalizedFlagMap::new();
+        finalized_flags.insert_if_new(
+            ApiLevel(36),
+            FinalizedFlag {
+                flag_name: "disabled_rw_exported".to_string(),
+                package_name: "com.android.aconfig.test".to_string(),
+            },
+        );
+        let config = JavaCodegenConfig {
+            codegen_mode: mode,
+            flag_ids,
+            allow_instrumentation: true,
+            package_fingerprint: 5801144784618221668,
+            new_exported: true,
+            single_exported_file: true,
+            finalized_flags,
+        };
+        let generated_files = generate_java_code(
+            crate::test::TEST_PACKAGE,
+            modified_parsed_flags.into_iter(),
+            config,
+        )
+        .unwrap();
+
+        let expect_exported_flags_content = r#"
+        package com.android.aconfig.test;
+
+        import android.os.Build;
+        import android.os.flagging.AconfigPackage;
+        import android.util.Log;
+        public final class ExportedFlags {
+
+            public static final String FLAG_DISABLED_RW_EXPORTED = "com.android.aconfig.test.disabled_rw_exported";
+            public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = "com.android.aconfig.test.enabled_fixed_ro_exported";
+            public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported";
+            private static final String TAG = "ExportedFlags";
+            private static volatile boolean isCached = false;
+
+            private static boolean disabledRwExported = false;
+            private static boolean enabledFixedRoExported = false;
+            private static boolean enabledRoExported = false;
+            private ExportedFlags() {}
+
+            private void init() {
+                try {
+                    AconfigPackage reader = AconfigPackage.load("com.android.aconfig.test");
+                    disabledRwExported = reader.getBooleanFlagValue("disabled_rw_exported", false);
+                    enabledFixedRoExported = reader.getBooleanFlagValue("enabled_fixed_ro_exported", false);
+                    enabledRoExported = reader.getBooleanFlagValue("enabled_ro_exported", false);
+                } catch (Exception e) {
+                    // pass
+                    Log.e(TAG, e.toString());
+                } catch (LinkageError e) {
+                    // for mainline module running on older devices.
+                    // This should be replaces to version check, after the version bump.
+                    Log.w(TAG, e.toString());
+                }
+                isCached = true;
+            }
+            public static boolean disabledRwExported() {
+                if (Build.VERSION.SDK_INT >= 36) {
+                  return true;
+                }
+
+                if (!featureFlags.isCached) {
+                    featureFlags.init();
+                }
+                return featureFlags.disabledRwExported;
+            }
+            public static boolean enabledFixedRoExported() {
+                if (!featureFlags.isCached) {
+                    featureFlags.init();
+                }
+                return featureFlags.enabledFixedRoExported;
+            }
+            public static boolean enabledRoExported() {
+                if (!featureFlags.isCached) {
+                    featureFlags.init();
+                }
+                return featureFlags.enabledRoExported;
+            }
+            private static ExportedFlags featureFlags = new ExportedFlags();
+        }"#;
+
+        let file = generated_files.iter().find(|f| f.path.ends_with("ExportedFlags.java")).unwrap();
+        assert_eq!(
+            None,
+            crate::test::first_significant_code_diff(
+                expect_exported_flags_content,
+                &String::from_utf8(file.contents.clone()).unwrap()
+            ),
+            "ExportedFlags content is not correct"
+        );
+    }
+
+    #[test]
     fn test_format_java_method_name() {
         let expected = "someSnakeName";
         let input = "____some_snake___name____";
diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
index 0c80d3b..14a98f0 100644
--- a/tools/aconfig/aconfig/src/commands.rs
+++ b/tools/aconfig/aconfig/src/commands.rs
@@ -81,18 +81,8 @@
             .read_to_string(&mut contents)
             .with_context(|| format!("failed to read {}", input.source))?;
 
-        let mut flag_declarations =
-            aconfig_protos::flag_declarations::try_from_text_proto(&contents)
-                .with_context(|| input.error_context())?;
-
-        // system_ext flags should be treated as system flags as we are combining /system_ext
-        // and /system as one container
-        // TODO: remove this logic when we start enforcing that system_ext cannot be set as
-        // container in aconfig declaration files.
-        if flag_declarations.container() == "system_ext" {
-            flag_declarations.set_container(String::from("system"));
-        }
-
+        let flag_declarations = aconfig_protos::flag_declarations::try_from_text_proto(&contents)
+            .with_context(|| input.error_context())?;
         ensure!(
             package == flag_declarations.package(),
             "failed to parse {}: expected package {}, got {}",
@@ -462,6 +452,7 @@
 pub fn should_include_flag(pf: &ProtoParsedFlag) -> bool {
     let should_filter_container = pf.container == Some("vendor".to_string())
         || pf.container == Some("system".to_string())
+        || pf.container == Some("system_ext".to_string())
         || pf.container == Some("product".to_string());
 
     let disabled_ro = pf.state == Some(ProtoFlagState::DISABLED.into())
diff --git a/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template b/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template
index ef18367..c702c9b 100644
--- a/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template
+++ b/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template
@@ -5,11 +5,18 @@
 import android.compat.annotation.UnsupportedAppUsage;
 {{ -endif }}
 import java.util.Arrays;
+{{ -if library_exported }}
+import java.util.HashMap;
+import java.util.Map;
+{{ -endif }}
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.function.BiPredicate;
 import java.util.function.Predicate;
+{{ -if library_exported }}
+import android.os.Build;
+{{ -endif }}
 
 {{ -if single_exported_file }}
 {{ -if library_exported }}
@@ -73,4 +80,24 @@
             ""{# The empty string here is to resolve the ending comma #}
         )
     );
+
+{{ -if library_exported }}
+    private Map<String, Integer> mFinalizedFlags = new HashMap<>(
+        Map.ofEntries(
+            {{ -for item in flag_elements }}
+            {{ -if item.finalized_sdk_present }}
+            Map.entry(Flags.FLAG_{item.flag_name_constant_suffix}, {item.finalized_sdk_value}),
+            {{ -endif }}
+            {{ -endfor }}
+            Map.entry("", Integer.MAX_VALUE){# The empty entry to avoid empty entries #}
+        )
+    );
+
+    public boolean isFlagFinalized(String flagName) \{
+        if (!mFinalizedFlags.containsKey(flagName)) \{
+            return false;
+        }
+        return Build.VERSION.SDK_INT >= mFinalizedFlags.get(flagName);
+    }
+{{ -endif }}
 }
diff --git a/tools/aconfig/aconfig/templates/ExportedFlags.java.template b/tools/aconfig/aconfig/templates/ExportedFlags.java.template
index 4e36942..176da18 100644
--- a/tools/aconfig/aconfig/templates/ExportedFlags.java.template
+++ b/tools/aconfig/aconfig/templates/ExportedFlags.java.template
@@ -19,11 +19,8 @@
             AconfigPackage reader = AconfigPackage.load("{package_name}");
             {{ -for namespace_with_flags in namespace_flags }}
             {{ -for flag in namespace_with_flags.flags }}
-            {{ -if flag.finalized_sdk_present }}
-            {flag.method_name} = Build.VERSION.SDK_INT >= {flag.finalized_sdk_value} ? true : reader.getBooleanFlagValue("{flag.flag_name}", {flag.default_value});
-            {{ - else }} {#- else finalized_sdk_present #}
             {flag.method_name} = reader.getBooleanFlagValue("{flag.flag_name}", {flag.default_value});
-            {{ -endif}}  {#- end finalized_sdk_present#}
+
             {{ -endfor }} {#- end namespace_with_flags.flags #}
             {{ -endfor }} {#- end namespace_flags #}
         } catch (Exception e) \{
@@ -39,6 +36,11 @@
 
 {{ -for flag in flag_elements }}
     public static boolean {flag.method_name}() \{
+        {{ -if flag.finalized_sdk_present }}
+        if (Build.VERSION.SDK_INT >= {flag.finalized_sdk_value}) \{
+          return true;
+        }
+        {{ -endif}}  {#- end finalized_sdk_present#}
         if (!featureFlags.isCached) \{
             featureFlags.init();
         }
diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.new_storage.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.new_storage.java.template
index 9492a83..8dc7581 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.new_storage.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.new_storage.java.template
@@ -2,7 +2,6 @@
 // TODO(b/303773055): Remove the annotation after access issue is resolved.
 import android.compat.annotation.UnsupportedAppUsage;
 {{ -if runtime_lookup_required }}
-import android.os.Build;
 {{ if is_platform_container }}
 import android.os.flagging.PlatformAconfigPackageInternal;
 {{ -else }} {#- else is_platform_container #}
diff --git a/tools/aconfig/aconfig/templates/Flags.java.template b/tools/aconfig/aconfig/templates/Flags.java.template
index 8a92d33..0cdc269 100644
--- a/tools/aconfig/aconfig/templates/Flags.java.template
+++ b/tools/aconfig/aconfig/templates/Flags.java.template
@@ -2,7 +2,9 @@
 {{ if not library_exported- }}
 // TODO(b/303773055): Remove the annotation after access issue is resolved.
 import android.compat.annotation.UnsupportedAppUsage;
-{{ -endif }}
+{{ else }}
+import android.os.Build;
+{{ -endif }} {#- end not library_exported#}
 {{ -if single_exported_file }}
 {{ -if library_exported }}
 /**
@@ -31,6 +33,13 @@
     @UnsupportedAppUsage
 {{ -endif }}
     public static boolean {item.method_name}() \{
+        {{ if library_exported- }}
+        {{ -if item.finalized_sdk_present }}
+        if (Build.VERSION.SDK_INT >= {item.finalized_sdk_value}) \{
+          return true;
+        }
+        {{ -endif}}  {#- end finalized_sdk_present#}
+        {{ -endif}}  {#- end library_exported#}
         return FEATURE_FLAGS.{item.method_name}();
     }
 {{ -endfor }}
diff --git a/tools/aconfig/aconfig_device_paths/partition_aconfig_flags_paths.txt b/tools/aconfig/aconfig_device_paths/partition_aconfig_flags_paths.txt
index e997e3d..140cd21 100644
--- a/tools/aconfig/aconfig_device_paths/partition_aconfig_flags_paths.txt
+++ b/tools/aconfig/aconfig_device_paths/partition_aconfig_flags_paths.txt
@@ -1,3 +1,4 @@
 "/system/etc/aconfig_flags.pb",
+"/system_ext/etc/aconfig_flags.pb",
 "/product/etc/aconfig_flags.pb",
 "/vendor/etc/aconfig_flags.pb",
diff --git a/tools/aconfig/aconfig_device_paths/src/lib.rs b/tools/aconfig/aconfig_device_paths/src/lib.rs
index 8871b4f..9ab9cea 100644
--- a/tools/aconfig/aconfig_device_paths/src/lib.rs
+++ b/tools/aconfig/aconfig_device_paths/src/lib.rs
@@ -62,12 +62,13 @@
 
     #[test]
     fn test_read_partition_paths() {
-        assert_eq!(read_partition_paths().len(), 3);
+        assert_eq!(read_partition_paths().len(), 4);
 
         assert_eq!(
             read_partition_paths(),
             vec![
                 PathBuf::from("/system/etc/aconfig_flags.pb"),
+                PathBuf::from("/system_ext/etc/aconfig_flags.pb"),
                 PathBuf::from("/product/etc/aconfig_flags.pb"),
                 PathBuf::from("/vendor/etc/aconfig_flags.pb")
             ]
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_info.rs b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
index cf16834..a39b7ed 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_info.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
@@ -199,49 +199,28 @@
     };
 
     // this test point locks down the value list serialization
-    // TODO: b/376108268 - Use parameterized tests.
     #[test]
-    fn test_serialization_default() {
-        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);
+    fn test_serialization() {
+        for file_version in 1..=MAX_SUPPORTED_FILE_VERSION {
+            let flag_info_list = create_test_flag_info_list(file_version);
 
-        let header: &FlagInfoHeader = &flag_info_list.header;
-        let reinterpreted_header = FlagInfoHeader::from_bytes(&header.into_bytes());
-        assert!(reinterpreted_header.is_ok());
-        assert_eq!(header, &reinterpreted_header.unwrap());
+            let header: &FlagInfoHeader = &flag_info_list.header;
+            let reinterpreted_header = FlagInfoHeader::from_bytes(&header.into_bytes());
+            assert!(reinterpreted_header.is_ok());
+            assert_eq!(header, &reinterpreted_header.unwrap());
 
-        let nodes: &Vec<FlagInfoNode> = &flag_info_list.nodes;
-        for node in nodes.iter() {
-            let reinterpreted_node = FlagInfoNode::from_bytes(&node.into_bytes()).unwrap();
-            assert_eq!(node, &reinterpreted_node);
+            let nodes: &Vec<FlagInfoNode> = &flag_info_list.nodes;
+            for node in nodes.iter() {
+                let reinterpreted_node = FlagInfoNode::from_bytes(&node.into_bytes()).unwrap();
+                assert_eq!(node, &reinterpreted_node);
+            }
+
+            let flag_info_bytes = flag_info_list.into_bytes();
+            let reinterpreted_info_list = FlagInfoList::from_bytes(&flag_info_bytes);
+            assert!(reinterpreted_info_list.is_ok());
+            assert_eq!(&flag_info_list, &reinterpreted_info_list.unwrap());
+            assert_eq!(flag_info_bytes.len() as u32, header.file_size);
         }
-
-        let flag_info_bytes = flag_info_list.into_bytes();
-        let reinterpreted_info_list = FlagInfoList::from_bytes(&flag_info_bytes);
-        assert!(reinterpreted_info_list.is_ok());
-        assert_eq!(&flag_info_list, &reinterpreted_info_list.unwrap());
-        assert_eq!(flag_info_bytes.len() as u32, header.file_size);
-    }
-
-    #[test]
-    fn test_serialization_max() {
-        let flag_info_list = create_test_flag_info_list(MAX_SUPPORTED_FILE_VERSION);
-
-        let header: &FlagInfoHeader = &flag_info_list.header;
-        let reinterpreted_header = FlagInfoHeader::from_bytes(&header.into_bytes());
-        assert!(reinterpreted_header.is_ok());
-        assert_eq!(header, &reinterpreted_header.unwrap());
-
-        let nodes: &Vec<FlagInfoNode> = &flag_info_list.nodes;
-        for node in nodes.iter() {
-            let reinterpreted_node = FlagInfoNode::from_bytes(&node.into_bytes()).unwrap();
-            assert_eq!(node, &reinterpreted_node);
-        }
-
-        let flag_info_bytes = flag_info_list.into_bytes();
-        let reinterpreted_info_list = FlagInfoList::from_bytes(&flag_info_bytes);
-        assert!(reinterpreted_info_list.is_ok());
-        assert_eq!(&flag_info_list, &reinterpreted_info_list.unwrap());
-        assert_eq!(flag_info_bytes.len() as u32, header.file_size);
     }
 
     // this test point locks down that version number should be at the top of serialized
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_table.rs b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
index 6fbee02..1b70c49 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
@@ -225,49 +225,28 @@
     };
 
     // this test point locks down the table serialization
-    // TODO: b/376108268 - Use parameterized tests.
     #[test]
-    fn test_serialization_default() {
-        let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION);
+    fn test_serialization() {
+        for file_version in 1..=MAX_SUPPORTED_FILE_VERSION {
+            let flag_table = create_test_flag_table(file_version);
 
-        let header: &FlagTableHeader = &flag_table.header;
-        let reinterpreted_header = FlagTableHeader::from_bytes(&header.into_bytes());
-        assert!(reinterpreted_header.is_ok());
-        assert_eq!(header, &reinterpreted_header.unwrap());
+            let header: &FlagTableHeader = &flag_table.header;
+            let reinterpreted_header = FlagTableHeader::from_bytes(&header.into_bytes());
+            assert!(reinterpreted_header.is_ok());
+            assert_eq!(header, &reinterpreted_header.unwrap());
 
-        let nodes: &Vec<FlagTableNode> = &flag_table.nodes;
-        for node in nodes.iter() {
-            let reinterpreted_node = FlagTableNode::from_bytes(&node.into_bytes()).unwrap();
-            assert_eq!(node, &reinterpreted_node);
+            let nodes: &Vec<FlagTableNode> = &flag_table.nodes;
+            for node in nodes.iter() {
+                let reinterpreted_node = FlagTableNode::from_bytes(&node.into_bytes()).unwrap();
+                assert_eq!(node, &reinterpreted_node);
+            }
+
+            let flag_table_bytes = flag_table.into_bytes();
+            let reinterpreted_table = FlagTable::from_bytes(&flag_table_bytes);
+            assert!(reinterpreted_table.is_ok());
+            assert_eq!(&flag_table, &reinterpreted_table.unwrap());
+            assert_eq!(flag_table_bytes.len() as u32, header.file_size);
         }
-
-        let flag_table_bytes = flag_table.into_bytes();
-        let reinterpreted_table = FlagTable::from_bytes(&flag_table_bytes);
-        assert!(reinterpreted_table.is_ok());
-        assert_eq!(&flag_table, &reinterpreted_table.unwrap());
-        assert_eq!(flag_table_bytes.len() as u32, header.file_size);
-    }
-
-    #[test]
-    fn test_serialization_max() {
-        let flag_table = create_test_flag_table(MAX_SUPPORTED_FILE_VERSION);
-
-        let header: &FlagTableHeader = &flag_table.header;
-        let reinterpreted_header = FlagTableHeader::from_bytes(&header.into_bytes());
-        assert!(reinterpreted_header.is_ok());
-        assert_eq!(header, &reinterpreted_header.unwrap());
-
-        let nodes: &Vec<FlagTableNode> = &flag_table.nodes;
-        for node in nodes.iter() {
-            let reinterpreted_node = FlagTableNode::from_bytes(&node.into_bytes()).unwrap();
-            assert_eq!(node, &reinterpreted_node);
-        }
-
-        let flag_table_bytes = flag_table.into_bytes();
-        let reinterpreted_table = FlagTable::from_bytes(&flag_table_bytes);
-        assert!(reinterpreted_table.is_ok());
-        assert_eq!(&flag_table, &reinterpreted_table.unwrap());
-        assert_eq!(flag_table_bytes.len() as u32, header.file_size);
     }
 
     // this test point locks down that version number should be at the top of serialized
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_value.rs b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
index 9a14bec..d73bcfb 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_value.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
@@ -138,37 +138,21 @@
 
     #[test]
     // this test point locks down the value list serialization
-    // TODO: b/376108268 - Use parameterized tests.
-    fn test_serialization_default() {
-        let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);
+    fn test_serialization() {
+        for file_version in 1..=MAX_SUPPORTED_FILE_VERSION {
+            let flag_value_list = create_test_flag_value_list(file_version);
 
-        let header: &FlagValueHeader = &flag_value_list.header;
-        let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes());
-        assert!(reinterpreted_header.is_ok());
-        assert_eq!(header, &reinterpreted_header.unwrap());
+            let header: &FlagValueHeader = &flag_value_list.header;
+            let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes());
+            assert!(reinterpreted_header.is_ok());
+            assert_eq!(header, &reinterpreted_header.unwrap());
 
-        let flag_value_bytes = flag_value_list.into_bytes();
-        let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_bytes);
-        assert!(reinterpreted_value_list.is_ok());
-        assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap());
-        assert_eq!(flag_value_bytes.len() as u32, header.file_size);
-    }
-
-    #[test]
-    // this test point locks down the value list serialization
-    fn test_serialization_max() {
-        let flag_value_list = create_test_flag_value_list(MAX_SUPPORTED_FILE_VERSION);
-
-        let header: &FlagValueHeader = &flag_value_list.header;
-        let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes());
-        assert!(reinterpreted_header.is_ok());
-        assert_eq!(header, &reinterpreted_header.unwrap());
-
-        let flag_value_bytes = flag_value_list.into_bytes();
-        let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_bytes);
-        assert!(reinterpreted_value_list.is_ok());
-        assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap());
-        assert_eq!(flag_value_bytes.len() as u32, header.file_size);
+            let flag_value_bytes = flag_value_list.into_bytes();
+            let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_bytes);
+            assert!(reinterpreted_value_list.is_ok());
+            assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap());
+            assert_eq!(flag_value_bytes.len() as u32, header.file_size);
+        }
     }
 
     #[test]
diff --git a/tools/aconfig/aconfig_storage_file/src/package_table.rs b/tools/aconfig/aconfig_storage_file/src/package_table.rs
index 21357c7..4d6bd91 100644
--- a/tools/aconfig/aconfig_storage_file/src/package_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/package_table.rs
@@ -287,50 +287,28 @@
 
     #[test]
     // this test point locks down the table serialization
-    // TODO: b/376108268 - Use parameterized tests.
-    fn test_serialization_default() {
-        let package_table = create_test_package_table(DEFAULT_FILE_VERSION);
-        let header: &PackageTableHeader = &package_table.header;
-        let reinterpreted_header = PackageTableHeader::from_bytes(&header.into_bytes());
-        assert!(reinterpreted_header.is_ok());
-        assert_eq!(header, &reinterpreted_header.unwrap());
+    fn test_serialization() {
+        for file_version in 1..=MAX_SUPPORTED_FILE_VERSION {
+            let package_table = create_test_package_table(file_version);
+            let header: &PackageTableHeader = &package_table.header;
+            let reinterpreted_header = PackageTableHeader::from_bytes(&header.into_bytes());
+            assert!(reinterpreted_header.is_ok());
+            assert_eq!(header, &reinterpreted_header.unwrap());
 
-        let nodes: &Vec<PackageTableNode> = &package_table.nodes;
-        for node in nodes.iter() {
-            let reinterpreted_node =
-                PackageTableNode::from_bytes(&node.into_bytes(header.version), header.version)
-                    .unwrap();
-            assert_eq!(node, &reinterpreted_node);
+            let nodes: &Vec<PackageTableNode> = &package_table.nodes;
+            for node in nodes.iter() {
+                let reinterpreted_node =
+                    PackageTableNode::from_bytes(&node.into_bytes(header.version), header.version)
+                        .unwrap();
+                assert_eq!(node, &reinterpreted_node);
+            }
+
+            let package_table_bytes = package_table.into_bytes();
+            let reinterpreted_table = PackageTable::from_bytes(&package_table_bytes);
+            assert!(reinterpreted_table.is_ok());
+            assert_eq!(&package_table, &reinterpreted_table.unwrap());
+            assert_eq!(package_table_bytes.len() as u32, header.file_size);
         }
-
-        let package_table_bytes = package_table.into_bytes();
-        let reinterpreted_table = PackageTable::from_bytes(&package_table_bytes);
-        assert!(reinterpreted_table.is_ok());
-        assert_eq!(&package_table, &reinterpreted_table.unwrap());
-        assert_eq!(package_table_bytes.len() as u32, header.file_size);
-    }
-
-    #[test]
-    fn test_serialization_max() {
-        let package_table = create_test_package_table(MAX_SUPPORTED_FILE_VERSION);
-        let header: &PackageTableHeader = &package_table.header;
-        let reinterpreted_header = PackageTableHeader::from_bytes(&header.into_bytes());
-        assert!(reinterpreted_header.is_ok());
-        assert_eq!(header, &reinterpreted_header.unwrap());
-
-        let nodes: &Vec<PackageTableNode> = &package_table.nodes;
-        for node in nodes.iter() {
-            let reinterpreted_node =
-                PackageTableNode::from_bytes(&node.into_bytes(header.version), header.version)
-                    .unwrap();
-            assert_eq!(node, &reinterpreted_node);
-        }
-
-        let package_table_bytes = package_table.into_bytes();
-        let reinterpreted_table = PackageTable::from_bytes(&package_table_bytes);
-        assert!(reinterpreted_table.is_ok());
-        assert_eq!(&package_table, &reinterpreted_table.unwrap());
-        assert_eq!(package_table_bytes.len() as u32, header.file_size);
     }
 
     #[test]
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java
index dc465b6..213f158 100644
--- a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java
@@ -26,6 +26,9 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.Objects;
+import java.util.concurrent.CyclicBarrier;
+
 @RunWith(JUnit4.class)
 public class FlagTableTest {
 
@@ -100,4 +103,53 @@
         assertEquals(-1, node7.getNextOffset());
         assertEquals(-1, node8.getNextOffset());
     }
+
+    @Test
+    public void testFlagTable_multithreadsRead() throws Exception {
+        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(2));
+
+        int numberOfThreads = 8;
+        Thread[] threads = new Thread[numberOfThreads];
+        final CyclicBarrier gate = new CyclicBarrier(numberOfThreads + 1);
+        String[] expects = {
+            "enabled_ro",
+            "enabled_rw",
+            "enabled_rw",
+            "disabled_rw",
+            "enabled_fixed_ro",
+            "enabled_ro",
+            "enabled_fixed_ro",
+            "disabled_rw"
+        };
+        int[] packageIds = {0, 0, 2, 1, 1, 1, 2, 0};
+
+        for (int i = 0; i < numberOfThreads; i++) {
+            String expectRet = expects[i];
+            int packageId = packageIds[i];
+            threads[i] =
+                    new Thread() {
+                        @Override
+                        public void run() {
+                            try {
+                                gate.await();
+                            } catch (Exception e) {
+                            }
+                            for (int j = 0; j < 10; j++) {
+                                if (!Objects.equals(
+                                        expectRet,
+                                        flagTable.get(packageId, expectRet).getFlagName())) {
+                                    throw new RuntimeException();
+                                }
+                            }
+                        }
+                    };
+            threads[i].start();
+        }
+
+        gate.await();
+
+        for (int i = 0; i < numberOfThreads; i++) {
+            threads[i].join();
+        }
+    }
 }
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java
index 306df7d..6311c19 100644
--- a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java
@@ -28,6 +28,9 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.Objects;
+import java.util.concurrent.CyclicBarrier;
+
 @RunWith(JUnit4.class)
 public class FlagValueListTest {
 
@@ -74,4 +77,43 @@
         fNode = flagTable.get(pNode.getPackageId(), "enabled_fixed_ro");
         assertTrue(flagValueList.getBoolean(pNode.getBooleanStartIndex() + fNode.getFlagIndex()));
     }
+
+    @Test
+    public void testFlagValueList_multithreadsRead() throws Exception {
+        FlagValueList flagValueList =
+                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(2));
+
+        int numberOfThreads = 8;
+        Thread[] threads = new Thread[numberOfThreads];
+        final CyclicBarrier gate = new CyclicBarrier(numberOfThreads + 1);
+        boolean[] expects = {false, true, true, false, true, true, true, true};
+
+        for (int i = 0; i < numberOfThreads; i++) {
+            boolean expectRet = expects[i];
+            int position = i;
+            threads[i] =
+                    new Thread() {
+                        @Override
+                        public void run() {
+                            try {
+                                gate.await();
+                            } catch (Exception e) {
+                            }
+                            for (int j = 0; j < 10; j++) {
+                                if (!Objects.equals(
+                                        expectRet, flagValueList.getBoolean(position))) {
+                                    throw new RuntimeException();
+                                }
+                            }
+                        }
+                    };
+            threads[i].start();
+        }
+
+        gate.await();
+
+        for (int i = 0; i < numberOfThreads; i++) {
+            threads[i].join();
+        }
+    }
 }
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java
index ddad249..3dd24b2 100644
--- a/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java
@@ -65,7 +65,11 @@
     /** @hide */
     @UnsupportedAppUsage
     public static final Set<String> PLATFORM_PACKAGE_MAP_FILES =
-            Set.of("system.package.map", "vendor.package.map", "product.package.map");
+            Set.of(
+                    "system.package.map",
+                    "system_ext.package.map",
+                    "vendor.package.map",
+                    "product.package.map");
 
     static {
         for (String pf : PLATFORM_PACKAGE_MAP_FILES) {
@@ -116,11 +120,11 @@
             return aconfigPackage;
         } catch (AconfigStorageException e) {
             throw new AconfigStorageReadException(
-                    e.getErrorCode(), "Fail to create AconfigPackage", e);
+                    e.getErrorCode(), "Fail to create PlatformAconfigPackage: " + packageName, e);
         } catch (Exception e) {
             throw new AconfigStorageReadException(
                     AconfigStorageReadException.ERROR_GENERIC,
-                    "Fail to create PlatformAconfigPackage",
+                    "Fail to create PlatformAconfigPackage: " + packageName,
                     e);
         }
     }
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java
index 9896baf..0c5bc1c 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java
+++ b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java
@@ -42,7 +42,8 @@
 @RunWith(JUnit4.class)
 public class PlatformAconfigPackageInternalTest {
 
-    private static final Set<String> PLATFORM_CONTAINERS = Set.of("system", "vendor", "product");
+    private static final Set<String> PLATFORM_CONTAINERS =
+            Set.of("system", "system_ext", "vendor", "product");
 
     @Test
     public void testPlatformAconfigPackageInternal_load() throws IOException {
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java
index 1c6c238..2b4ead8 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java
+++ b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java
@@ -42,7 +42,8 @@
 @RunWith(JUnit4.class)
 public class PlatformAconfigPackageTest {
 
-    private static final Set<String> PLATFORM_CONTAINERS = Set.of("system", "vendor", "product");
+    private static final Set<String> PLATFORM_CONTAINERS =
+            Set.of("system", "system_ext", "vendor", "product");
 
     @Test
     public void testPlatformAconfigPackage_StorageFilesCache() throws IOException {
diff --git a/tools/aconfig/exported_flag_check/Android.bp b/tools/aconfig/exported_flag_check/Android.bp
new file mode 100644
index 0000000..184149a
--- /dev/null
+++ b/tools/aconfig/exported_flag_check/Android.bp
@@ -0,0 +1,28 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+    name: "exported-flag-check-defaults",
+    edition: "2021",
+    clippy_lints: "android",
+    lints: "android",
+    srcs: ["src/main.rs"],
+    rustlibs: [
+        "libaconfig_protos",
+        "libanyhow",
+        "libclap",
+        "libregex",
+    ],
+}
+
+rust_binary_host {
+    name: "exported-flag-check",
+    defaults: ["record-finalized-flags-defaults"],
+}
+
+rust_test_host {
+    name: "exported-flag-check-test",
+    defaults: ["record-finalized-flags-defaults"],
+    test_suites: ["general-tests"],
+}
diff --git a/tools/aconfig/printflags/Cargo.toml b/tools/aconfig/exported_flag_check/Cargo.toml
similarity index 63%
rename from tools/aconfig/printflags/Cargo.toml
rename to tools/aconfig/exported_flag_check/Cargo.toml
index 7313f5d..6bc07c5 100644
--- a/tools/aconfig/printflags/Cargo.toml
+++ b/tools/aconfig/exported_flag_check/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "printflags"
+name = "exported-flag-check"
 version = "0.1.0"
 edition = "2021"
 
@@ -8,8 +8,7 @@
 cargo = []
 
 [dependencies]
-anyhow = "1.0.69"
-paste = "1.0.11"
-protobuf = "3.2.0"
-regex = "1.10.3"
 aconfig_protos = { path = "../aconfig_protos" }
+anyhow = "1.0.69"
+clap = { version = "4.1.8", features = ["derive"] }
+regex = "1.11.1"
diff --git a/tools/aconfig/exported_flag_check/allow_flag_list.txt b/tools/aconfig/exported_flag_check/allow_flag_list.txt
new file mode 100644
index 0000000..93dacee
--- /dev/null
+++ b/tools/aconfig/exported_flag_check/allow_flag_list.txt
@@ -0,0 +1,393 @@
+android.adpf.adpf_viewrootimpl_action_down_boost
+android.app.admin.flags.coexistence_migration_for_supervision_enabled
+android.app.admin.flags.enable_supervision_service_sync
+android.app.admin.flags.lock_now_coexistence
+android.app.admin.flags.permission_migration_for_zero_trust_api_enabled
+android.app.admin.flags.reset_password_with_token_coexistence
+android.app.admin.flags.set_application_restrictions_coexistence
+android.app.admin.flags.set_backup_service_enabled_coexistence
+android.app.admin.flags.set_keyguard_disabled_features_coexistence
+android.app.admin.flags.set_permission_grant_state_coexistence
+android.app.app_restrictions_api
+android.app.enforce_pic_testmode_protocol
+android.app.job.backup_jobs_exemption
+android.app.pic_uses_shared_memory
+android.app.pinner_service_client_api
+android.app.supervision.flags.deprecate_dpm_supervision_apis
+android.app.supervision.flags.enable_sync_with_dpm
+android.app.supervision.flags.supervision_api
+android.app.supervision.flags.supervision_api_on_wear
+android.app.ui_rich_ongoing
+android.appwidget.flags.use_smaller_app_widget_system_radius
+android.car.feature.always_send_initial_value_event
+android.car.feature.android_b_vehicle_properties
+android.car.feature.android_vic_vehicle_properties
+android.car.feature.area_id_config_access
+android.car.feature.async_audio_service_init
+android.car.feature.audio_control_hal_configuration
+android.car.feature.audio_legacy_mode_navigation_volume
+android.car.feature.audio_vendor_freeze_improvements
+android.car.feature.batched_subscriptions
+android.car.feature.car_app_card
+android.car.feature.car_audio_dynamic_devices
+android.car.feature.car_audio_fade_manager_configuration
+android.car.feature.car_audio_min_max_activation_volume
+android.car.feature.car_audio_mute_ambiguity
+android.car.feature.car_evs_query_service_status
+android.car.feature.car_evs_stream_management
+android.car.feature.car_night_global_setting
+android.car.feature.car_power_cancel_shell_command
+android.car.feature.car_property_detailed_error_codes
+android.car.feature.car_property_supported_value
+android.car.feature.car_property_value_property_status
+android.car.feature.cluster_health_monitoring
+android.car.feature.display_compatibility
+android.car.feature.handle_property_events_in_binder_thread
+android.car.feature.persist_ap_settings
+android.car.feature.projection_query_bt_profile_inhibit
+android.car.feature.serverless_remote_access
+android.car.feature.subscription_with_resolution
+android.car.feature.supports_secure_passenger_users
+android.car.feature.switch_user_ignoring_uxr
+android.car.feature.variable_update_rate
+android.car.feature.visible_background_user_restrictions
+android.companion.new_association_builder
+android.companion.ongoing_perm_sync
+android.companion.virtualdevice.flags.camera_multiple_input_streams
+android.companion.virtualdevice.flags.notifications_for_device_streaming
+android.content.pm.get_package_storage_stats
+android.content.res.layout_readwrite_flags
+android.content.res.resources_minor_version_support
+android.content.res.rro_control_for_android_no_overlayable
+android.content.res.self_targeting_android_resource_frro
+android.content.res.system_context_handle_app_info_changed
+android.credentials.flags.settings_activity_enabled
+android.hardware.biometrics.screen_off_unlock_udfps
+android.hardware.devicestate.feature.flags.device_state_property_migration
+android.hardware.devicestate.feature.flags.device_state_rdm_v2
+android.hardware.devicestate.feature.flags.device_state_requester_cancel_state
+android.hardware.usb.flags.enable_interface_name_device_filter
+android.hardware.usb.flags.enable_is_mode_change_supported_api
+android.media.audio.focus_exclusive_with_recording
+android.media.audio.focus_freeze_test_api
+android.media.audio.foreground_audio_control
+android.media.audio.hardening_permission_api
+android.media.audio.hardening_permission_spa
+android.media.audio.ro_foreground_audio_control
+android.media.audiopolicy.audio_mix_test_api
+android.media.codec.aidl_hal_input_surface
+android.media.swcodec.flags.apv_software_codec
+android.media.swcodec.flags.mpeg2_keep_threads_active
+android.media.tv.flags.enable_le_audio_broadcast_ui
+android.media.tv.flags.enable_le_audio_unicast_ui
+android.media.tv.flags.hdmi_control_collect_physical_address
+android.media.tv.flags.hdmi_control_enhanced_behavior
+android.media.tv.flags.tif_unbind_inactive_tis
+android.multiuser.enable_biometrics_to_unlock_private_space
+android.net.platform.flags.mdns_improvement_for_25q2
+android.nfc.nfc_persist_log
+android.nfc.nfc_watchdog
+android.os.adpf_graphics_pipeline
+android.os.android_os_build_vanilla_ice_cream
+android.os.battery_saver_supported_check_api
+android.os.network_time_uses_shared_memory
+android.os.profiling.persist_queue
+android.os.profiling.redaction_enabled
+android.permission.flags.allow_host_permission_dialogs_on_virtual_devices
+android.permission.flags.device_aware_permissions_enabled
+android.permission.flags.device_policy_management_role_split_create_managed_profile_enabled
+android.permission.flags.enable_aiai_proxied_text_classifiers
+android.permission.flags.enable_otp_in_text_classifiers
+android.permission.flags.enable_sqlite_appops_accesses
+android.permission.flags.location_bypass_privacy_dashboard_enabled
+android.permission.flags.note_op_batching_enabled
+android.permission.flags.permission_request_short_circuit_enabled
+android.permission.flags.rate_limit_batched_note_op_async_callbacks_enabled
+android.permission.flags.sensitive_notification_app_protection
+android.permission.flags.supervision_role_permission_update_enabled
+android.permission.flags.unknown_call_package_install_blocking_enabled
+android.permission.flags.updatable_text_classifier_for_otp_detection_enabled
+android.permission.flags.use_profile_labels_for_default_app_section_titles
+android.permission.flags.wallet_role_cross_user_enabled
+android.provider.allow_config_maximum_call_log_entries_per_sim
+android.provider.backup_tasks_settings_screen
+android.provider.flags.new_storage_writer_system_api
+android.service.autofill.fill_dialog_improvements_impl
+android.service.chooser.fix_resolver_memory_leak
+android.service.notification.redact_sensitive_notifications_big_text_style
+android.service.notification.redact_sensitive_notifications_from_untrusted_listeners
+android.view.accessibility.motion_event_observing
+android.view.flags.expected_presentation_time_api
+android.view.flags.toolkit_frame_rate_touch_boost_25q1
+android.view.inputmethod.concurrent_input_methods
+android.view.inputmethod.ime_switcher_revamp
+android.view.inputmethod.imm_userhandle_hostsidetests
+android.webkit.mainline_apis
+android.widget.flags.use_wear_material3_ui
+com.android.aconfig.test.disabled_rw_exported
+com.android.aconfig.test.enabled_fixed_ro_exported
+com.android.aconfig.test.enabled_ro_exported
+com.android.aconfig.test.exported.exported_flag
+com.android.aconfig.test.forcereadonly.fro_exported
+com.android.adservices.ondevicepersonalization.flags.on_device_personalization_apis_enabled
+com.android.appsearch.flags.app_open_event_indexer_enabled
+com.android.appsearch.flags.apps_indexer_enabled
+com.android.appsearch.flags.enable_app_functions_schema_parser
+com.android.appsearch.flags.enable_apps_indexer_incremental_put
+com.android.appsearch.flags.enable_contacts_index_first_middle_and_last_names
+com.android.appsearch.flags.enable_document_limiter_replace_tracking
+com.android.appsearch.flags.enable_enterprise_empty_batch_result_fix
+com.android.bluetooth.flags.allow_switching_hid_and_hogp
+com.android.bluetooth.flags.bt_offload_socket_api
+com.android.bluetooth.flags.channel_sounding
+com.android.bluetooth.flags.fix_started_module_race
+com.android.bluetooth.flags.le_subrate_api
+com.android.bluetooth.flags.leaudio_broadcast_monitor_source_sync_status
+com.android.bluetooth.flags.leaudio_broadcast_volume_control_for_connected_devices
+com.android.bluetooth.flags.leaudio_multiple_vocs_instances_api
+com.android.bluetooth.flags.metadata_api_inactive_audio_device_upon_connection
+com.android.bluetooth.flags.settings_can_control_hap_preset
+com.android.bluetooth.flags.unix_file_socket_creation_failure
+com.android.graphics.flags.icon_load_drawable_return_null_when_uri_decode_fails
+com.android.graphics.hwui.flags.animated_image_drawable_filter_bitmap
+com.android.hardware.input.manage_key_gestures
+com.android.healthfitness.flags.activity_intensity_db
+com.android.healthfitness.flags.add_missing_access_logs
+com.android.healthfitness.flags.architecture_improvement
+com.android.healthfitness.flags.cloud_backup_and_restore
+com.android.healthfitness.flags.cycle_phases
+com.android.healthfitness.flags.d2d_file_deletion_bug_fix
+com.android.healthfitness.flags.dependency_injection
+com.android.healthfitness.flags.development_database
+com.android.healthfitness.flags.ecosystem_metrics
+com.android.healthfitness.flags.ecosystem_metrics_db_changes
+com.android.healthfitness.flags.export_import
+com.android.healthfitness.flags.export_import_fast_follow
+com.android.healthfitness.flags.export_import_nice_to_have
+com.android.healthfitness.flags.expressive_theming_enabled
+com.android.healthfitness.flags.health_connect_mappings
+com.android.healthfitness.flags.immediate_export
+com.android.healthfitness.flags.logcat_censor_iae
+com.android.healthfitness.flags.new_information_architecture
+com.android.healthfitness.flags.onboarding
+com.android.healthfitness.flags.permission_metrics
+com.android.healthfitness.flags.permission_tracker_fix_mapping_init
+com.android.healthfitness.flags.personal_health_record_database
+com.android.healthfitness.flags.personal_health_record_disable_d2d
+com.android.healthfitness.flags.personal_health_record_disable_export_import
+com.android.healthfitness.flags.personal_health_record_enable_d2d_and_export_import
+com.android.healthfitness.flags.personal_health_record_entries_screen
+com.android.healthfitness.flags.personal_health_record_lock_screen_banner
+com.android.healthfitness.flags.personal_health_record_telemetry
+com.android.healthfitness.flags.personal_health_record_telemetry_private_ww
+com.android.healthfitness.flags.personal_health_record_ui_telemetry
+com.android.healthfitness.flags.phr_fhir_basic_complex_type_validation
+com.android.healthfitness.flags.phr_fhir_complex_type_validation
+com.android.healthfitness.flags.phr_fhir_oneof_validation
+com.android.healthfitness.flags.phr_fhir_primitive_type_validation
+com.android.healthfitness.flags.phr_fhir_structural_validation
+com.android.healthfitness.flags.phr_read_medical_resources_fix_query_limit
+com.android.healthfitness.flags.phr_upsert_fix_parcel_size_calculation
+com.android.healthfitness.flags.phr_upsert_fix_use_shared_memory
+com.android.icu.icu_v_api
+com.android.internal.telephony.flags.async_init_carrier_privileges_tracker
+com.android.internal.telephony.flags.cleanup_carrier_app_update_enabled_state_logic
+com.android.internal.telephony.flags.oem_enabled_satellite_phase_2
+com.android.internal.telephony.flags.remap_disconnect_cause_sip_request_cancelled
+com.android.libcore.hpke_v_apis
+com.android.libcore.read_only_dynamic_code_load
+com.android.libcore.v_apis
+com.android.media.audio.hardening_impl
+com.android.media.audio.hardening_strict
+com.android.media.extractor.flags.extractor_mp4_enable_apv
+com.android.media.extractor.flags.extractor_sniff_midi_optimizations
+com.android.media.flags.enable_cross_user_routing_in_media_router2
+com.android.media.flags.enable_notifying_activity_manager_with_media_session_status_change
+com.android.media.metrics.flags.mediametrics_to_module
+com.android.media.projection.flags.media_projection_connected_display
+com.android.media.projection.flags.media_projection_connected_display_no_virtual_device
+com.android.net.ct.flags.certificate_transparency_job
+com.android.net.ct.flags.certificate_transparency_service
+com.android.net.flags.restrict_local_network
+com.android.net.flags.tethering_active_sessions_metrics
+com.android.net.thread.flags.thread_mobile_enabled
+com.android.nfc.module.flags.nfc_hce_latency_events
+com.android.org.conscrypt.flags.certificate_transparency_checkservertrusted_api
+com.android.permission.flags.add_banners_to_privacy_sensitive_apps_for_aaos
+com.android.permission.flags.app_permission_fragment_uses_preferences
+com.android.permission.flags.archiving_read_only
+com.android.permission.flags.decluttered_permission_manager_enabled
+com.android.permission.flags.enable_coarse_fine_location_prompt_for_aaos
+com.android.permission.flags.enhanced_confirmation_backport_enabled
+com.android.permission.flags.expressive_design_enabled
+com.android.permission.flags.livedata_refactor_permission_timeline_enabled
+com.android.permission.flags.odad_notifications_supported
+com.android.permission.flags.permission_timeline_attribution_label_fix
+com.android.permission.flags.private_profile_supported
+com.android.permission.flags.safety_center_enabled_no_device_config
+com.android.permission.flags.safety_center_issue_only_affects_group_status
+com.android.permission.flags.wear_compose_material3
+com.android.permission.flags.wear_privacy_dashboard_enabled_read_only
+com.android.providers.contactkeys.flags.contactkeys_strip_fix
+com.android.providers.media.flags.enable_backup_and_restore
+com.android.providers.media.flags.enable_malicious_app_detector
+com.android.providers.media.flags.enable_mark_media_as_favorite_api
+com.android.providers.media.flags.enable_modern_photopicker
+com.android.providers.media.flags.enable_photopicker_search
+com.android.providers.media.flags.enable_photopicker_transcoding
+com.android.providers.media.flags.enable_stable_uris_for_external_primary_volume
+com.android.providers.media.flags.enable_stable_uris_for_public_volume
+com.android.providers.media.flags.enable_unicode_check
+com.android.providers.media.flags.index_media_latitude_longitude
+com.android.providers.media.flags.version_lockdown
+com.android.ranging.flags.ranging_stack_updates_25q4
+com.android.server.backup.enable_read_all_external_storage_files
+com.android.server.telecom.flags.allow_system_apps_resolve_voip_calls
+com.android.server.telecom.flags.telecom_app_label_proxy_hsum_aware
+com.android.server.telecom.flags.telecom_main_user_in_block_check
+com.android.server.telecom.flags.telecom_main_user_in_get_respond_message_app
+com.android.server.updates.certificate_transparency_installer
+com.android.system.virtualmachine.flags.terminal_gui_support
+com.android.tradeinmode.flags.enable_trade_in_mode
+com.android.update_engine.minor_changes_2025q4
+com.android.uwb.flags.uwb_fira_3_0_25q4
+com.android.wifi.flags.network_provider_battery_charging_status
+com.android.wifi.flags.p2p_dialog2
+com.android.wifi.flags.shared_connectivity_broadcast_receiver_test_api
+com.android.wifi.flags.wep_disabled_in_apm
+com.android.window.flags.untrusted_embedding_state_sharing
+vendor.vibrator.hal.flags.enable_pwle_v2
+vendor.vibrator.hal.flags.remove_capo
+
+android.app.supervision.flags.enable_app_approval
+android.app.supervision.flags.enable_supervision_app_service
+android.app.supervision.flags.enable_supervision_pin_recovery_screen
+android.app.supervision.flags.enable_supervision_settings_screen
+android.app.supervision.flags.enable_web_content_filters_screen
+android.car.feature.display_compatibility_caption_bar
+android.companion.virtualdevice.flags.viewconfiguration_apis
+android.content.pm.always_load_past_certs_v4
+android.content.res.always_false
+android.content.res.use_new_aconfig_storage
+android.credentials.flags.propagate_user_context_for_intent_creation
+android.database.sqlite.concurrent_open_helper
+android.hardware.devicestate.feature.flags.device_state_configuration_flag
+android.media.audio.ringtone_user_uri_check
+android.media.soundtrigger.detection_service_paused_resumed_api
+android.media.tv.flags.tif_extension_standardization
+android.os.allow_thermal_hal_skin_forecast
+android.os.force_concurrent_message_queue
+android.permission.flags.enable_all_sqlite_appops_accesses
+android.permission.flags.grant_read_blocked_numbers_to_system_ui_intelligence
+android.permission.flags.record_all_runtime_appops_sqlite
+android.permission.flags.unknown_call_setting_blocked_logging_enabled
+android.server.wear_gesture_api
+android.view.accessibility.a11y_is_visited_api
+android.view.accessibility.request_rectangle_with_source
+android.view.contentcapture.flags.flush_after_each_frame
+com.android.adservices.flags.ad_id_cache_enabled
+com.android.adservices.flags.adservices_enablement_check_enabled
+com.android.adservices.flags.adservices_outcomereceiver_r_api_enabled
+com.android.adservices.flags.enable_adservices_api_enabled
+com.android.adservices.flags.sdksandbox_invalidate_effective_target_sdk_version_cache
+com.android.adservices.flags.sdksandbox_use_effective_target_sdk_version_for_restrictions
+com.android.appsearch.flags.enable_all_package_indexing_on_indexer_update
+com.android.appsearch.flags.enable_app_functions
+com.android.appsearch.flags.enable_app_open_events_indexer_check_prior_attempt
+com.android.appsearch.flags.enable_app_search_manage_blob_files
+com.android.appsearch.flags.enable_apps_indexer_check_prior_attempt
+com.android.appsearch.flags.enable_batch_put
+com.android.appsearch.flags.enable_calculate_time_since_last_attempted_optimize
+com.android.appsearch.flags.enable_check_contacts_indexer_delta_timestamps
+com.android.appsearch.flags.enable_check_contacts_indexer_update_job_params
+com.android.appsearch.flags.enable_four_hour_min_time_optimize_threshold
+com.android.appsearch.flags.enable_isolated_storage
+com.android.appsearch.flags.enable_marker_file_for_optimize
+com.android.appsearch.flags.enable_qualified_id_join_index_v3
+com.android.appsearch.flags.enable_recovery_proof_persistence
+com.android.appsearch.flags.enable_release_backup_schema_file_if_overlay_present
+com.android.appsearch.flags.enable_soft_index_restoration
+com.android.clockwork.flags.support_paired_device_none
+com.android.gms.flags.enable_deleted_gms
+com.android.gms.flags.enable_new_gms
+com.android.gms.flags.enable_optional_gms
+com.android.hardware.input.key_event_activity_detection
+com.android.healthfitness.flags.cloud_backup_and_restore_db
+com.android.healthfitness.flags.exercise_segment_weight
+com.android.healthfitness.flags.exercise_segment_weight_db
+com.android.healthfitness.flags.extend_export_import_telemetry
+com.android.healthfitness.flags.launch_onboarding_activity
+com.android.healthfitness.flags.personal_health_record_enable_export_import
+com.android.healthfitness.flags.phr_change_logs
+com.android.healthfitness.flags.phr_change_logs_db
+com.android.healthfitness.flags.phr_fhir_extension_validation
+com.android.healthfitness.flags.phr_fhir_resource_validator_use_weak_reference
+com.android.healthfitness.flags.phr_fhir_validation_disallow_empty_objects_arrays
+com.android.healthfitness.flags.refactor_aggregations
+com.android.healthfitness.flags.single_user_permission_intent_tracker
+com.android.healthfitness.flags.smoking
+com.android.healthfitness.flags.smoking_db
+com.android.healthfitness.flags.step_tracking_enabled
+com.android.healthfitness.flags.symptoms
+com.android.healthfitness.flags.symptoms_db
+com.android.icu.telephony_lookup_mcc_extension
+com.android.internal.telephony.flags.pass_copied_call_state_list
+com.android.internal.telephony.flags.robust_number_verification
+com.android.internal.telephony.flags.satellite_exit_p2p_session_outside_geofence
+com.android.internal.telephony.flags.starlink_data_bugfix
+com.android.media.audio.hardening_partial
+com.android.media.flags.enable_suggested_device_api
+com.android.media.flags.enable_use_of_singleton_audio_manager_route_controller
+com.android.media.projection.flags.app_content_sharing
+com.android.media.projection.flags.show_stop_dialog_post_call_end
+com.android.permission.flags.cross_user_role_ux_bugfix_enabled
+com.android.permission.flags.default_apps_recommendation_enabled
+com.android.permission.flags.fix_safety_center_touch_target
+com.android.providers.media.flags.enable_exclusion_list_for_default_folders
+com.android.providers.media.flags.enable_mime_type_fix_for_android_15
+com.android.providers.media.flags.exclude_unreliable_volumes
+com.android.providers.media.flags.revoke_access_owned_photos
+com.android.sdksandbox.flags.sandbox_activity_sdk_based_context
+com.android.sdksandbox.flags.selinux_input_selector
+com.android.sdksandbox.flags.selinux_sdk_sandbox_audit
+com.android.settings.flags.enable_remove_association_bt_unpair
+com.android.settingslib.widget.theme.flags.is_expressive_design_enabled
+com.android.window.flags.fix_hide_overlay_api
+com.android.window.flags.update_host_input_transfer_token
+com.fuchsia.bluetooth.flags.a2dp_lhdc_api
+com.fuchsia.bluetooth.flags.aics_api
+com.fuchsia.bluetooth.flags.allow_switching_hid_and_hogp
+com.fuchsia.bluetooth.flags.bt_offload_socket_api
+com.fuchsia.bluetooth.flags.bt_socket_api_l2cap_cid
+com.fuchsia.bluetooth.flags.channel_sounding
+com.fuchsia.bluetooth.flags.channel_sounding_25q2_apis
+com.fuchsia.bluetooth.flags.directed_advertising_api
+com.fuchsia.bluetooth.flags.encryption_change_broadcast
+com.fuchsia.bluetooth.flags.hci_vendor_specific_extension
+com.fuchsia.bluetooth.flags.identity_address_type_api
+com.fuchsia.bluetooth.flags.key_missing_public
+com.fuchsia.bluetooth.flags.leaudio_add_opus_codec_type
+com.fuchsia.bluetooth.flags.leaudio_broadcast_api_get_local_metadata
+com.fuchsia.bluetooth.flags.leaudio_broadcast_api_manage_primary_group
+com.fuchsia.bluetooth.flags.leaudio_broadcast_monitor_source_sync_status
+com.fuchsia.bluetooth.flags.leaudio_broadcast_volume_control_for_connected_devices
+com.fuchsia.bluetooth.flags.leaudio_mono_location_errata_api
+com.fuchsia.bluetooth.flags.leaudio_multiple_vocs_instances_api
+com.fuchsia.bluetooth.flags.metadata_api_inactive_audio_device_upon_connection
+com.fuchsia.bluetooth.flags.metadata_api_microphone_for_call_enabled
+com.fuchsia.bluetooth.flags.settings_can_control_hap_preset
+com.fuchsia.bluetooth.flags.socket_settings_api
+com.fuchsia.bluetooth.flags.support_bluetooth_quality_report_v6
+com.fuchsia.bluetooth.flags.support_exclusive_manager
+com.fuchsia.bluetooth.flags.support_metadata_device_types_apis
+com.fuchsia.bluetooth.flags.support_remote_device_metadata
+com.fuchsia.bluetooth.flags.unix_file_socket_creation_failure
+com.google.android.clockwork.pele.flags.koru_feature_cached_views
+com.google.android.clockwork.pele.flags.koru_origami
+com.google.android.device.pixel.watch.flags.pdms_flag_1
+com.google.android.haptics.flags.vendor_vibration_control
+com.google.clockwork.flags.prevent_ime_startup
+vendor.gc2.flags.mse_report
+vendor.google.plat_security.flags.enable_service
+vendor.google.plat_security.flags.enable_trusty_service
+vendor.google.wireless_charger.service.flags.enable_service
\ No newline at end of file
diff --git a/tools/aconfig/exported_flag_check/allow_package_list.txt b/tools/aconfig/exported_flag_check/allow_package_list.txt
new file mode 100644
index 0000000..e76472b
--- /dev/null
+++ b/tools/aconfig/exported_flag_check/allow_package_list.txt
@@ -0,0 +1,2 @@
+com.google.wear.sdk
+com.google.wear.services.infra.flags
diff --git a/tools/aconfig/exported_flag_check/src/main.rs b/tools/aconfig/exported_flag_check/src/main.rs
new file mode 100644
index 0000000..866a700
--- /dev/null
+++ b/tools/aconfig/exported_flag_check/src/main.rs
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+//! `exported-flag-check` is a tool to ensures that exported flags are used as intended
+use anyhow::{ensure, Result};
+use clap::Parser;
+use std::{collections::HashSet, fs::File, path::PathBuf};
+
+mod utils;
+
+use utils::{
+    check_all_exported_flags, extract_flagged_api_flags, get_exported_flags_from_binary_proto,
+    read_finalized_flags,
+};
+
+const ABOUT: &str = "CCheck Exported Flags
+
+This tool ensures that exported flags are used as intended. Exported flags, marked with
+`is_exported: true` in their declaration, are designed to control access to specific API
+features. This tool identifies and reports any exported flags that are not currently
+associated with an API feature, preventing unnecessary flag proliferation and maintaining
+a clear API design.
+
+This tool works as follows:
+
+  - Read API signature files from source tree (*current.txt files) [--api-signature-file]
+  - Read the current aconfig flag values from source tree [--parsed-flags-file]
+  - Read the previous finalized-flags.txt files from prebuilts/sdk [--finalized-flags-file]
+  - Extract the flags slated for API by scanning through the API signature files
+  - Merge the found flags with the recorded flags from previous API finalizations
+  - Error if exported flags are not in the set
+";
+
+#[derive(Parser, Debug)]
+#[clap(about=ABOUT)]
+struct Cli {
+    #[arg(long)]
+    parsed_flags_file: PathBuf,
+
+    #[arg(long)]
+    api_signature_file: Vec<PathBuf>,
+
+    #[arg(long)]
+    finalized_flags_file: PathBuf,
+}
+
+fn main() -> Result<()> {
+    let args = Cli::parse();
+
+    let mut flags_used_with_flaggedapi_annotation = HashSet::new();
+    for path in &args.api_signature_file {
+        let file = File::open(path)?;
+        let flags = extract_flagged_api_flags(file)?;
+        flags_used_with_flaggedapi_annotation.extend(flags);
+    }
+
+    let file = File::open(args.parsed_flags_file)?;
+    let all_flags = get_exported_flags_from_binary_proto(file)?;
+
+    let file = File::open(args.finalized_flags_file)?;
+    let already_finalized_flags = read_finalized_flags(file)?;
+
+    let exported_flags = check_all_exported_flags(
+        &flags_used_with_flaggedapi_annotation,
+        &all_flags,
+        &already_finalized_flags,
+    )?;
+
+    println!("{}", exported_flags.join("\n"));
+
+    ensure!(
+        exported_flags.is_empty(),
+        "Flags {} are exported but not used to guard any API. \
+    Exported flag should be used to guard API",
+        exported_flags.join(",")
+    );
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test() {
+        let input = include_bytes!("../tests/api-signature-file.txt");
+        let flags_used_with_flaggedapi_annotation = extract_flagged_api_flags(&input[..]).unwrap();
+
+        let input = include_bytes!("../tests/flags.protobuf");
+        let all_flags_to_be_finalized = get_exported_flags_from_binary_proto(&input[..]).unwrap();
+
+        let input = include_bytes!("../tests/finalized-flags.txt");
+        let already_finalized_flags = read_finalized_flags(&input[..]).unwrap();
+
+        let exported_flags = check_all_exported_flags(
+            &flags_used_with_flaggedapi_annotation,
+            &all_flags_to_be_finalized,
+            &already_finalized_flags,
+        )
+        .unwrap();
+
+        assert_eq!(1, exported_flags.len());
+    }
+}
diff --git a/tools/aconfig/exported_flag_check/src/utils.rs b/tools/aconfig/exported_flag_check/src/utils.rs
new file mode 100644
index 0000000..3686fec
--- /dev/null
+++ b/tools/aconfig/exported_flag_check/src/utils.rs
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+use aconfig_protos::ParsedFlagExt;
+use anyhow::{anyhow, Context, Result};
+use regex::Regex;
+use std::{
+    collections::HashSet,
+    io::{BufRead, BufReader, Read},
+};
+
+pub(crate) type FlagId = String;
+
+/// Grep for all flags used with @FlaggedApi annotations in an API signature file (*current.txt
+/// file).
+pub(crate) fn extract_flagged_api_flags<R: Read>(mut reader: R) -> Result<HashSet<FlagId>> {
+    let mut haystack = String::new();
+    reader.read_to_string(&mut haystack)?;
+    let regex = Regex::new(r#"(?ms)@FlaggedApi\("(.*?)"\)"#).unwrap();
+    let iter = regex.captures_iter(&haystack).map(|cap| cap[1].to_owned());
+    Ok(HashSet::from_iter(iter))
+}
+
+/// Read a list of flag names. The input is expected to be plain text, with each line containing
+/// the name of a single flag.
+pub(crate) fn read_finalized_flags<R: Read>(reader: R) -> Result<HashSet<FlagId>> {
+    BufReader::new(reader)
+        .lines()
+        .map(|line_result| line_result.context("Failed to read line from finalized flags file"))
+        .collect()
+}
+
+/// Parse a ProtoParsedFlags binary protobuf blob and return the fully qualified names of flags
+/// have is_exported as true.
+pub(crate) fn get_exported_flags_from_binary_proto<R: Read>(
+    mut reader: R,
+) -> Result<HashSet<FlagId>> {
+    let mut buffer = Vec::new();
+    reader.read_to_end(&mut buffer)?;
+    let parsed_flags = aconfig_protos::parsed_flags::try_from_binary_proto(&buffer)
+        .map_err(|_| anyhow!("failed to parse binary proto"))?;
+    let iter = parsed_flags
+        .parsed_flag
+        .into_iter()
+        .filter(|flag| flag.is_exported())
+        .map(|flag| flag.fully_qualified_name());
+    Ok(HashSet::from_iter(iter))
+}
+
+fn get_allow_flag_list() -> Result<HashSet<FlagId>> {
+    let allow_list: HashSet<FlagId> =
+        include_str!("../allow_flag_list.txt").lines().map(|x| x.into()).collect();
+    Ok(allow_list)
+}
+
+fn get_allow_package_list() -> Result<HashSet<FlagId>> {
+    let allow_list: HashSet<FlagId> =
+        include_str!("../allow_package_list.txt").lines().map(|x| x.into()).collect();
+    Ok(allow_list)
+}
+
+/// Filter out the flags have is_exported as true but not used with @FlaggedApi annotations
+/// in the source tree, or in the previously finalized flags set.
+pub(crate) fn check_all_exported_flags(
+    flags_used_with_flaggedapi_annotation: &HashSet<FlagId>,
+    all_flags: &HashSet<FlagId>,
+    already_finalized_flags: &HashSet<FlagId>,
+) -> Result<Vec<FlagId>> {
+    let allow_flag_list = get_allow_flag_list()?;
+    let allow_package_list = get_allow_package_list()?;
+
+    let new_flags: Vec<FlagId> = all_flags
+        .difference(flags_used_with_flaggedapi_annotation)
+        .cloned()
+        .collect::<HashSet<_>>()
+        .difference(already_finalized_flags)
+        .cloned()
+        .collect::<HashSet<_>>()
+        .difference(&allow_flag_list)
+        .filter(|flag| {
+            if let Some(last_dot_index) = flag.rfind('.') {
+                let package_name = &flag[..last_dot_index];
+                !allow_package_list.contains(package_name)
+            } else {
+                true
+            }
+        })
+        .cloned()
+        .collect();
+
+    Ok(new_flags)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_extract_flagged_api_flags() {
+        let api_signature_file = include_bytes!("../tests/api-signature-file.txt");
+        let flags = extract_flagged_api_flags(&api_signature_file[..]).unwrap();
+        assert_eq!(
+            flags,
+            HashSet::from_iter(vec![
+                "record_finalized_flags.test.foo".to_string(),
+                "this.flag.is.not.used".to_string(),
+            ])
+        );
+    }
+
+    #[test]
+    fn test_read_finalized_flags() {
+        let input = include_bytes!("../tests/finalized-flags.txt");
+        let flags = read_finalized_flags(&input[..]).unwrap();
+        assert_eq!(
+            flags,
+            HashSet::from_iter(vec![
+                "record_finalized_flags.test.bar".to_string(),
+                "record_finalized_flags.test.baz".to_string(),
+            ])
+        );
+    }
+
+    #[test]
+    fn test_disabled_or_read_write_flags_are_ignored() {
+        let bytes = include_bytes!("../tests/flags.protobuf");
+        let flags = get_exported_flags_from_binary_proto(&bytes[..]).unwrap();
+        assert_eq!(
+            flags,
+            HashSet::from_iter(vec![
+                "record_finalized_flags.test.foo".to_string(),
+                "record_finalized_flags.test.not_enabled".to_string()
+            ])
+        );
+    }
+}
diff --git a/tools/aconfig/exported_flag_check/tests/api-signature-file.txt b/tools/aconfig/exported_flag_check/tests/api-signature-file.txt
new file mode 100644
index 0000000..2ad559f
--- /dev/null
+++ b/tools/aconfig/exported_flag_check/tests/api-signature-file.txt
@@ -0,0 +1,15 @@
+// Signature format: 2.0
+package android {
+
+  public final class C {
+    ctor public C();
+  }
+
+  public static final class C.inner {
+    ctor public C.inner();
+    field @FlaggedApi("record_finalized_flags.test.foo") public static final String FOO = "foo";
+    field @FlaggedApi("this.flag.is.not.used") public static final String BAR = "bar";
+  }
+
+}
+
diff --git a/tools/aconfig/exported_flag_check/tests/finalized-flags.txt b/tools/aconfig/exported_flag_check/tests/finalized-flags.txt
new file mode 100644
index 0000000..7fbcb3d
--- /dev/null
+++ b/tools/aconfig/exported_flag_check/tests/finalized-flags.txt
@@ -0,0 +1,2 @@
+record_finalized_flags.test.bar
+record_finalized_flags.test.baz
diff --git a/tools/aconfig/exported_flag_check/tests/flags.declarations b/tools/aconfig/exported_flag_check/tests/flags.declarations
new file mode 100644
index 0000000..f86dbfa
--- /dev/null
+++ b/tools/aconfig/exported_flag_check/tests/flags.declarations
@@ -0,0 +1,18 @@
+package: "record_finalized_flags.test"
+container: "system"
+
+flag {
+    name: "foo"
+    namespace: "test"
+    description: "FIXME"
+    bug: ""
+    is_exported:true
+}
+
+flag {
+    name: "not_enabled"
+    namespace: "test"
+    description: "FIXME"
+    bug: ""
+    is_exported:true
+}
diff --git a/tools/aconfig/exported_flag_check/tests/flags.protobuf b/tools/aconfig/exported_flag_check/tests/flags.protobuf
new file mode 100644
index 0000000..be64ef9
--- /dev/null
+++ b/tools/aconfig/exported_flag_check/tests/flags.protobuf
Binary files differ
diff --git a/tools/aconfig/exported_flag_check/tests/flags.values b/tools/aconfig/exported_flag_check/tests/flags.values
new file mode 100644
index 0000000..ff6225d
--- /dev/null
+++ b/tools/aconfig/exported_flag_check/tests/flags.values
@@ -0,0 +1,13 @@
+flag_value {
+    package: "record_finalized_flags.test"
+    name: "foo"
+    state: ENABLED
+    permission: READ_ONLY
+}
+
+flag_value {
+    package: "record_finalized_flags.test"
+    name: "not_enabled"
+    state: DISABLED
+    permission: READ_ONLY
+}
diff --git a/tools/aconfig/exported_flag_check/tests/generate-flags-protobuf.sh b/tools/aconfig/exported_flag_check/tests/generate-flags-protobuf.sh
new file mode 100755
index 0000000..701189c
--- /dev/null
+++ b/tools/aconfig/exported_flag_check/tests/generate-flags-protobuf.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+aconfig create-cache \
+    --package record_finalized_flags.test \
+    --container system \
+    --declarations flags.declarations \
+    --values flags.values \
+    --cache flags.protobuf
diff --git a/tools/aconfig/fake_device_config/src/android/os/Build.java b/tools/aconfig/fake_device_config/src/android/os/Build.java
index 8ec72fb..790ff82 100644
--- a/tools/aconfig/fake_device_config/src/android/os/Build.java
+++ b/tools/aconfig/fake_device_config/src/android/os/Build.java
@@ -18,6 +18,9 @@
 
 public class Build {
     public static class VERSION {
-        public static final int SDK_INT = 0;
+        public static final int SDK_INT = placeholder();
+        private static int placeholder() {
+            throw new UnsupportedOperationException("Stub!");
+        }
     }
 }
diff --git a/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java b/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java
index ec79f7d..c06a532 100644
--- a/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java
+++ b/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java
@@ -24,7 +24,11 @@
 public class PlatformAconfigPackage {
 
     public static final Set<String> PLATFORM_PACKAGE_MAP_FILES =
-            Set.of("system.package.map", "vendor.package.map", "product.package.map");
+            Set.of(
+                    "system.package.map",
+                    "system_ext.package.map",
+                    "vendor.package.map",
+                    "product.package.map");
 
     public static PlatformAconfigPackage load(String packageName) {
         throw new UnsupportedOperationException("Stub!");
diff --git a/tools/aconfig/printflags/Android.bp b/tools/aconfig/printflags/Android.bp
deleted file mode 100644
index 6f7bca3..0000000
--- a/tools/aconfig/printflags/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_defaults {
-    name: "printflags.defaults",
-    edition: "2021",
-    clippy_lints: "android",
-    lints: "android",
-    srcs: ["src/main.rs"],
-    rustlibs: [
-        "libaconfig_protos",
-        "libanyhow",
-        "libprotobuf",
-        "libregex",
-    ],
-}
-
-rust_binary {
-    name: "printflags",
-    defaults: ["printflags.defaults"],
-    apex_available: [
-        "//apex_available:platform",
-    ],
-}
-
-rust_test_host {
-    name: "printflags.test",
-    defaults: ["printflags.defaults"],
-    test_suites: ["general-tests"],
-}
diff --git a/tools/aconfig/printflags/src/main.rs b/tools/aconfig/printflags/src/main.rs
deleted file mode 100644
index 7838b51..0000000
--- a/tools/aconfig/printflags/src/main.rs
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//! `printflags` is a device binary to print feature flags.
-
-use aconfig_protos::ProtoFlagState as State;
-use aconfig_protos::ProtoParsedFlags;
-use anyhow::{bail, Context, Result};
-use regex::Regex;
-use std::collections::BTreeMap;
-use std::collections::HashMap;
-use std::process::Command;
-use std::{fs, str};
-
-fn parse_device_config(raw: &str) -> HashMap<String, String> {
-    let mut flags = HashMap::new();
-    let regex = Regex::new(r"(?m)^([[[:alnum:]]_]+/[[[:alnum:]]_\.]+)=(true|false)$").unwrap();
-    for capture in regex.captures_iter(raw) {
-        let key = capture.get(1).unwrap().as_str().to_string();
-        let value = match capture.get(2).unwrap().as_str() {
-            "true" => format!("{:?} (device_config)", State::ENABLED),
-            "false" => format!("{:?} (device_config)", State::DISABLED),
-            _ => panic!(),
-        };
-        flags.insert(key, value);
-    }
-    flags
-}
-
-fn xxd(bytes: &[u8]) -> String {
-    let n = 8.min(bytes.len());
-    let mut v = Vec::with_capacity(n);
-    for byte in bytes.iter().take(n) {
-        v.push(format!("{:02x}", byte));
-    }
-    let trailer = match bytes.len() {
-        0..=8 => "",
-        _ => " ..",
-    };
-    format!("[{}{}]", v.join(" "), trailer)
-}
-
-fn main() -> Result<()> {
-    // read device_config
-    let output = Command::new("/system/bin/device_config").arg("list").output()?;
-    if !output.status.success() {
-        let reason = match output.status.code() {
-            Some(code) => format!("exit code {}", code),
-            None => "terminated by signal".to_string(),
-        };
-        bail!("failed to execute device_config: {}", reason);
-    }
-    let dc_stdout = str::from_utf8(&output.stdout)?;
-    let device_config_flags = parse_device_config(dc_stdout);
-
-    // read aconfig_flags.pb files
-    let apex_pattern = Regex::new(r"^/apex/[^@]+\.[^@]+$").unwrap();
-    let mut mount_points = vec![
-        "system".to_string(),
-        "system_ext".to_string(),
-        "product".to_string(),
-        "vendor".to_string(),
-    ];
-    for apex in fs::read_dir("/apex")? {
-        let path_name = apex?.path().display().to_string();
-        if let Some(canonical_path) = apex_pattern.captures(&path_name) {
-            mount_points.push(canonical_path.get(0).unwrap().as_str().to_owned());
-        }
-    }
-
-    let mut flags: BTreeMap<String, Vec<String>> = BTreeMap::new();
-    for mount_point in mount_points {
-        let path = format!("/{}/etc/aconfig_flags.pb", mount_point);
-        let Ok(bytes) = fs::read(&path) else {
-            eprintln!("warning: failed to read {}", path);
-            continue;
-        };
-        let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)
-            .with_context(|| {
-                format!("failed to parse {} ({}, {} byte(s))", path, xxd(&bytes), bytes.len())
-            })?;
-        for flag in parsed_flags.parsed_flag {
-            let key = format!("{}/{}.{}", flag.namespace(), flag.package(), flag.name());
-            let value = format!("{:?} + {:?} ({})", flag.permission(), flag.state(), mount_point);
-            flags.entry(key).or_default().push(value);
-        }
-    }
-
-    // print flags
-    for (key, mut value) in flags {
-        if let Some(dc_value) = device_config_flags.get(&key) {
-            value.push(dc_value.to_string());
-        }
-        println!("{}: {}", key, value.join(", "));
-    }
-
-    Ok(())
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_parse_device_config() {
-        let input = r#"
-namespace_one/com.foo.bar.flag_one=true
-namespace_one/com.foo.bar.flag_two=false
-random_noise;
-namespace_two/android.flag_one=true
-namespace_two/android.flag_two=nonsense
-"#;
-        let expected = HashMap::from([
-            (
-                "namespace_one/com.foo.bar.flag_one".to_string(),
-                "ENABLED (device_config)".to_string(),
-            ),
-            (
-                "namespace_one/com.foo.bar.flag_two".to_string(),
-                "DISABLED (device_config)".to_string(),
-            ),
-            ("namespace_two/android.flag_one".to_string(), "ENABLED (device_config)".to_string()),
-        ]);
-        let actual = parse_device_config(input);
-        assert_eq!(expected, actual);
-    }
-
-    #[test]
-    fn test_xxd() {
-        let input = [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9];
-        assert_eq!("[]", &xxd(&input[0..0]));
-        assert_eq!("[00]", &xxd(&input[0..1]));
-        assert_eq!("[00 01]", &xxd(&input[0..2]));
-        assert_eq!("[00 01 02 03 04 05 06]", &xxd(&input[0..7]));
-        assert_eq!("[00 01 02 03 04 05 06 07]", &xxd(&input[0..8]));
-        assert_eq!("[00 01 02 03 04 05 06 07 ..]", &xxd(&input[0..9]));
-        assert_eq!("[00 01 02 03 04 05 06 07 ..]", &xxd(&input));
-    }
-}
diff --git a/tools/compliance/Android.bp b/tools/compliance/Android.bp
index ef5c760..33f515b 100644
--- a/tools/compliance/Android.bp
+++ b/tools/compliance/Android.bp
@@ -39,16 +39,6 @@
 }
 
 blueprint_go_binary {
-    name: "compliancenotice_bom",
-    srcs: ["cmd/bom/bom.go"],
-    deps: [
-        "compliance-module",
-        "soong-response",
-    ],
-    testSrcs: ["cmd/bom/bom_test.go"],
-}
-
-blueprint_go_binary {
     name: "compliancenotice_shippedlibs",
     srcs: ["cmd/shippedlibs/shippedlibs.go"],
     deps: [
@@ -131,22 +121,6 @@
     testSrcs: ["cmd/xmlnotice/xmlnotice_test.go"],
 }
 
-blueprint_go_binary {
-    name: "compliance_sbom",
-    srcs: ["cmd/sbom/sbom.go"],
-    deps: [
-        "compliance-module",
-        "blueprint-deptools",
-        "soong-response",
-        "spdx-tools-spdxv2_2",
-        "spdx-tools-builder2v2",
-        "spdx-tools-spdxcommon",
-        "spdx-tools-spdx-json",
-        "spdx-tools-spdxlib",
-    ],
-    testSrcs: ["cmd/sbom/sbom_test.go"],
-}
-
 bootstrap_go_package {
     name: "compliance-module",
     srcs: [
diff --git a/tools/compliance/cmd/bom/bom.go b/tools/compliance/cmd/bom/bom.go
deleted file mode 100644
index 187f828..0000000
--- a/tools/compliance/cmd/bom/bom.go
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright 2021 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
-	"bytes"
-	"flag"
-	"fmt"
-	"io"
-	"io/fs"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"android/soong/response"
-	"android/soong/tools/compliance"
-)
-
-var (
-	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
-	failNoLicenses    = fmt.Errorf("No licenses found")
-)
-
-type context struct {
-	stdout      io.Writer
-	stderr      io.Writer
-	rootFS      fs.FS
-	stripPrefix []string
-}
-
-func (ctx context) strip(installPath string) string {
-	for _, prefix := range ctx.stripPrefix {
-		if strings.HasPrefix(installPath, prefix) {
-			p := strings.TrimPrefix(installPath, prefix)
-			if 0 == len(p) {
-				continue
-			}
-			return p
-		}
-	}
-	return installPath
-}
-
-// newMultiString creates a flag that allows multiple values in an array.
-func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
-	var f multiString
-	flags.Var(&f, name, usage)
-	return &f
-}
-
-// multiString implements the flag `Value` interface for multiple strings.
-type multiString []string
-
-func (ms *multiString) String() string     { return strings.Join(*ms, ", ") }
-func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
-
-func main() {
-	var expandedArgs []string
-	for _, arg := range os.Args[1:] {
-		if strings.HasPrefix(arg, "@") {
-			f, err := os.Open(strings.TrimPrefix(arg, "@"))
-			if err != nil {
-				fmt.Fprintln(os.Stderr, err.Error())
-				os.Exit(1)
-			}
-
-			respArgs, err := response.ReadRspFile(f)
-			f.Close()
-			if err != nil {
-				fmt.Fprintln(os.Stderr, err.Error())
-				os.Exit(1)
-			}
-			expandedArgs = append(expandedArgs, respArgs...)
-		} else {
-			expandedArgs = append(expandedArgs, arg)
-		}
-	}
-
-	flags := flag.NewFlagSet("flags", flag.ExitOnError)
-
-	flags.Usage = func() {
-		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
-
-Outputs a bill of materials. i.e. the list of installed paths.
-
-Options:
-`, filepath.Base(os.Args[0]))
-		flags.PrintDefaults()
-	}
-
-	outputFile := flags.String("o", "-", "Where to write the bill of materials. (default stdout)")
-	stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
-
-	flags.Parse(expandedArgs)
-
-	// Must specify at least one root target.
-	if flags.NArg() == 0 {
-		flags.Usage()
-		os.Exit(2)
-	}
-
-	if len(*outputFile) == 0 {
-		flags.Usage()
-		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
-		os.Exit(2)
-	} else {
-		dir, err := filepath.Abs(filepath.Dir(*outputFile))
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
-			os.Exit(1)
-		}
-		fi, err := os.Stat(dir)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
-			os.Exit(1)
-		}
-		if !fi.IsDir() {
-			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
-			os.Exit(1)
-		}
-	}
-
-	var ofile io.Writer
-	ofile = os.Stdout
-	if *outputFile != "-" {
-		ofile = &bytes.Buffer{}
-	}
-
-	ctx := &context{ofile, os.Stderr, compliance.FS, *stripPrefix}
-
-	err := billOfMaterials(ctx, flags.Args()...)
-	if err != nil {
-		if err == failNoneRequested {
-			flags.Usage()
-		}
-		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
-		os.Exit(1)
-	}
-	if *outputFile != "-" {
-		err := os.WriteFile(*outputFile, ofile.(*bytes.Buffer).Bytes(), 0666)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err)
-			os.Exit(1)
-		}
-	}
-	os.Exit(0)
-}
-
-// billOfMaterials implements the bom utility.
-func billOfMaterials(ctx *context, files ...string) error {
-	// Must be at least one root file.
-	if len(files) < 1 {
-		return failNoneRequested
-	}
-
-	// Read the license graph from the license metadata files (*.meta_lic).
-	licenseGraph, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)
-	if err != nil {
-		return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
-	}
-	if licenseGraph == nil {
-		return failNoLicenses
-	}
-
-	// rs contains all notice resolutions.
-	rs := compliance.ResolveNotices(licenseGraph)
-
-	ni, err := compliance.IndexLicenseTexts(ctx.rootFS, licenseGraph, rs)
-	if err != nil {
-		return fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err)
-	}
-
-	for path := range ni.InstallPaths() {
-		fmt.Fprintln(ctx.stdout, ctx.strip(path))
-	}
-	return nil
-}
diff --git a/tools/compliance/cmd/bom/bom_test.go b/tools/compliance/cmd/bom/bom_test.go
deleted file mode 100644
index 87a3b50..0000000
--- a/tools/compliance/cmd/bom/bom_test.go
+++ /dev/null
@@ -1,322 +0,0 @@
-// Copyright 2021 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
-	"bufio"
-	"bytes"
-	"fmt"
-	"os"
-	"strings"
-	"testing"
-
-	"android/soong/tools/compliance"
-)
-
-func TestMain(m *testing.M) {
-	// Change into the parent directory before running the tests
-	// so they can find the testdata directory.
-	if err := os.Chdir(".."); err != nil {
-		fmt.Printf("failed to change to testdata directory: %s\n", err)
-		os.Exit(1)
-	}
-	os.Exit(m.Run())
-}
-
-func Test(t *testing.T) {
-	tests := []struct {
-		condition   string
-		name        string
-		outDir      string
-		roots       []string
-		stripPrefix string
-		expectedOut []string
-	}{
-		{
-			condition:   "firstparty",
-			name:        "apex",
-			roots:       []string{"highest.apex.meta_lic"},
-			stripPrefix: "out/target/product/fictional",
-			expectedOut: []string{
-				"/system/apex/highest.apex",
-				"/system/apex/highest.apex/bin/bin1",
-				"/system/apex/highest.apex/bin/bin2",
-				"/system/apex/highest.apex/lib/liba.so",
-				"/system/apex/highest.apex/lib/libb.so",
-			},
-		},
-		{
-			condition:   "firstparty",
-			name:        "container",
-			roots:       []string{"container.zip.meta_lic"},
-			stripPrefix: "out/target/product/fictional/data/",
-			expectedOut: []string{
-				"container.zip",
-				"container.zip/bin1",
-				"container.zip/bin2",
-				"container.zip/liba.so",
-				"container.zip/libb.so",
-			},
-		},
-		{
-			condition:   "firstparty",
-			name:        "application",
-			roots:       []string{"application.meta_lic"},
-			stripPrefix: "out/target/product/fictional/bin/",
-			expectedOut: []string{"application"},
-		},
-		{
-			condition:   "firstparty",
-			name:        "binary",
-			roots:       []string{"bin/bin1.meta_lic"},
-			stripPrefix: "out/target/product/fictional/system/",
-			expectedOut: []string{"bin/bin1"},
-		},
-		{
-			condition:   "firstparty",
-			name:        "library",
-			roots:       []string{"lib/libd.so.meta_lic"},
-			stripPrefix: "out/target/product/fictional/system/",
-			expectedOut: []string{"lib/libd.so"},
-		},
-		{
-			condition: "notice",
-			name:      "apex",
-			roots:     []string{"highest.apex.meta_lic"},
-			expectedOut: []string{
-				"out/target/product/fictional/system/apex/highest.apex",
-				"out/target/product/fictional/system/apex/highest.apex/bin/bin1",
-				"out/target/product/fictional/system/apex/highest.apex/bin/bin2",
-				"out/target/product/fictional/system/apex/highest.apex/lib/liba.so",
-				"out/target/product/fictional/system/apex/highest.apex/lib/libb.so",
-			},
-		},
-		{
-			condition: "notice",
-			name:      "container",
-			roots:     []string{"container.zip.meta_lic"},
-			expectedOut: []string{
-				"out/target/product/fictional/data/container.zip",
-				"out/target/product/fictional/data/container.zip/bin1",
-				"out/target/product/fictional/data/container.zip/bin2",
-				"out/target/product/fictional/data/container.zip/liba.so",
-				"out/target/product/fictional/data/container.zip/libb.so",
-			},
-		},
-		{
-			condition:   "notice",
-			name:        "application",
-			roots:       []string{"application.meta_lic"},
-			expectedOut: []string{"out/target/product/fictional/bin/application"},
-		},
-		{
-			condition:   "notice",
-			name:        "binary",
-			roots:       []string{"bin/bin1.meta_lic"},
-			expectedOut: []string{"out/target/product/fictional/system/bin/bin1"},
-		},
-		{
-			condition:   "notice",
-			name:        "library",
-			roots:       []string{"lib/libd.so.meta_lic"},
-			expectedOut: []string{"out/target/product/fictional/system/lib/libd.so"},
-		},
-		{
-			condition:   "reciprocal",
-			name:        "apex",
-			roots:       []string{"highest.apex.meta_lic"},
-			stripPrefix: "out/target/product/fictional/system/apex/",
-			expectedOut: []string{
-				"highest.apex",
-				"highest.apex/bin/bin1",
-				"highest.apex/bin/bin2",
-				"highest.apex/lib/liba.so",
-				"highest.apex/lib/libb.so",
-			},
-		},
-		{
-			condition:   "reciprocal",
-			name:        "container",
-			roots:       []string{"container.zip.meta_lic"},
-			stripPrefix: "out/target/product/fictional/data/",
-			expectedOut: []string{
-				"container.zip",
-				"container.zip/bin1",
-				"container.zip/bin2",
-				"container.zip/liba.so",
-				"container.zip/libb.so",
-			},
-		},
-		{
-			condition:   "reciprocal",
-			name:        "application",
-			roots:       []string{"application.meta_lic"},
-			stripPrefix: "out/target/product/fictional/bin/",
-			expectedOut: []string{"application"},
-		},
-		{
-			condition:   "reciprocal",
-			name:        "binary",
-			roots:       []string{"bin/bin1.meta_lic"},
-			stripPrefix: "out/target/product/fictional/system/",
-			expectedOut: []string{"bin/bin1"},
-		},
-		{
-			condition:   "reciprocal",
-			name:        "library",
-			roots:       []string{"lib/libd.so.meta_lic"},
-			stripPrefix: "out/target/product/fictional/system/",
-			expectedOut: []string{"lib/libd.so"},
-		},
-		{
-			condition:   "restricted",
-			name:        "apex",
-			roots:       []string{"highest.apex.meta_lic"},
-			stripPrefix: "out/target/product/fictional/system/apex/",
-			expectedOut: []string{
-				"highest.apex",
-				"highest.apex/bin/bin1",
-				"highest.apex/bin/bin2",
-				"highest.apex/lib/liba.so",
-				"highest.apex/lib/libb.so",
-			},
-		},
-		{
-			condition:   "restricted",
-			name:        "container",
-			roots:       []string{"container.zip.meta_lic"},
-			stripPrefix: "out/target/product/fictional/data/",
-			expectedOut: []string{
-				"container.zip",
-				"container.zip/bin1",
-				"container.zip/bin2",
-				"container.zip/liba.so",
-				"container.zip/libb.so",
-			},
-		},
-		{
-			condition:   "restricted",
-			name:        "application",
-			roots:       []string{"application.meta_lic"},
-			stripPrefix: "out/target/product/fictional/bin/",
-			expectedOut: []string{"application"},
-		},
-		{
-			condition:   "restricted",
-			name:        "binary",
-			roots:       []string{"bin/bin1.meta_lic"},
-			stripPrefix: "out/target/product/fictional/system/",
-			expectedOut: []string{"bin/bin1"},
-		},
-		{
-			condition:   "restricted",
-			name:        "library",
-			roots:       []string{"lib/libd.so.meta_lic"},
-			stripPrefix: "out/target/product/fictional/system/",
-			expectedOut: []string{"lib/libd.so"},
-		},
-		{
-			condition:   "proprietary",
-			name:        "apex",
-			roots:       []string{"highest.apex.meta_lic"},
-			stripPrefix: "out/target/product/fictional/system/apex/",
-			expectedOut: []string{
-				"highest.apex",
-				"highest.apex/bin/bin1",
-				"highest.apex/bin/bin2",
-				"highest.apex/lib/liba.so",
-				"highest.apex/lib/libb.so",
-			},
-		},
-		{
-			condition:   "proprietary",
-			name:        "container",
-			roots:       []string{"container.zip.meta_lic"},
-			stripPrefix: "out/target/product/fictional/data/",
-			expectedOut: []string{
-				"container.zip",
-				"container.zip/bin1",
-				"container.zip/bin2",
-				"container.zip/liba.so",
-				"container.zip/libb.so",
-			},
-		},
-		{
-			condition:   "proprietary",
-			name:        "application",
-			roots:       []string{"application.meta_lic"},
-			stripPrefix: "out/target/product/fictional/bin/",
-			expectedOut: []string{"application"},
-		},
-		{
-			condition:   "proprietary",
-			name:        "binary",
-			roots:       []string{"bin/bin1.meta_lic"},
-			stripPrefix: "out/target/product/fictional/system/",
-			expectedOut: []string{"bin/bin1"},
-		},
-		{
-			condition:   "proprietary",
-			name:        "library",
-			roots:       []string{"lib/libd.so.meta_lic"},
-			stripPrefix: "out/target/product/fictional/system/",
-			expectedOut: []string{"lib/libd.so"},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
-			stdout := &bytes.Buffer{}
-			stderr := &bytes.Buffer{}
-
-			rootFiles := make([]string, 0, len(tt.roots))
-			for _, r := range tt.roots {
-				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
-			}
-
-			ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), []string{tt.stripPrefix}}
-
-			err := billOfMaterials(&ctx, rootFiles...)
-			if err != nil {
-				t.Fatalf("bom: error = %v, stderr = %v", err, stderr)
-				return
-			}
-			if stderr.Len() > 0 {
-				t.Errorf("bom: gotStderr = %v, want none", stderr)
-			}
-
-			t.Logf("got stdout: %s", stdout.String())
-
-			t.Logf("want stdout: %s", strings.Join(tt.expectedOut, "\n"))
-
-			out := bufio.NewScanner(stdout)
-			lineno := 0
-			for out.Scan() {
-				line := out.Text()
-				if strings.TrimLeft(line, " ") == "" {
-					continue
-				}
-				if len(tt.expectedOut) <= lineno {
-					t.Errorf("bom: unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
-				} else if tt.expectedOut[lineno] != line {
-					t.Errorf("bom: unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno])
-				}
-				lineno++
-			}
-			for ; lineno < len(tt.expectedOut); lineno++ {
-				t.Errorf("bom: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno])
-			}
-		})
-	}
-}
diff --git a/tools/compliance/cmd/sbom/sbom.go b/tools/compliance/cmd/sbom/sbom.go
deleted file mode 100644
index a53741f..0000000
--- a/tools/compliance/cmd/sbom/sbom.go
+++ /dev/null
@@ -1,547 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
-	"bytes"
-	"crypto/sha1"
-	"encoding/hex"
-	"flag"
-	"fmt"
-	"io"
-	"io/fs"
-	"os"
-	"path/filepath"
-	"sort"
-	"strings"
-	"time"
-
-	"android/soong/response"
-	"android/soong/tools/compliance"
-	"android/soong/tools/compliance/projectmetadata"
-
-	"github.com/google/blueprint/deptools"
-
-	"github.com/spdx/tools-golang/builder/builder2v2"
-	spdx_json "github.com/spdx/tools-golang/json"
-	"github.com/spdx/tools-golang/spdx/common"
-	spdx "github.com/spdx/tools-golang/spdx/v2_2"
-	"github.com/spdx/tools-golang/spdxlib"
-)
-
-var (
-	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
-	failNoLicenses    = fmt.Errorf("No licenses found")
-)
-
-const NOASSERTION = "NOASSERTION"
-
-type context struct {
-	stdout       io.Writer
-	stderr       io.Writer
-	rootFS       fs.FS
-	product      string
-	stripPrefix  []string
-	creationTime creationTimeGetter
-	buildid      string
-}
-
-func (ctx context) strip(installPath string) string {
-	for _, prefix := range ctx.stripPrefix {
-		if strings.HasPrefix(installPath, prefix) {
-			p := strings.TrimPrefix(installPath, prefix)
-			if 0 == len(p) {
-				p = ctx.product
-			}
-			if 0 == len(p) {
-				continue
-			}
-			return p
-		}
-	}
-	return installPath
-}
-
-// newMultiString creates a flag that allows multiple values in an array.
-func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
-	var f multiString
-	flags.Var(&f, name, usage)
-	return &f
-}
-
-// multiString implements the flag `Value` interface for multiple strings.
-type multiString []string
-
-func (ms *multiString) String() string     { return strings.Join(*ms, ", ") }
-func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
-
-func main() {
-	var expandedArgs []string
-	for _, arg := range os.Args[1:] {
-		if strings.HasPrefix(arg, "@") {
-			f, err := os.Open(strings.TrimPrefix(arg, "@"))
-			if err != nil {
-				fmt.Fprintln(os.Stderr, err.Error())
-				os.Exit(1)
-			}
-
-			respArgs, err := response.ReadRspFile(f)
-			f.Close()
-			if err != nil {
-				fmt.Fprintln(os.Stderr, err.Error())
-				os.Exit(1)
-			}
-			expandedArgs = append(expandedArgs, respArgs...)
-		} else {
-			expandedArgs = append(expandedArgs, arg)
-		}
-	}
-
-	flags := flag.NewFlagSet("flags", flag.ExitOnError)
-
-	flags.Usage = func() {
-		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
-
-Outputs an SBOM.spdx.
-
-Options:
-`, filepath.Base(os.Args[0]))
-		flags.PrintDefaults()
-	}
-
-	outputFile := flags.String("o", "-", "Where to write the SBOM spdx file. (default stdout)")
-	depsFile := flags.String("d", "", "Where to write the deps file")
-	product := flags.String("product", "", "The name of the product for which the notice is generated.")
-	stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
-	buildid := flags.String("build_id", "", "Uniquely identifies the build. (default timestamp)")
-
-	flags.Parse(expandedArgs)
-
-	// Must specify at least one root target.
-	if flags.NArg() == 0 {
-		flags.Usage()
-		os.Exit(2)
-	}
-
-	if len(*outputFile) == 0 {
-		flags.Usage()
-		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
-		os.Exit(2)
-	} else {
-		dir, err := filepath.Abs(filepath.Dir(*outputFile))
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
-			os.Exit(1)
-		}
-		fi, err := os.Stat(dir)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
-			os.Exit(1)
-		}
-		if !fi.IsDir() {
-			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
-			os.Exit(1)
-		}
-	}
-
-	var ofile io.Writer
-	ofile = os.Stdout
-	var obuf *bytes.Buffer
-	if *outputFile != "-" {
-		obuf = &bytes.Buffer{}
-		ofile = obuf
-	}
-
-	ctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, actualTime, *buildid}
-
-	spdxDoc, deps, err := sbomGenerator(ctx, flags.Args()...)
-
-	if err != nil {
-		if err == failNoneRequested {
-			flags.Usage()
-		}
-		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
-		os.Exit(1)
-	}
-
-	// writing the spdx Doc created
-	if err := spdx_json.Save2_2(spdxDoc, ofile); err != nil {
-		fmt.Fprintf(os.Stderr, "failed to write document to %v: %v", *outputFile, err)
-		os.Exit(1)
-	}
-
-	if *outputFile != "-" {
-		err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "could not write output to %q: %s\n", *outputFile, err)
-			os.Exit(1)
-		}
-	}
-
-	if *depsFile != "" {
-		err := deptools.WriteDepFile(*depsFile, *outputFile, deps)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "could not write deps to %q: %s\n", *depsFile, err)
-			os.Exit(1)
-		}
-	}
-	os.Exit(0)
-}
-
-type creationTimeGetter func() string
-
-// actualTime returns current time in UTC
-func actualTime() string {
-	t := time.Now().UTC()
-	return t.UTC().Format("2006-01-02T15:04:05Z")
-}
-
-// replaceSlashes replaces "/" by "-" for the library path to be used for packages & files SPDXID
-func replaceSlashes(x string) string {
-	return strings.ReplaceAll(x, "/", "-")
-}
-
-// stripDocName removes the outdir prefix and meta_lic suffix from a target Name
-func stripDocName(name string) string {
-	// remove outdir prefix
-	if strings.HasPrefix(name, "out/") {
-		name = name[4:]
-	}
-
-	// remove suffix
-	if strings.HasSuffix(name, ".meta_lic") {
-		name = name[:len(name)-9]
-	} else if strings.HasSuffix(name, "/meta_lic") {
-		name = name[:len(name)-9] + "/"
-	}
-
-	return name
-}
-
-// getPackageName returns a package name of a target Node
-func getPackageName(_ *context, tn *compliance.TargetNode) string {
-	return replaceSlashes(tn.Name())
-}
-
-// getDocumentName returns a package name of a target Node
-func getDocumentName(ctx *context, tn *compliance.TargetNode, pm *projectmetadata.ProjectMetadata) string {
-	if len(ctx.product) > 0 {
-		return replaceSlashes(ctx.product)
-	}
-	if len(tn.ModuleName()) > 0 {
-		if pm != nil {
-			return replaceSlashes(pm.Name() + ":" + tn.ModuleName())
-		}
-		return replaceSlashes(tn.ModuleName())
-	}
-
-	return stripDocName(replaceSlashes(tn.Name()))
-}
-
-// getDownloadUrl returns the download URL if available (GIT, SVN, etc..),
-// or NOASSERTION if not available, none determined or ambiguous
-func getDownloadUrl(_ *context, pm *projectmetadata.ProjectMetadata) string {
-	if pm == nil {
-		return NOASSERTION
-	}
-
-	urlsByTypeName := pm.UrlsByTypeName()
-	if urlsByTypeName == nil {
-		return NOASSERTION
-	}
-
-	url := urlsByTypeName.DownloadUrl()
-	if url == "" {
-		return NOASSERTION
-	}
-	return url
-}
-
-// getProjectMetadata returns the optimal project metadata for the target node
-func getProjectMetadata(_ *context, pmix *projectmetadata.Index,
-	tn *compliance.TargetNode) (*projectmetadata.ProjectMetadata, error) {
-	pms, err := pmix.MetadataForProjects(tn.Projects()...)
-	if err != nil {
-		return nil, fmt.Errorf("Unable to read projects for %q: %w\n", tn.Name(), err)
-	}
-	if len(pms) == 0 {
-		return nil, nil
-	}
-
-	// Getting the project metadata that contains most of the info needed for sbomGenerator
-	score := -1
-	index := -1
-	for i := 0; i < len(pms); i++ {
-		tempScore := 0
-		if pms[i].Name() != "" {
-			tempScore += 1
-		}
-		if pms[i].Version() != "" {
-			tempScore += 1
-		}
-		if pms[i].UrlsByTypeName().DownloadUrl() != "" {
-			tempScore += 1
-		}
-
-		if tempScore == score {
-			if pms[i].Project() < pms[index].Project() {
-				index = i
-			}
-		} else if tempScore > score {
-			score = tempScore
-			index = i
-		}
-	}
-	return pms[index], nil
-}
-
-// inputFiles returns the complete list of files read
-func inputFiles(lg *compliance.LicenseGraph, pmix *projectmetadata.Index, licenseTexts []string) []string {
-	projectMeta := pmix.AllMetadataFiles()
-	targets := lg.TargetNames()
-	files := make([]string, 0, len(licenseTexts)+len(targets)+len(projectMeta))
-	files = append(files, licenseTexts...)
-	files = append(files, targets...)
-	files = append(files, projectMeta...)
-	return files
-}
-
-// generateSPDXNamespace generates a unique SPDX Document Namespace using a SHA1 checksum
-func generateSPDXNamespace(buildid string, created string, files ...string) string {
-
-	seed := strings.Join(files, "")
-
-	if buildid == "" {
-		seed += created
-	} else {
-		seed += buildid
-	}
-
-	// Compute a SHA1 checksum of the seed.
-	hash := sha1.Sum([]byte(seed))
-	uuid := hex.EncodeToString(hash[:])
-
-	namespace := fmt.Sprintf("SPDXRef-DOCUMENT-%s", uuid)
-
-	return namespace
-}
-
-// sbomGenerator implements the spdx bom utility
-
-// SBOM is part of the new government regulation issued to improve national cyber security
-// and enhance software supply chain and transparency, see https://www.cisa.gov/sbom
-
-// sbomGenerator uses the SPDX standard, see the SPDX specification (https://spdx.github.io/spdx-spec/)
-// sbomGenerator is also following the internal google SBOM styleguide (http://goto.google.com/spdx-style-guide)
-func sbomGenerator(ctx *context, files ...string) (*spdx.Document, []string, error) {
-	// Must be at least one root file.
-	if len(files) < 1 {
-		return nil, nil, failNoneRequested
-	}
-
-	pmix := projectmetadata.NewIndex(ctx.rootFS)
-
-	lg, err := compliance.ReadLicenseGraph(ctx.rootFS, ctx.stderr, files)
-
-	if err != nil {
-		return nil, nil, fmt.Errorf("Unable to read license text file(s) for %q: %v\n", files, err)
-	}
-
-	// creating the packages section
-	pkgs := []*spdx.Package{}
-
-	// creating the relationship section
-	relationships := []*spdx.Relationship{}
-
-	// creating the license section
-	otherLicenses := []*spdx.OtherLicense{}
-
-	// spdx document name
-	var docName string
-
-	// main package name
-	var mainPkgName string
-
-	// implementing the licenses references for the packages
-	licenses := make(map[string]string)
-	concludedLicenses := func(licenseTexts []string) string {
-		licenseRefs := make([]string, 0, len(licenseTexts))
-		for _, licenseText := range licenseTexts {
-			license := strings.SplitN(licenseText, ":", 2)[0]
-			if _, ok := licenses[license]; !ok {
-				licenseRef := "LicenseRef-" + replaceSlashes(license)
-				licenses[license] = licenseRef
-			}
-
-			licenseRefs = append(licenseRefs, licenses[license])
-		}
-		if len(licenseRefs) > 1 {
-			return "(" + strings.Join(licenseRefs, " AND ") + ")"
-		} else if len(licenseRefs) == 1 {
-			return licenseRefs[0]
-		}
-		return "NONE"
-	}
-
-	isMainPackage := true
-	visitedNodes := make(map[*compliance.TargetNode]struct{})
-
-	// performing a Breadth-first top down walk of licensegraph and building package information
-	compliance.WalkTopDownBreadthFirst(nil, lg,
-		func(lg *compliance.LicenseGraph, tn *compliance.TargetNode, path compliance.TargetEdgePath) bool {
-			if err != nil {
-				return false
-			}
-			var pm *projectmetadata.ProjectMetadata
-			pm, err = getProjectMetadata(ctx, pmix, tn)
-			if err != nil {
-				return false
-			}
-
-			if isMainPackage {
-				docName = getDocumentName(ctx, tn, pm)
-				mainPkgName = replaceSlashes(getPackageName(ctx, tn))
-				isMainPackage = false
-			}
-
-			if len(path) == 0 {
-				// Add the describe relationship for the main package
-				rln := &spdx.Relationship{
-					RefA:         common.MakeDocElementID("" /* this document */, "DOCUMENT"),
-					RefB:         common.MakeDocElementID("", mainPkgName),
-					Relationship: "DESCRIBES",
-				}
-				relationships = append(relationships, rln)
-
-			} else {
-				// Check parent and identify annotation
-				parent := path[len(path)-1]
-				targetEdge := parent.Edge()
-				if targetEdge.IsRuntimeDependency() {
-					// Adding the dynamic link annotation RUNTIME_DEPENDENCY_OF relationship
-					rln := &spdx.Relationship{
-						RefA:         common.MakeDocElementID("", replaceSlashes(getPackageName(ctx, tn))),
-						RefB:         common.MakeDocElementID("", replaceSlashes(getPackageName(ctx, targetEdge.Target()))),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					}
-					relationships = append(relationships, rln)
-
-				} else if targetEdge.IsDerivation() {
-					// Adding the  derivation annotation as a CONTAINS relationship
-					rln := &spdx.Relationship{
-						RefA:         common.MakeDocElementID("", replaceSlashes(getPackageName(ctx, targetEdge.Target()))),
-						RefB:         common.MakeDocElementID("", replaceSlashes(getPackageName(ctx, tn))),
-						Relationship: "CONTAINS",
-					}
-					relationships = append(relationships, rln)
-
-				} else if targetEdge.IsBuildTool() {
-					// Adding the toolchain annotation as a BUILD_TOOL_OF relationship
-					rln := &spdx.Relationship{
-						RefA:         common.MakeDocElementID("", replaceSlashes(getPackageName(ctx, tn))),
-						RefB:         common.MakeDocElementID("", replaceSlashes(getPackageName(ctx, targetEdge.Target()))),
-						Relationship: "BUILD_TOOL_OF",
-					}
-					relationships = append(relationships, rln)
-
-				} else {
-					panic(fmt.Errorf("Unknown dependency type: %v", targetEdge.Annotations()))
-				}
-			}
-
-			if _, alreadyVisited := visitedNodes[tn]; alreadyVisited {
-				return false
-			}
-			visitedNodes[tn] = struct{}{}
-			pkgName := getPackageName(ctx, tn)
-
-			// Making an spdx package and adding it to pkgs
-			pkg := &spdx.Package{
-				PackageName:             replaceSlashes(pkgName),
-				PackageDownloadLocation: getDownloadUrl(ctx, pm),
-				PackageSPDXIdentifier:   common.ElementID(replaceSlashes(pkgName)),
-				PackageLicenseConcluded: concludedLicenses(tn.LicenseTexts()),
-			}
-
-			if pm != nil && pm.Version() != "" {
-				pkg.PackageVersion = pm.Version()
-			} else {
-				pkg.PackageVersion = NOASSERTION
-			}
-
-			pkgs = append(pkgs, pkg)
-
-			return true
-		})
-
-	// Adding Non-standard licenses
-
-	licenseTexts := make([]string, 0, len(licenses))
-
-	for licenseText := range licenses {
-		licenseTexts = append(licenseTexts, licenseText)
-	}
-
-	sort.Strings(licenseTexts)
-
-	for _, licenseText := range licenseTexts {
-		// open the file
-		f, err := ctx.rootFS.Open(filepath.Clean(licenseText))
-		if err != nil {
-			return nil, nil, fmt.Errorf("error opening license text file %q: %w", licenseText, err)
-		}
-
-		// read the file
-		text, err := io.ReadAll(f)
-		if err != nil {
-			return nil, nil, fmt.Errorf("error reading license text file %q: %w", licenseText, err)
-		}
-		// Making an spdx License and adding it to otherLicenses
-		otherLicenses = append(otherLicenses, &spdx.OtherLicense{
-			LicenseName:       strings.Replace(licenses[licenseText], "LicenseRef-", "", -1),
-			LicenseIdentifier: string(licenses[licenseText]),
-			ExtractedText:     string(text),
-		})
-	}
-
-	deps := inputFiles(lg, pmix, licenseTexts)
-	sort.Strings(deps)
-
-	// Making the SPDX doc
-	ci, err := builder2v2.BuildCreationInfoSection2_2("Organization", "Google LLC", nil)
-	if err != nil {
-		return nil, nil, fmt.Errorf("Unable to build creation info section for SPDX doc: %v\n", err)
-	}
-
-	ci.Created = ctx.creationTime()
-
-	doc := &spdx.Document{
-		SPDXVersion:       "SPDX-2.2",
-		DataLicense:       "CC0-1.0",
-		SPDXIdentifier:    "DOCUMENT",
-		DocumentName:      docName,
-		DocumentNamespace: generateSPDXNamespace(ctx.buildid, ci.Created, files...),
-		CreationInfo:      ci,
-		Packages:          pkgs,
-		Relationships:     relationships,
-		OtherLicenses:     otherLicenses,
-	}
-
-	if err := spdxlib.ValidateDocument2_2(doc); err != nil {
-		return nil, nil, fmt.Errorf("Unable to validate the SPDX doc: %v\n", err)
-	}
-
-	return doc, deps, nil
-}
diff --git a/tools/compliance/cmd/sbom/sbom_test.go b/tools/compliance/cmd/sbom/sbom_test.go
deleted file mode 100644
index 13ba66d..0000000
--- a/tools/compliance/cmd/sbom/sbom_test.go
+++ /dev/null
@@ -1,2558 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
-	"bytes"
-	"encoding/json"
-	"fmt"
-	"os"
-	"reflect"
-	"strings"
-	"testing"
-	"time"
-
-	"android/soong/tools/compliance"
-
-	"github.com/spdx/tools-golang/builder/builder2v2"
-	"github.com/spdx/tools-golang/spdx/common"
-	spdx "github.com/spdx/tools-golang/spdx/v2_2"
-)
-
-func TestMain(m *testing.M) {
-	// Change into the parent directory before running the tests
-	// so they can find the testdata directory.
-	if err := os.Chdir(".."); err != nil {
-		fmt.Printf("failed to change to testdata directory: %s\n", err)
-		os.Exit(1)
-	}
-	os.Exit(m.Run())
-}
-
-func Test(t *testing.T) {
-	tests := []struct {
-		condition    string
-		name         string
-		outDir       string
-		roots        []string
-		stripPrefix  string
-		expectedOut  *spdx.Document
-		expectedDeps []string
-	}{
-		{
-			condition: "firstparty",
-			name:      "apex",
-			roots:     []string{"highest.apex.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-firstparty-highest.apex",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/firstparty/highest.apex.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-firstparty-highest.apex.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-highest.apex.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-bin-bin2.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-bin-bin2.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-highest.apex.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-bin-bin1.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-bin-bin2.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-libb.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-lib-libd.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/firstparty/bin/bin1.meta_lic",
-				"testdata/firstparty/bin/bin2.meta_lic",
-				"testdata/firstparty/highest.apex.meta_lic",
-				"testdata/firstparty/lib/liba.so.meta_lic",
-				"testdata/firstparty/lib/libb.so.meta_lic",
-				"testdata/firstparty/lib/libc.a.meta_lic",
-				"testdata/firstparty/lib/libd.so.meta_lic",
-			},
-		},
-		{
-			condition: "firstparty",
-			name:      "application",
-			roots:     []string{"application.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-firstparty-application",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/firstparty/application.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-firstparty-application.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-application.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-bin-bin3.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-bin-bin3.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-application.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-bin-bin3.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-application.meta_lic"),
-						Relationship: "BUILD_TOOL_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-application.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-application.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/firstparty/application.meta_lic",
-				"testdata/firstparty/bin/bin3.meta_lic",
-				"testdata/firstparty/lib/liba.so.meta_lic",
-				"testdata/firstparty/lib/libb.so.meta_lic",
-			},
-		},
-		{
-			condition: "firstparty",
-			name:      "container",
-			roots:     []string{"container.zip.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-firstparty-container.zip",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/firstparty/container.zip.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-firstparty-container.zip.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-container.zip.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-bin-bin2.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-bin-bin2.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-container.zip.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-bin-bin1.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-bin-bin2.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-libb.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-lib-libd.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/firstparty/bin/bin1.meta_lic",
-				"testdata/firstparty/bin/bin2.meta_lic",
-				"testdata/firstparty/container.zip.meta_lic",
-				"testdata/firstparty/lib/liba.so.meta_lic",
-				"testdata/firstparty/lib/libb.so.meta_lic",
-				"testdata/firstparty/lib/libc.a.meta_lic",
-				"testdata/firstparty/lib/libd.so.meta_lic",
-			},
-		},
-		{
-			condition: "firstparty",
-			name:      "binary",
-			roots:     []string{"bin/bin1.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-firstparty-bin-bin1",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/firstparty/bin/bin1.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-firstparty-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-firstparty-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-bin-bin1.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-firstparty-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/firstparty/bin/bin1.meta_lic",
-				"testdata/firstparty/lib/liba.so.meta_lic",
-				"testdata/firstparty/lib/libc.a.meta_lic",
-			},
-		},
-		{
-			condition: "firstparty",
-			name:      "library",
-			roots:     []string{"lib/libd.so.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-firstparty-lib-libd.so",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/firstparty/lib/libd.so.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-firstparty-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-firstparty-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-firstparty-lib-libd.so.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/firstparty/lib/libd.so.meta_lic",
-			},
-		},
-		{
-			condition: "notice",
-			name:      "apex",
-			roots:     []string{"highest.apex.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-notice-highest.apex",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/notice/highest.apex.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-notice-highest.apex.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-highest.apex.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-bin-bin2.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-bin-bin2.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-highest.apex.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-bin-bin1.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-bin-bin2.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-libb.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-lib-libd.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/notice/bin/bin1.meta_lic",
-				"testdata/notice/bin/bin2.meta_lic",
-				"testdata/notice/highest.apex.meta_lic",
-				"testdata/notice/lib/liba.so.meta_lic",
-				"testdata/notice/lib/libb.so.meta_lic",
-				"testdata/notice/lib/libc.a.meta_lic",
-				"testdata/notice/lib/libd.so.meta_lic",
-			},
-		},
-		{
-			condition: "notice",
-			name:      "container",
-			roots:     []string{"container.zip.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-notice-container.zip",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/notice/container.zip.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-notice-container.zip.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-container.zip.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-bin-bin2.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-bin-bin2.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-container.zip.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-bin-bin1.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-bin-bin2.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-libb.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-lib-libd.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/notice/bin/bin1.meta_lic",
-				"testdata/notice/bin/bin2.meta_lic",
-				"testdata/notice/container.zip.meta_lic",
-				"testdata/notice/lib/liba.so.meta_lic",
-				"testdata/notice/lib/libb.so.meta_lic",
-				"testdata/notice/lib/libc.a.meta_lic",
-				"testdata/notice/lib/libd.so.meta_lic",
-			},
-		},
-		{
-			condition: "notice",
-			name:      "application",
-			roots:     []string{"application.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-notice-application",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/notice/application.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-notice-application.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-application.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-bin-bin3.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-bin-bin3.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-application.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-bin-bin3.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-application.meta_lic"),
-						Relationship: "BUILD_TOOL_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-application.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-application.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/notice/application.meta_lic",
-				"testdata/notice/bin/bin3.meta_lic",
-				"testdata/notice/lib/liba.so.meta_lic",
-				"testdata/notice/lib/libb.so.meta_lic",
-			},
-		},
-		{
-			condition: "notice",
-			name:      "binary",
-			roots:     []string{"bin/bin1.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-notice-bin-bin1",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/notice/bin/bin1.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-notice-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						PackageName:             "testdata-notice-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-bin-bin1.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-notice-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/notice/bin/bin1.meta_lic",
-				"testdata/notice/lib/liba.so.meta_lic",
-				"testdata/notice/lib/libc.a.meta_lic",
-			},
-		},
-		{
-			condition: "notice",
-			name:      "library",
-			roots:     []string{"lib/libd.so.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-notice-lib-libd.so",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/notice/lib/libd.so.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-notice-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-notice-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-notice-lib-libd.so.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/notice/lib/libd.so.meta_lic",
-			},
-		},
-		{
-			condition: "reciprocal",
-			name:      "apex",
-			roots:     []string{"highest.apex.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-reciprocal-highest.apex",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/reciprocal/highest.apex.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-reciprocal-highest.apex.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-highest.apex.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-reciprocal-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-reciprocal-bin-bin2.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-bin-bin2.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-reciprocal-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-					{
-						PackageName:             "testdata-reciprocal-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-reciprocal-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-					{
-						PackageName:             "testdata-reciprocal-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-highest.apex.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-bin-bin1.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-bin-bin2.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-lib-libb.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-lib-libd.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-						ExtractedText:     "$$$Reciprocal License$$$\n",
-						LicenseName:       "testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/reciprocal/RECIPROCAL_LICENSE",
-				"testdata/reciprocal/bin/bin1.meta_lic",
-				"testdata/reciprocal/bin/bin2.meta_lic",
-				"testdata/reciprocal/highest.apex.meta_lic",
-				"testdata/reciprocal/lib/liba.so.meta_lic",
-				"testdata/reciprocal/lib/libb.so.meta_lic",
-				"testdata/reciprocal/lib/libc.a.meta_lic",
-				"testdata/reciprocal/lib/libd.so.meta_lic",
-			},
-		},
-		{
-			condition: "reciprocal",
-			name:      "application",
-			roots:     []string{"application.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-reciprocal-application",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/reciprocal/application.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-reciprocal-application.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-application.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-reciprocal-bin-bin3.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-bin-bin3.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						PackageName:             "testdata-reciprocal-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-					{
-						PackageName:             "testdata-reciprocal-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-application.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-bin-bin3.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-application.meta_lic"),
-						Relationship: "BUILD_TOOL_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-application.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-application.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-						ExtractedText:     "$$$Reciprocal License$$$\n",
-						LicenseName:       "testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/reciprocal/RECIPROCAL_LICENSE",
-				"testdata/reciprocal/application.meta_lic",
-				"testdata/reciprocal/bin/bin3.meta_lic",
-				"testdata/reciprocal/lib/liba.so.meta_lic",
-				"testdata/reciprocal/lib/libb.so.meta_lic",
-			},
-		},
-		{
-			condition: "reciprocal",
-			name:      "binary",
-			roots:     []string{"bin/bin1.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-reciprocal-bin-bin1",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/reciprocal/bin/bin1.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-reciprocal-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-reciprocal-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-					{
-						PackageName:             "testdata-reciprocal-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-bin-bin1.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-reciprocal-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-						ExtractedText:     "$$$Reciprocal License$$$\n",
-						LicenseName:       "testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/reciprocal/RECIPROCAL_LICENSE",
-				"testdata/reciprocal/bin/bin1.meta_lic",
-				"testdata/reciprocal/lib/liba.so.meta_lic",
-				"testdata/reciprocal/lib/libc.a.meta_lic",
-			},
-		},
-		{
-			condition: "reciprocal",
-			name:      "library",
-			roots:     []string{"lib/libd.so.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-reciprocal-lib-libd.so",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/reciprocal/lib/libd.so.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-reciprocal-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-reciprocal-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-reciprocal-lib-libd.so.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/reciprocal/lib/libd.so.meta_lic",
-			},
-		},
-		{
-			condition: "restricted",
-			name:      "apex",
-			roots:     []string{"highest.apex.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-restricted-highest.apex",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/restricted/highest.apex.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-restricted-highest.apex.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-highest.apex.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-bin-bin2.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-bin-bin2.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-highest.apex.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-bin-bin1.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-bin-bin2.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-lib-libb.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-lib-libd.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-						ExtractedText:     "$$$Reciprocal License$$$\n",
-						LicenseName:       "testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-						ExtractedText:     "###Restricted License###\n",
-						LicenseName:       "testdata-restricted-RESTRICTED_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/reciprocal/RECIPROCAL_LICENSE",
-				"testdata/restricted/RESTRICTED_LICENSE",
-				"testdata/restricted/bin/bin1.meta_lic",
-				"testdata/restricted/bin/bin2.meta_lic",
-				"testdata/restricted/highest.apex.meta_lic",
-				"testdata/restricted/lib/liba.so.meta_lic",
-				"testdata/restricted/lib/libb.so.meta_lic",
-				"testdata/restricted/lib/libc.a.meta_lic",
-				"testdata/restricted/lib/libd.so.meta_lic",
-			},
-		},
-		{
-			condition: "restricted",
-			name:      "container",
-			roots:     []string{"container.zip.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-restricted-container.zip",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/restricted/container.zip.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-restricted-container.zip.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-container.zip.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-bin-bin2.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-bin-bin2.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-container.zip.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-bin-bin1.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-bin-bin2.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-lib-libb.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-lib-libd.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-						ExtractedText:     "$$$Reciprocal License$$$\n",
-						LicenseName:       "testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-						ExtractedText:     "###Restricted License###\n",
-						LicenseName:       "testdata-restricted-RESTRICTED_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/reciprocal/RECIPROCAL_LICENSE",
-				"testdata/restricted/RESTRICTED_LICENSE",
-				"testdata/restricted/bin/bin1.meta_lic",
-				"testdata/restricted/bin/bin2.meta_lic",
-				"testdata/restricted/container.zip.meta_lic",
-				"testdata/restricted/lib/liba.so.meta_lic",
-				"testdata/restricted/lib/libb.so.meta_lic",
-				"testdata/restricted/lib/libc.a.meta_lic",
-				"testdata/restricted/lib/libd.so.meta_lic",
-			},
-		},
-		{
-			condition: "restricted",
-			name:      "binary",
-			roots:     []string{"bin/bin1.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-restricted-bin-bin1",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/restricted/bin/bin1.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-restricted-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-					},
-					{
-						PackageName:             "testdata-restricted-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-bin-bin1.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-restricted-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-reciprocal-RECIPROCAL_LICENSE",
-						ExtractedText:     "$$$Reciprocal License$$$\n",
-						LicenseName:       "testdata-reciprocal-RECIPROCAL_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-						ExtractedText:     "###Restricted License###\n",
-						LicenseName:       "testdata-restricted-RESTRICTED_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/reciprocal/RECIPROCAL_LICENSE",
-				"testdata/restricted/RESTRICTED_LICENSE",
-				"testdata/restricted/bin/bin1.meta_lic",
-				"testdata/restricted/lib/liba.so.meta_lic",
-				"testdata/restricted/lib/libc.a.meta_lic",
-			},
-		},
-		{
-			condition: "restricted",
-			name:      "library",
-			roots:     []string{"lib/libd.so.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-restricted-lib-libd.so",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/restricted/lib/libd.so.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-restricted-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-restricted-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-restricted-lib-libd.so.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/restricted/lib/libd.so.meta_lic",
-			},
-		},
-		{
-			condition: "proprietary",
-			name:      "apex",
-			roots:     []string{"highest.apex.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-proprietary-highest.apex",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/proprietary/highest.apex.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-proprietary-highest.apex.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-highest.apex.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-bin-bin2.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-bin-bin2.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-highest.apex.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-bin-bin1.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-bin-bin2.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-highest.apex.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-libb.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-lib-libd.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-						ExtractedText:     "@@@Proprietary License@@@\n",
-						LicenseName:       "testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-						ExtractedText:     "###Restricted License###\n",
-						LicenseName:       "testdata-restricted-RESTRICTED_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/proprietary/PROPRIETARY_LICENSE",
-				"testdata/proprietary/bin/bin1.meta_lic",
-				"testdata/proprietary/bin/bin2.meta_lic",
-				"testdata/proprietary/highest.apex.meta_lic",
-				"testdata/proprietary/lib/liba.so.meta_lic",
-				"testdata/proprietary/lib/libb.so.meta_lic",
-				"testdata/proprietary/lib/libc.a.meta_lic",
-				"testdata/proprietary/lib/libd.so.meta_lic",
-				"testdata/restricted/RESTRICTED_LICENSE",
-			},
-		},
-		{
-			condition: "proprietary",
-			name:      "container",
-			roots:     []string{"container.zip.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-proprietary-container.zip",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/proprietary/container.zip.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-proprietary-container.zip.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-container.zip.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-bin-bin2.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-bin-bin2.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-container.zip.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-bin-bin1.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-bin-bin2.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-container.zip.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-libb.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-lib-libd.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-bin-bin2.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-						ExtractedText:     "@@@Proprietary License@@@\n",
-						LicenseName:       "testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-						ExtractedText:     "###Restricted License###\n",
-						LicenseName:       "testdata-restricted-RESTRICTED_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/proprietary/PROPRIETARY_LICENSE",
-				"testdata/proprietary/bin/bin1.meta_lic",
-				"testdata/proprietary/bin/bin2.meta_lic",
-				"testdata/proprietary/container.zip.meta_lic",
-				"testdata/proprietary/lib/liba.so.meta_lic",
-				"testdata/proprietary/lib/libb.so.meta_lic",
-				"testdata/proprietary/lib/libc.a.meta_lic",
-				"testdata/proprietary/lib/libd.so.meta_lic",
-				"testdata/restricted/RESTRICTED_LICENSE",
-			},
-		},
-		{
-			condition: "proprietary",
-			name:      "application",
-			roots:     []string{"application.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-proprietary-application",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/proprietary/application.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-proprietary-application.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-application.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-bin-bin3.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-bin-bin3.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-libb.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-libb.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-application.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-bin-bin3.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-application.meta_lic"),
-						Relationship: "BUILD_TOOL_OF",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-application.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-lib-libb.so.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-application.meta_lic"),
-						Relationship: "RUNTIME_DEPENDENCY_OF",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-						ExtractedText:     "@@@Proprietary License@@@\n",
-						LicenseName:       "testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-restricted-RESTRICTED_LICENSE",
-						ExtractedText:     "###Restricted License###\n",
-						LicenseName:       "testdata-restricted-RESTRICTED_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/proprietary/PROPRIETARY_LICENSE",
-				"testdata/proprietary/application.meta_lic",
-				"testdata/proprietary/bin/bin3.meta_lic",
-				"testdata/proprietary/lib/liba.so.meta_lic",
-				"testdata/proprietary/lib/libb.so.meta_lic",
-				"testdata/restricted/RESTRICTED_LICENSE",
-			},
-		},
-		{
-			condition: "proprietary",
-			name:      "binary",
-			roots:     []string{"bin/bin1.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-proprietary-bin-bin1",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/proprietary/bin/bin1.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-proprietary-bin-bin1.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-bin-bin1.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-liba.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-liba.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-					{
-						PackageName:             "testdata-proprietary-lib-libc.a.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-libc.a.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-bin-bin1.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-liba.so.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-					{
-						RefA:         common.MakeDocElementID("", "testdata-proprietary-bin-bin1.meta_lic"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-libc.a.meta_lic"),
-						Relationship: "CONTAINS",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-firstparty-FIRST_PARTY_LICENSE",
-						ExtractedText:     "&&&First Party License&&&\n",
-						LicenseName:       "testdata-firstparty-FIRST_PARTY_LICENSE",
-					},
-					{
-						LicenseIdentifier: "LicenseRef-testdata-proprietary-PROPRIETARY_LICENSE",
-						ExtractedText:     "@@@Proprietary License@@@\n",
-						LicenseName:       "testdata-proprietary-PROPRIETARY_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/firstparty/FIRST_PARTY_LICENSE",
-				"testdata/proprietary/PROPRIETARY_LICENSE",
-				"testdata/proprietary/bin/bin1.meta_lic",
-				"testdata/proprietary/lib/liba.so.meta_lic",
-				"testdata/proprietary/lib/libc.a.meta_lic",
-			},
-		},
-		{
-			condition: "proprietary",
-			name:      "library",
-			roots:     []string{"lib/libd.so.meta_lic"},
-			expectedOut: &spdx.Document{
-				SPDXVersion:       "SPDX-2.2",
-				DataLicense:       "CC0-1.0",
-				SPDXIdentifier:    "DOCUMENT",
-				DocumentName:      "testdata-proprietary-lib-libd.so",
-				DocumentNamespace: generateSPDXNamespace("", "1970-01-01T00:00:00Z", "testdata/proprietary/lib/libd.so.meta_lic"),
-				CreationInfo:      getCreationInfo(t),
-				Packages: []*spdx.Package{
-					{
-						PackageName:             "testdata-proprietary-lib-libd.so.meta_lic",
-						PackageVersion:          "NOASSERTION",
-						PackageDownloadLocation: "NOASSERTION",
-						PackageSPDXIdentifier:   common.ElementID("testdata-proprietary-lib-libd.so.meta_lic"),
-						PackageLicenseConcluded: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-					},
-				},
-				Relationships: []*spdx.Relationship{
-					{
-						RefA:         common.MakeDocElementID("", "DOCUMENT"),
-						RefB:         common.MakeDocElementID("", "testdata-proprietary-lib-libd.so.meta_lic"),
-						Relationship: "DESCRIBES",
-					},
-				},
-				OtherLicenses: []*spdx.OtherLicense{
-					{
-						LicenseIdentifier: "LicenseRef-testdata-notice-NOTICE_LICENSE",
-						ExtractedText:     "%%%Notice License%%%\n",
-						LicenseName:       "testdata-notice-NOTICE_LICENSE",
-					},
-				},
-			},
-			expectedDeps: []string{
-				"testdata/notice/NOTICE_LICENSE",
-				"testdata/proprietary/lib/libd.so.meta_lic",
-			},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
-			stdout := &bytes.Buffer{}
-			stderr := &bytes.Buffer{}
-
-			rootFiles := make([]string, 0, len(tt.roots))
-			for _, r := range tt.roots {
-				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
-			}
-
-			ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), "", []string{tt.stripPrefix}, fakeTime, ""}
-
-			spdxDoc, deps, err := sbomGenerator(&ctx, rootFiles...)
-			if err != nil {
-				t.Fatalf("sbom: error = %v, stderr = %v", err, stderr)
-				return
-			}
-			if stderr.Len() > 0 {
-				t.Errorf("sbom: gotStderr = %v, want none", stderr)
-			}
-
-			if err := validate(spdxDoc); err != nil {
-				t.Fatalf("sbom: document fails to validate: %v", err)
-			}
-
-			gotData, err := json.Marshal(spdxDoc)
-			if err != nil {
-				t.Fatalf("sbom: failed to marshal spdx doc: %v", err)
-				return
-			}
-
-			t.Logf("Got SPDX Doc: %s", string(gotData))
-
-			expectedData, err := json.Marshal(tt.expectedOut)
-			if err != nil {
-				t.Fatalf("sbom: failed to marshal spdx doc: %v", err)
-				return
-			}
-
-			t.Logf("Want SPDX Doc: %s", string(expectedData))
-
-			// compare the spdx Docs
-			compareSpdxDocs(t, spdxDoc, tt.expectedOut)
-
-			// compare deps
-			t.Logf("got deps: %q", deps)
-
-			t.Logf("want deps: %q", tt.expectedDeps)
-
-			if g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {
-				t.Errorf("unexpected deps, wanted:\n%s\ngot:\n%s\n",
-					strings.Join(w, "\n"), strings.Join(g, "\n"))
-			}
-		})
-	}
-}
-
-func TestGenerateSPDXNamespace(t *testing.T) {
-
-	buildID1 := "example-1"
-	buildID2 := "example-2"
-	files1 := "file1"
-	timestamp1 := "2022-05-01"
-	timestamp2 := "2022-05-02"
-	files2 := "file2"
-
-	// Test case 1: different timestamps, same files
-	nsh1 := generateSPDXNamespace("", timestamp1, files1)
-	nsh2 := generateSPDXNamespace("", timestamp2, files1)
-
-	if nsh1 == "" {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", "", timestamp1, files1)
-	}
-
-	if nsh2 == "" {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", "", timestamp2, files1)
-	}
-
-	if nsh1 == nsh2 {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s) and generateSPDXNamespace(%s, %s, %s): expected different namespace hashes, but got the same", "", timestamp1, files1, "", timestamp2, files1)
-	}
-
-	// Test case 2: different build ids, same timestamps and files
-	nsh1 = generateSPDXNamespace(buildID1, timestamp1, files1)
-	nsh2 = generateSPDXNamespace(buildID2, timestamp1, files1)
-
-	if nsh1 == "" {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID1, timestamp1, files1)
-	}
-
-	if nsh2 == "" {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID2, timestamp1, files1)
-	}
-
-	if nsh1 == nsh2 {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s) and generateSPDXNamespace(%s, %s, %s): expected different namespace hashes, but got the same", buildID1, timestamp1, files1, buildID2, timestamp1, files1)
-	}
-
-	// Test case 3: same build ids and files, different timestamps
-	nsh1 = generateSPDXNamespace(buildID1, timestamp1, files1)
-	nsh2 = generateSPDXNamespace(buildID1, timestamp2, files1)
-
-	if nsh1 == "" {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID1, timestamp1, files1)
-	}
-
-	if nsh2 == "" {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID1, timestamp2, files1)
-	}
-
-	if nsh1 != nsh2 {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s) and generateSPDXNamespace(%s, %s, %s): expected same namespace hashes, but got different: %s and %s", buildID1, timestamp1, files1, buildID2, timestamp1, files1, nsh1, nsh2)
-	}
-
-	// Test case 4: same build ids and timestamps, different files
-	nsh1 = generateSPDXNamespace(buildID1, timestamp1, files1)
-	nsh2 = generateSPDXNamespace(buildID1, timestamp1, files2)
-
-	if nsh1 == "" {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID1, timestamp1, files1)
-	}
-
-	if nsh2 == "" {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", buildID1, timestamp1, files2)
-	}
-
-	if nsh1 == nsh2 {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s) and generateSPDXNamespace(%s, %s, %s): expected different namespace hashes, but got the same", buildID1, timestamp1, files1, buildID1, timestamp1, files2)
-	}
-
-	// Test case 5: empty build ids, same timestamps and different files
-	nsh1 = generateSPDXNamespace("", timestamp1, files1)
-	nsh2 = generateSPDXNamespace("", timestamp1, files2)
-
-	if nsh1 == "" {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", "", timestamp1, files1)
-	}
-
-	if nsh2 == "" {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s): expected non-empty string, but got empty string", "", timestamp1, files2)
-	}
-
-	if nsh1 == nsh2 {
-		t.Errorf("generateSPDXNamespace(%s, %s, %s) and generateSPDXNamespace(%s, %s, %s): expected different namespace hashes, but got the same", "", timestamp1, files1, "", timestamp1, files2)
-	}
-}
-
-func getCreationInfo(t *testing.T) *spdx.CreationInfo {
-	ci, err := builder2v2.BuildCreationInfoSection2_2("Organization", "Google LLC", nil)
-	if err != nil {
-		t.Errorf("Unable to get creation info: %v", err)
-		return nil
-	}
-	return ci
-}
-
-// validate returns an error if the Document is found to be invalid
-func validate(doc *spdx.Document) error {
-	if doc.SPDXVersion == "" {
-		return fmt.Errorf("SPDXVersion: got nothing, want spdx version")
-	}
-	if doc.DataLicense == "" {
-		return fmt.Errorf("DataLicense: got nothing, want Data License")
-	}
-	if doc.SPDXIdentifier == "" {
-		return fmt.Errorf("SPDXIdentifier: got nothing, want SPDX Identifier")
-	}
-	if doc.DocumentName == "" {
-		return fmt.Errorf("DocumentName: got nothing, want Document Name")
-	}
-	if c := fmt.Sprintf("%v", doc.CreationInfo.Creators[1].Creator); c != "Google LLC" {
-		return fmt.Errorf("Creator: got %v, want  'Google LLC'", c)
-	}
-	_, err := time.Parse(time.RFC3339, doc.CreationInfo.Created)
-	if err != nil {
-		return fmt.Errorf("Invalid time spec: %q: got error %q, want no error", doc.CreationInfo.Created, err)
-	}
-
-	for _, license := range doc.OtherLicenses {
-		if license.ExtractedText == "" {
-			return fmt.Errorf("License file: %q: got nothing, want license text", license.LicenseName)
-		}
-	}
-	return nil
-}
-
-// compareSpdxDocs deep-compares two spdx docs by going through the info section, packages, relationships and licenses
-func compareSpdxDocs(t *testing.T, actual, expected *spdx.Document) {
-
-	if actual == nil || expected == nil {
-		t.Errorf("SBOM: SPDX Doc is nil! Got %v: Expected %v", actual, expected)
-	}
-
-	if actual.DocumentName != expected.DocumentName {
-		t.Errorf("sbom: unexpected SPDX Document Name got %q, want %q", actual.DocumentName, expected.DocumentName)
-	}
-
-	if actual.SPDXVersion != expected.SPDXVersion {
-		t.Errorf("sbom: unexpected SPDX Version got %s, want %s", actual.SPDXVersion, expected.SPDXVersion)
-	}
-
-	if actual.DataLicense != expected.DataLicense {
-		t.Errorf("sbom: unexpected SPDX DataLicense got %s, want %s", actual.DataLicense, expected.DataLicense)
-	}
-
-	if actual.SPDXIdentifier != expected.SPDXIdentifier {
-		t.Errorf("sbom: unexpected SPDX Identified got %s, want %s", actual.SPDXIdentifier, expected.SPDXIdentifier)
-	}
-
-	if actual.DocumentNamespace != expected.DocumentNamespace {
-		t.Errorf("sbom: unexpected SPDX Document Namespace got %s, want %s", actual.DocumentNamespace, expected.DocumentNamespace)
-	}
-
-	// compare creation info
-	compareSpdxCreationInfo(t, actual.CreationInfo, expected.CreationInfo)
-
-	// compare packages
-	if len(actual.Packages) != len(expected.Packages) {
-		t.Errorf("SBOM: Number of Packages is different! Got %d: Expected %d", len(actual.Packages), len(expected.Packages))
-	}
-
-	for i, pkg := range actual.Packages {
-		if !compareSpdxPackages(t, i, pkg, expected.Packages[i]) {
-			break
-		}
-	}
-
-	// compare licenses
-	if len(actual.OtherLicenses) != len(expected.OtherLicenses) {
-		t.Errorf("SBOM: Number of Licenses in actual is different! Got %d: Expected %d", len(actual.OtherLicenses), len(expected.OtherLicenses))
-	}
-	for i, license := range actual.OtherLicenses {
-		if !compareLicenses(t, i, license, expected.OtherLicenses[i]) {
-			break
-		}
-	}
-
-	//compare Relationships
-	if len(actual.Relationships) != len(expected.Relationships) {
-		t.Errorf("SBOM: Number of Licenses in actual is different! Got %d: Expected %d", len(actual.Relationships), len(expected.Relationships))
-	}
-	for i, rl := range actual.Relationships {
-		if !compareRelationShips(t, i, rl, expected.Relationships[i]) {
-			break
-		}
-	}
-}
-
-func compareSpdxCreationInfo(t *testing.T, actual, expected *spdx.CreationInfo) {
-	if actual == nil || expected == nil {
-		t.Errorf("SBOM: Creation info is nil! Got %q: Expected %q", actual, expected)
-	}
-
-	if actual.LicenseListVersion != expected.LicenseListVersion {
-		t.Errorf("SBOM: Creation info license version Error! Got %s: Expected %s", actual.LicenseListVersion, expected.LicenseListVersion)
-	}
-
-	if len(actual.Creators) != len(expected.Creators) {
-		t.Errorf("SBOM: Creation info creators Error! Got %d: Expected %d", len(actual.Creators), len(expected.Creators))
-	}
-
-	for i, info := range actual.Creators {
-		if info != expected.Creators[i] {
-			t.Errorf("SBOM: Creation info creators Error! Got %q: Expected %q", info, expected.Creators[i])
-		}
-	}
-}
-
-func compareSpdxPackages(t *testing.T, i int, actual, expected *spdx.Package) bool {
-	if actual == nil || expected == nil {
-		t.Errorf("SBOM: Packages are nil at index %d! Got %v: Expected %v", i, actual, expected)
-		return false
-	}
-	if actual.PackageName != expected.PackageName {
-		t.Errorf("SBOM: Package name Error at index %d! Got %s: Expected %s", i, actual.PackageName, expected.PackageName)
-		return false
-	}
-
-	if actual.PackageVersion != expected.PackageVersion {
-		t.Errorf("SBOM: Package version Error at index %d! Got %s: Expected %s", i, actual.PackageVersion, expected.PackageVersion)
-		return false
-	}
-
-	if actual.PackageSPDXIdentifier != expected.PackageSPDXIdentifier {
-		t.Errorf("SBOM: Package identifier Error at index %d! Got %s: Expected %s", i, actual.PackageSPDXIdentifier, expected.PackageSPDXIdentifier)
-		return false
-	}
-
-	if actual.PackageDownloadLocation != expected.PackageDownloadLocation {
-		t.Errorf("SBOM: Package download location Error at index %d! Got %s: Expected %s", i, actual.PackageDownloadLocation, expected.PackageDownloadLocation)
-		return false
-	}
-
-	if actual.PackageLicenseConcluded != expected.PackageLicenseConcluded {
-		t.Errorf("SBOM: Package license concluded Error at index %d! Got %s: Expected %s", i, actual.PackageLicenseConcluded, expected.PackageLicenseConcluded)
-		return false
-	}
-	return true
-}
-
-func compareRelationShips(t *testing.T, i int, actual, expected *spdx.Relationship) bool {
-	if actual == nil || expected == nil {
-		t.Errorf("SBOM: Relationships is nil at index %d! Got %v: Expected %v", i, actual, expected)
-		return false
-	}
-
-	if actual.RefA != expected.RefA {
-		t.Errorf("SBOM: Relationship RefA Error at index %d! Got %s: Expected %s", i, actual.RefA, expected.RefA)
-		return false
-	}
-
-	if actual.RefB != expected.RefB {
-		t.Errorf("SBOM: Relationship RefB Error at index %d! Got %s: Expected %s", i, actual.RefB, expected.RefB)
-		return false
-	}
-
-	if actual.Relationship != expected.Relationship {
-		t.Errorf("SBOM: Relationship type Error at index %d! Got %s: Expected %s", i, actual.Relationship, expected.Relationship)
-		return false
-	}
-	return true
-}
-
-func compareLicenses(t *testing.T, i int, actual, expected *spdx.OtherLicense) bool {
-	if actual == nil || expected == nil {
-		t.Errorf("SBOM: Licenses is nil at index %d! Got %v: Expected %v", i, actual, expected)
-		return false
-	}
-
-	if actual.LicenseName != expected.LicenseName {
-		t.Errorf("SBOM: License Name Error at index %d! Got %s: Expected %s", i, actual.LicenseName, expected.LicenseName)
-		return false
-	}
-
-	if actual.LicenseIdentifier != expected.LicenseIdentifier {
-		t.Errorf("SBOM: License Identifier Error at index %d! Got %s: Expected %s", i, actual.LicenseIdentifier, expected.LicenseIdentifier)
-		return false
-	}
-
-	if actual.ExtractedText != expected.ExtractedText {
-		t.Errorf("SBOM: License Extracted Text Error at index %d! Got: %q want: %q", i, actual.ExtractedText, expected.ExtractedText)
-		return false
-	}
-	return true
-}
-
-func fakeTime() string {
-	t := time.UnixMicro(0)
-	return t.UTC().Format("2006-01-02T15:04:05Z")
-}
diff --git a/tools/dependency_mapper/Android.bp b/tools/dependency_mapper/Android.bp
new file mode 100644
index 0000000..6763c0e
--- /dev/null
+++ b/tools/dependency_mapper/Android.bp
@@ -0,0 +1,45 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+    default_team: "trendy_team_android_crumpet",
+}
+
+java_binary_host {
+    name: "dependency-mapper",
+    main_class: "com.android.dependencymapper.Main",
+    static_libs: [
+        "dependency-mapper-host-lib",
+    ],
+    visibility: ["//visibility:public"],
+}
+
+java_library_host {
+    name: "dependency-mapper-host-lib",
+    srcs: [
+        "src/**/*.java",
+        "proto/**/*.proto",
+    ],
+    static_libs: [
+        "gson",
+        "ow2-asm",
+    ],
+}
+
+java_test_host {
+    name: "dependency-mapper-tests",
+    srcs: ["tests/src/**/*.java"],
+    static_libs: [
+        "junit",
+        "dependency-mapper-host-lib",
+    ],
+    data: [
+        "tests/res/**/*",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+}
+
+java_library {
+    name: "dependency-mapper-test-data",
+    srcs: ["tests/res/**/*.java"],
+}
diff --git a/tools/dependency_mapper/OWNERS b/tools/dependency_mapper/OWNERS
new file mode 100644
index 0000000..4477269
--- /dev/null
+++ b/tools/dependency_mapper/OWNERS
@@ -0,0 +1 @@
+himanshuz@google.com
\ No newline at end of file
diff --git a/tools/dependency_mapper/README.md b/tools/dependency_mapper/README.md
new file mode 100644
index 0000000..475aef2
--- /dev/null
+++ b/tools/dependency_mapper/README.md
@@ -0,0 +1,26 @@
+# Dependency Mapper
+
+[dependency-mapper] command line tool. This tool finds the usage based dependencies between java
+files by utilizing byte-code and java file analysis.
+
+# Getting Started
+
+## Inputs
+* rsp file, containing list of java files separated by whitespace.
+* jar file, containing class files generated after compiling the contents of rsp file.
+
+## Output
+* proto file, representing the list of dependencies for each java file present in input rsp file,
+represented by [proto/usage.proto]
+
+## Usage
+```
+dependency-mapper --src-path [src-list.rsp] --jar-path [classes.jar] --usage-map-path [usage-map.proto]"
+```
+
+# Notes
+## Dependencies enlisted are only within the java files present in input.
+## Ensure that [SourceFile] is present in the classes present in the jar.
+## To ensure dependencies are listed correctly
+* Classes jar should only contain class files generated from the source rsp files.
+* Classes jar should not exclude any class file that was generated from source rsp files.
\ No newline at end of file
diff --git a/tools/dependency_mapper/proto/dependency.proto b/tools/dependency_mapper/proto/dependency.proto
new file mode 100644
index 0000000..60a88f8
--- /dev/null
+++ b/tools/dependency_mapper/proto/dependency.proto
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.dependencymapper;
+option java_package = "com.android.dependencymapper";
+option java_outer_classname = "DependencyProto";
+
+/**
+ * A com.android.dependencymapper.DependencyProto.FileDependency object.
+ */
+
+message FileDependency {
+
+  // java file path on disk
+  optional string file_path = 1;
+  // if a change in this file warrants recompiling all files
+  optional bool is_dependency_to_all = 2;
+  // class files generated when this java file is compiled
+  repeated string generated_classes = 3;
+  // dependencies of this file.
+  repeated string file_dependencies = 4;
+}
+
+/**
+ * A com.android.dependencymapper.DependencyProto.FileDependencyList object.
+ */
+message FileDependencyList {
+
+  // List of java file usages
+  repeated FileDependency fileDependency = 1;
+}
\ No newline at end of file
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependenciesVisitor.java b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependenciesVisitor.java
new file mode 100644
index 0000000..ba65145
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependenciesVisitor.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.TypePath;
+
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An ASM based class visitor to analyze and club all dependencies of a java file.
+ * Most of the logic of this class is inspired from
+ * <a href="https://github.com/gradle/gradle/blob/master/platforms/jvm/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/asm/ClassDependenciesVisitor.java">gradle incremental compilation</a>
+ */
+public class ClassDependenciesVisitor extends ClassVisitor {
+
+    private final static int API = Opcodes.ASM9;
+
+    private final Set<String> mClassTypes;
+    private final Set<Object> mConstantsDefined;
+    private final Set<Object> mInlinedUsages;
+    private String mSource;
+    private boolean isAnnotationType;
+    private boolean mIsDependencyToAll;
+    private final RetentionPolicyVisitor retentionPolicyVisitor;
+
+    private final ClassRelevancyFilter mClassFilter;
+
+    private ClassDependenciesVisitor(ClassReader reader, ClassRelevancyFilter filter) {
+        super(API);
+        this.mClassTypes = new HashSet<>();
+        this.mConstantsDefined = new HashSet<>();
+        this.mInlinedUsages =  new HashSet<>();
+        this.retentionPolicyVisitor = new RetentionPolicyVisitor();
+        this.mClassFilter = filter;
+        collectRemainingClassDependencies(reader);
+    }
+
+    public static ClassDependencyData analyze(
+            String className, ClassReader reader, ClassRelevancyFilter filter) {
+        ClassDependenciesVisitor visitor = new ClassDependenciesVisitor(reader, filter);
+        reader.accept(visitor, ClassReader.SKIP_FRAMES);
+        // Sometimes a class may contain references to the same class, we remove such cases to
+        // prevent circular dependency.
+        visitor.getClassTypes().remove(className);
+        return new ClassDependencyData(Utils.buildPackagePrependedClassSource(
+                className, visitor.getSource()), className, visitor.getClassTypes(),
+                visitor.isDependencyToAll(), visitor.getConstantsDefined(),
+                visitor.getInlinedUsages());
+    }
+
+    @Override
+    public void visitSource(String source, String debug) {
+        mSource = source;
+    }
+
+    @Override
+    public void visit(int version, int access, String name, String signature, String superName,
+            String[] interfaces) {
+        isAnnotationType = isAnnotationType(interfaces);
+        maybeAddClassTypesFromSignature(signature, mClassTypes);
+        if (superName != null) {
+            // superName can be null if what we are analyzing is `java.lang.Object`
+            // which can happen when a custom Java SDK is on classpath (typically, android.jar)
+            Type type = Type.getObjectType(superName);
+            maybeAddClassType(mClassTypes, type);
+        }
+        for (String s : interfaces) {
+            Type interfaceType = Type.getObjectType(s);
+            maybeAddClassType(mClassTypes, interfaceType);
+        }
+    }
+
+    // performs a fast analysis of classes referenced in bytecode (method bodies)
+    // avoiding us to implement a costly visitor and potentially missing edge cases
+    private void collectRemainingClassDependencies(ClassReader reader) {
+        char[] charBuffer = new char[reader.getMaxStringLength()];
+        for (int i = 1; i < reader.getItemCount(); i++) {
+            int itemOffset = reader.getItem(i);
+            // see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
+            if (itemOffset > 0 && reader.readByte(itemOffset - 1) == 7) {
+                // A CONSTANT_Class entry, read the class descriptor
+                String classDescriptor = reader.readUTF8(itemOffset, charBuffer);
+                Type type = Type.getObjectType(classDescriptor);
+                maybeAddClassType(mClassTypes, type);
+            }
+        }
+    }
+
+    private void maybeAddClassTypesFromSignature(String signature, Set<String> types) {
+        if (signature != null) {
+            SignatureReader signatureReader = new SignatureReader(signature);
+            signatureReader.accept(new SignatureVisitor(API) {
+                @Override
+                public void visitClassType(String className) {
+                    Type type = Type.getObjectType(className);
+                    maybeAddClassType(types, type);
+                }
+            });
+        }
+    }
+
+    protected void maybeAddClassType(Set<String> types, Type type) {
+        while (type.getSort() == Type.ARRAY) {
+            type = type.getElementType();
+        }
+        if (type.getSort() != Type.OBJECT) {
+            return;
+        }
+        //String name = Utils.classPackageToFilePath(type.getClassName());
+        String name = type.getClassName();
+        if (mClassFilter.test(name)) {
+            types.add(name);
+        }
+    }
+
+    public String getSource() {
+        return mSource;
+    }
+
+    public Set<String> getClassTypes() {
+        return mClassTypes;
+    }
+
+    public Set<Object> getConstantsDefined() {
+        return mConstantsDefined;
+    }
+
+    public Set<Object> getInlinedUsages() {
+        return mInlinedUsages;
+    }
+
+    private boolean isAnnotationType(String[] interfaces) {
+        return interfaces.length == 1 && interfaces[0].equals("java/lang/annotation/Annotation");
+    }
+
+    @Override
+    public FieldVisitor visitField(
+            int access, String name, String desc, String signature, Object value) {
+        maybeAddClassTypesFromSignature(signature, mClassTypes);
+        maybeAddClassType(mClassTypes, Type.getType(desc));
+        if (isAccessibleConstant(access, value)) {
+            mConstantsDefined.add(value);
+        }
+        return new FieldVisitor(mClassTypes);
+    }
+
+    @Override
+    public MethodVisitor visitMethod(
+            int access, String name, String desc, String signature, String[] exceptions) {
+        maybeAddClassTypesFromSignature(signature, mClassTypes);
+        Type methodType = Type.getMethodType(desc);
+        maybeAddClassType(mClassTypes, methodType.getReturnType());
+        for (Type argType : methodType.getArgumentTypes()) {
+            maybeAddClassType(mClassTypes, argType);
+        }
+        return new MethodVisitor(mClassTypes);
+    }
+
+    @Override
+    public org.objectweb.asm.AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        if (isAnnotationType && "Ljava/lang/annotation/Retention;".equals(desc)) {
+            return retentionPolicyVisitor;
+        } else {
+            maybeAddClassType(mClassTypes, Type.getType(desc));
+            return new AnnotationVisitor(mClassTypes);
+        }
+    }
+
+    private static boolean isAccessible(int access) {
+        return (access & Opcodes.ACC_PRIVATE) == 0;
+    }
+
+    private static boolean isAccessibleConstant(int access, Object value) {
+        return isConstant(access) && isAccessible(access) && value != null;
+    }
+
+    private static boolean isConstant(int access) {
+        return (access & Opcodes.ACC_FINAL) != 0 && (access & Opcodes.ACC_STATIC) != 0;
+    }
+
+    public boolean isDependencyToAll() {
+        return mIsDependencyToAll;
+    }
+
+    private class FieldVisitor extends org.objectweb.asm.FieldVisitor {
+        private final Set<String> types;
+
+        public FieldVisitor(Set<String> types) {
+            super(API);
+            this.types = types;
+        }
+
+        @Override
+        public org.objectweb.asm.AnnotationVisitor visitAnnotation(
+                String descriptor, boolean visible) {
+            maybeAddClassType(types, Type.getType(descriptor));
+            return new AnnotationVisitor(types);
+        }
+
+        @Override
+        public org.objectweb.asm.AnnotationVisitor visitTypeAnnotation(int typeRef,
+                TypePath typePath, String descriptor, boolean visible) {
+            maybeAddClassType(types, Type.getType(descriptor));
+            return new AnnotationVisitor(types);
+        }
+    }
+
+    private class MethodVisitor extends org.objectweb.asm.MethodVisitor {
+        private final Set<String> types;
+
+        protected MethodVisitor(Set<String> types) {
+            super(API);
+            this.types = types;
+        }
+
+        @Override
+        public void visitLdcInsn(Object value) {
+            mInlinedUsages.add(value);
+            super.visitLdcInsn(value);
+        }
+
+        @Override
+        public void visitLocalVariable(
+                String name, String desc, String signature, Label start, Label end, int index) {
+            maybeAddClassTypesFromSignature(signature, mClassTypes);
+            maybeAddClassType(mClassTypes, Type.getType(desc));
+            super.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+
+        @Override
+        public org.objectweb.asm.AnnotationVisitor visitAnnotation(
+                String descriptor, boolean visible) {
+            maybeAddClassType(types, Type.getType(descriptor));
+            return new AnnotationVisitor(types);
+        }
+
+        @Override
+        public org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(
+                int parameter, String descriptor, boolean visible) {
+            maybeAddClassType(types, Type.getType(descriptor));
+            return new AnnotationVisitor(types);
+        }
+
+        @Override
+        public org.objectweb.asm.AnnotationVisitor visitTypeAnnotation(
+                int typeRef, TypePath typePath, String descriptor, boolean visible) {
+            maybeAddClassType(types, Type.getType(descriptor));
+            return new AnnotationVisitor(types);
+        }
+    }
+
+    private class RetentionPolicyVisitor extends org.objectweb.asm.AnnotationVisitor {
+        public RetentionPolicyVisitor() {
+            super(ClassDependenciesVisitor.API);
+        }
+
+        @Override
+        public void visitEnum(String name, String desc, String value) {
+            if ("Ljava/lang/annotation/RetentionPolicy;".equals(desc)) {
+                RetentionPolicy policy = RetentionPolicy.valueOf(value);
+                if (policy == RetentionPolicy.SOURCE) {
+                    mIsDependencyToAll = true;
+                }
+            }
+        }
+    }
+
+    private class AnnotationVisitor extends org.objectweb.asm.AnnotationVisitor {
+        private final Set<String> types;
+
+        public AnnotationVisitor(Set<String> types) {
+            super(ClassDependenciesVisitor.API);
+            this.types = types;
+        }
+
+        @Override
+        public void visit(String name, Object value) {
+            if (value instanceof Type) {
+                maybeAddClassType(types, (Type) value);
+            }
+        }
+
+        @Override
+        public org.objectweb.asm.AnnotationVisitor visitArray(String name) {
+            return this;
+        }
+
+        @Override
+        public org.objectweb.asm.AnnotationVisitor visitAnnotation(String name, String descriptor) {
+            maybeAddClassType(types, Type.getType(descriptor));
+            return this;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyAnalyzer.java b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyAnalyzer.java
new file mode 100644
index 0000000..4a37b41
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyAnalyzer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * An utility class that reads each class file present in the classes jar, then analyzes the same,
+ * collecting the dependencies in {@link List<ClassDependencyData>}
+ */
+public class ClassDependencyAnalyzer {
+
+    public static List<ClassDependencyData> analyze(Path classJar, ClassRelevancyFilter classFilter) {
+        List<ClassDependencyData> classAnalysisList = new ArrayList<>();
+        try (JarFile jarFile = new JarFile(classJar.toFile())) {
+            Enumeration<JarEntry> entries = jarFile.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry entry = entries.nextElement();
+                if (entry.getName().endsWith(".class")) {
+                    try (InputStream inputStream = jarFile.getInputStream(entry)) {
+                        String name = Utils.trimAndConvertToPackageBasedPath(entry.getName());
+                        ClassDependencyData classAnalysis = ClassDependenciesVisitor.analyze(name,
+                                new ClassReader(inputStream), classFilter);
+                        classAnalysisList.add(classAnalysis);
+                    }
+                }
+            }
+        } catch (IOException e) {
+            System.err.println("Error reading the jar file at: " + classJar);
+            throw new RuntimeException(e);
+        }
+        return classAnalysisList;
+    }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyData.java b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyData.java
new file mode 100644
index 0000000..58e388f
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyData.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import java.util.Set;
+
+/**
+ * Represents the Class Dependency Data collected via ASM analysis.
+ */
+public class ClassDependencyData {
+    private final String mPackagePrependedClassSource;
+    private final String mQualifiedName;
+    private final Set<String> mClassDependencies;
+    private final boolean mIsDependencyToAll;
+    private final Set<Object> mConstantsDefined;
+    private final Set<Object> mInlinedUsages;
+
+    public ClassDependencyData(String packagePrependedClassSource, String className,
+            Set<String> classDependencies, boolean isDependencyToAll, Set<Object> constantsDefined,
+            Set<Object> inlinedUsages) {
+        this.mPackagePrependedClassSource = packagePrependedClassSource;
+        this.mQualifiedName = className;
+        this.mClassDependencies = classDependencies;
+        this.mIsDependencyToAll = isDependencyToAll;
+        this.mConstantsDefined = constantsDefined;
+        this.mInlinedUsages = inlinedUsages;
+    }
+
+    public String getPackagePrependedClassSource() {
+        return mPackagePrependedClassSource;
+    }
+
+    public String getQualifiedName() {
+        return mQualifiedName;
+    }
+
+    public Set<String> getClassDependencies() {
+        return mClassDependencies;
+    }
+
+    public Set<Object> getConstantsDefined() {
+        return mConstantsDefined;
+    }
+
+    public Set<Object> inlinedUsages() {
+        return mInlinedUsages;
+    }
+
+    public boolean isDependencyToAll() {
+        return mIsDependencyToAll;
+    }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/ClassRelevancyFilter.java b/tools/dependency_mapper/src/com/android/dependencymapper/ClassRelevancyFilter.java
new file mode 100644
index 0000000..c46b53f
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/ClassRelevancyFilter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import java.util.Set;
+import java.util.function.Predicate;
+
+/**
+ * A filter representing the list of class files which are relevant for dependency analysis.
+ */
+public class ClassRelevancyFilter implements Predicate<String> {
+
+    private final Set<String> mAllowlistedClassNames;
+
+    public ClassRelevancyFilter(Set<String> allowlistedClassNames) {
+        this.mAllowlistedClassNames = allowlistedClassNames;
+    }
+
+    @Override
+    public boolean test(String className) {
+        return mAllowlistedClassNames.contains(className);
+    }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/DependencyMapper.java b/tools/dependency_mapper/src/com/android/dependencymapper/DependencyMapper.java
new file mode 100644
index 0000000..ecf520c
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/DependencyMapper.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import com.android.dependencymapper.DependencyProto;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class binds {@link List<ClassDependencyData>} and {@link List<JavaSourceData>} together as a
+ * flat map, which represents dependency related attributes of a java file.
+ */
+public class DependencyMapper {
+    private final List<ClassDependencyData> mClassAnalysisList;
+    private final List<JavaSourceData> mJavaSourceDataList;
+    private final Map<String, String> mClassToSourceMap = new HashMap<>();
+    private final Map<String, Set<String>> mFileDependencies = new HashMap<>();
+    private final Set<String> mDependencyToAll = new HashSet<>();
+    private final Map<String, Set<String>> mSourceToClasses = new HashMap<>();
+
+    public DependencyMapper(List<ClassDependencyData> classAnalysisList, List<JavaSourceData> javaSourceDataList) {
+        this.mClassAnalysisList = classAnalysisList;
+        this.mJavaSourceDataList = javaSourceDataList;
+    }
+
+    public DependencyProto.FileDependencyList buildDependencyMaps() {
+        buildClassDependencyMaps();
+        buildSourceToClassMap();
+        return createFileDependencies();
+    }
+
+    private void buildClassDependencyMaps() {
+        // Create a map between package appended file names and file paths.
+        Map<String, String> sourcePaths = generateSourcePaths();
+        // A map between qualified className and its dependencies
+        Map<String, Set<String>> classDependencies = new HashMap<>();
+        // A map between constant values and the their declarations.
+        Map<Object, Set<String>> constantRegistry = new HashMap<>();
+        // A map between constant values and the their inlined usages.
+        Map<Object, Set<String>> inlinedUsages = new HashMap<>();
+
+        for (ClassDependencyData analysis : mClassAnalysisList) {
+            String className = analysis.getQualifiedName();
+
+            // Compute qualified class name to source path map.
+            String sourceKey = analysis.getPackagePrependedClassSource();
+            String sourcePath = sourcePaths.get(sourceKey);
+            mClassToSourceMap.put(className, sourcePath);
+
+            // compute classDependencies
+            classDependencies.computeIfAbsent(className, k ->
+                    new HashSet<>()).addAll(analysis.getClassDependencies());
+
+            // Compute constantRegistry
+            analysis.getConstantsDefined().forEach(c ->
+                    constantRegistry.computeIfAbsent(c, k -> new HashSet<>()).add(className));
+            // Compute inlinedUsages map.
+            analysis.inlinedUsages().forEach(u ->
+                    inlinedUsages.computeIfAbsent(u, k -> new HashSet<>()).add(className));
+
+            if (analysis.isDependencyToAll()) {
+                mDependencyToAll.add(sourcePath);
+            }
+        }
+        // Finally build file dependencies
+        buildFileDependencies(
+                combineDependencies(classDependencies, inlinedUsages, constantRegistry));
+    }
+
+    private Map<String, String> generateSourcePaths() {
+        Map<String, String> sourcePaths = new HashMap<>();
+        mJavaSourceDataList.forEach(data ->
+                sourcePaths.put(data.getPackagePrependedFileName(), data.getFilePath()));
+        return sourcePaths;
+    }
+
+    private Map<String, Set<String>> combineDependencies(Map<String, Set<String>> classDependencies,
+            Map<Object, Set<String>> inlinedUsages,
+            Map<Object, Set<String>> constantRegistry) {
+        Map<String, Set<String>> combined = new HashMap<>(
+                buildConstantDependencies(inlinedUsages, constantRegistry));
+        classDependencies.forEach((k, v) ->
+                combined.computeIfAbsent(k, key -> new HashSet<>()).addAll(v));
+        return combined;
+    }
+
+    private Map<String, Set<String>> buildConstantDependencies(
+            Map<Object, Set<String>> inlinedUsages, Map<Object, Set<String>> constantRegistry) {
+        Map<String, Set<String>> constantDependencies = new HashMap<>();
+        for (Map.Entry<Object, Set<String>> usageEntry : inlinedUsages.entrySet()) {
+            Object usage = usageEntry.getKey();
+            Set<String> usageClasses = usageEntry.getValue();
+            if (constantRegistry.containsKey(usage)) {
+                Set<String> declarationClasses = constantRegistry.get(usage);
+                for (String usageClass : usageClasses) {
+                    // Sometimes Usage and Declarations are in the same file, we remove such cases
+                    // to prevent circular dependency.
+                    declarationClasses.remove(usageClass);
+                    constantDependencies.computeIfAbsent(usageClass, k ->
+                            new HashSet<>()).addAll(declarationClasses);
+                }
+            }
+        }
+
+        return constantDependencies;
+    }
+
+    private void buildFileDependencies(Map<String, Set<String>> combinedClassDependencies) {
+        combinedClassDependencies.forEach((className, dependencies) -> {
+            String sourceFile = mClassToSourceMap.get(className);
+            if (sourceFile == null) {
+                throw new IllegalArgumentException("Class '" + className
+                        + "' does not have a corresponding source file.");
+            }
+            mFileDependencies.computeIfAbsent(sourceFile, k -> new HashSet<>());
+            dependencies.forEach(dependency -> {
+                String dependencySource = mClassToSourceMap.get(dependency);
+                if (dependencySource == null) {
+                    throw new IllegalArgumentException("Dependency '" + dependency
+                            + "' does not have a corresponding source file.");
+                }
+                mFileDependencies.get(sourceFile).add(dependencySource);
+            });
+        });
+    }
+
+    private void buildSourceToClassMap() {
+        mClassToSourceMap.forEach((className, sourceFile) ->
+                mSourceToClasses.computeIfAbsent(sourceFile, k ->
+                        new HashSet<>()).add(className));
+    }
+
+    private DependencyProto.FileDependencyList createFileDependencies() {
+        List<DependencyProto.FileDependency> fileDependencies = new ArrayList<>();
+        mFileDependencies.forEach((file, dependencies) -> {
+            DependencyProto.FileDependency dependency = DependencyProto.FileDependency.newBuilder()
+                    .setFilePath(file)
+                    .setIsDependencyToAll(mDependencyToAll.contains(file))
+                    .addAllGeneratedClasses(mSourceToClasses.get(file))
+                    .addAllFileDependencies(dependencies)
+                    .build();
+            fileDependencies.add(dependency);
+        });
+        return DependencyProto.FileDependencyList.newBuilder()
+                .addAllFileDependency(fileDependencies).build();
+    }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceAnalyzer.java b/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceAnalyzer.java
new file mode 100644
index 0000000..3a4efad
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceAnalyzer.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * An utility class that reads each java file present in the rsp content then analyzes the same,
+ * collecting the analysis in {@link List<JavaSourceData>}
+ */
+public class JavaSourceAnalyzer {
+
+    // Regex that matches against "package abc.xyz.lmn;" declarations in a java file.
+    private static final String PACKAGE_REGEX = "^package\\s+([a-zA-Z_][a-zA-Z0-9_.]*);";
+
+    public static List<JavaSourceData> analyze(Path srcRspFile) {
+        List<JavaSourceData> javaSourceDataList = new ArrayList<>();
+        try (BufferedReader reader = new BufferedReader(new FileReader(srcRspFile.toFile()))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                // Split the line by spaces, tabs, multiple java files can be on a single line.
+                String[] files = line.trim().split("\\s+");
+                for (String file : files) {
+                    Path p = Paths.get("", file);
+                    System.out.println(p.toAbsolutePath().toString());
+                    javaSourceDataList
+                            .add(new JavaSourceData(file, constructPackagePrependedFileName(file)));
+                }
+            }
+        } catch (IOException e) {
+            System.err.println("Error reading rsp file at: " + srcRspFile);
+            throw new RuntimeException(e);
+        }
+        return javaSourceDataList;
+    }
+
+    private static String constructPackagePrependedFileName(String filePath) {
+        String packageAppendedFileName = null;
+        // if the file path is abc/def/ghi/JavaFile.java we extract JavaFile.java
+        String javaFileName = filePath.substring(filePath.lastIndexOf("/") + 1);
+        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
+            String line;
+            // Process each line and match against the package regex pattern.
+            while ((line = reader.readLine()) != null) {
+                Pattern pattern = Pattern.compile(PACKAGE_REGEX);
+                Matcher matcher = pattern.matcher(line);
+                if (matcher.find()) {
+                    packageAppendedFileName = matcher.group(1) + "." + javaFileName;
+                    break;
+                }
+            }
+        } catch (IOException e) {
+            System.err.println("Error reading java file at: " + filePath);
+            throw new RuntimeException(e);
+        }
+        // Should not be null
+        assert packageAppendedFileName != null;
+        return packageAppendedFileName;
+    }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceData.java b/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceData.java
new file mode 100644
index 0000000..89453d0
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceData.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+/**
+ * POJO representing the data collected from Java Source file analysis.
+ */
+public class JavaSourceData {
+
+    private final String mFilePath;
+    private final String mPackagePrependedFileName;
+
+    public JavaSourceData(String filePath, String packagePrependedFileName) {
+        mFilePath = filePath;
+        mPackagePrependedFileName = packagePrependedFileName;
+    }
+
+    public String getFilePath() {
+        return mFilePath;
+    }
+
+    public String getPackagePrependedFileName() {
+        return mPackagePrependedFileName;
+    }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/Main.java b/tools/dependency_mapper/src/com/android/dependencymapper/Main.java
new file mode 100644
index 0000000..131c931
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/Main.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import static com.android.dependencymapper.Utils.listClassesInJar;
+
+import com.android.dependencymapper.DependencyProto;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Set;
+
+public class Main {
+
+    public static void main(String[] args) throws IOException, InterruptedException {
+        try {
+            InputData input = parseAndValidateInput(args);
+            generateDependencyMap(input);
+        } catch (IllegalArgumentException e) {
+            System.err.println("Error: " + e.getMessage());
+            showUsage();
+        }
+    }
+
+    private static class InputData {
+        public Path srcList;
+        public Path classesJar;
+        public Path dependencyMapProto;
+
+        public InputData(Path srcList, Path classesJar, Path dependencyMapProto) {
+            this.srcList = srcList;
+            this.classesJar = classesJar;
+            this.dependencyMapProto = dependencyMapProto;
+        }
+    }
+
+    private static InputData parseAndValidateInput(String[] args) {
+        for (String arg : args) {
+            if ("--help".equals(arg)) {
+                showUsage();
+                System.exit(0); // Indicate successful exit after showing help
+            }
+        }
+
+        if (args.length != 6) { // Explicitly check for the correct number of arguments
+            throw new IllegalArgumentException("Incorrect number of arguments");
+        }
+
+        Path srcList = null;
+        Path classesJar = null;
+        Path dependencyMapProto = null;
+
+        for (int i = 0; i < args.length; i += 2) {
+            String arg = args[i].trim();
+            String argValue = args[i + 1].trim();
+
+            switch (arg) {
+                case "--src-path" -> srcList = Path.of(argValue);
+                case "--jar-path" -> classesJar = Path.of(argValue);
+                case "--dependency-map-path" -> dependencyMapProto = Path.of(argValue);
+                default -> throw new IllegalArgumentException("Unknown argument: " + arg);
+            }
+        }
+
+        // Validate file existence and readability
+        validateFile(srcList, "--src-path");
+        validateFile(classesJar, "--jar-path");
+
+        return new InputData(srcList, classesJar, dependencyMapProto);
+    }
+
+    private static void validateFile(Path path, String argName) {
+        if (path == null) {
+            throw new IllegalArgumentException(argName + " is required");
+        }
+        if (!Files.exists(path)) {
+            throw new IllegalArgumentException(argName + " does not exist: " + path);
+        }
+        if (!Files.isReadable(path)) {
+            throw new IllegalArgumentException(argName + " is not readable: " + path);
+        }
+    }
+
+    private static void generateDependencyMap(InputData input) {
+        // First collect all classes in the jar.
+        Set<String> classesInJar = listClassesInJar(input.classesJar);
+        // Perform dependency analysis.
+        List<ClassDependencyData> classDependencyDataList = ClassDependencyAnalyzer
+                .analyze(input.classesJar, new ClassRelevancyFilter(classesInJar));
+        // Perform java source analysis.
+        List<JavaSourceData> javaSourceDataList = JavaSourceAnalyzer.analyze(input.srcList);
+        // Collect all dependencies and map them as DependencyProto.FileDependencyList
+        DependencyMapper dp = new DependencyMapper(classDependencyDataList, javaSourceDataList);
+        DependencyProto.FileDependencyList dependencyList =  dp.buildDependencyMaps();
+
+        // Write the proto to output file
+        Utils.writeContentsToProto(dependencyList, input.dependencyMapProto);
+    }
+
+    private static void showUsage() {
+        System.err.println(
+                "Usage: dependency-mapper "
+                        + "--src-path [src-list.rsp] "
+                        + "--jar-path [classes.jar] "
+                        + "--dependency-map-path [dependency-map.proto]");
+    }
+
+}
\ No newline at end of file
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/Utils.java b/tools/dependency_mapper/src/com/android/dependencymapper/Utils.java
new file mode 100644
index 0000000..5dd5f35
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/Utils.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import com.android.dependencymapper.DependencyProto;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+public class Utils {
+
+    public static String trimAndConvertToPackageBasedPath(String fileBasedPath) {
+        // Remove ".class" from the fileBasedPath, then replace "/" with "."
+        return fileBasedPath.replaceAll("\\..*", "").replaceAll("/", ".");
+    }
+
+    public static String buildPackagePrependedClassSource(String qualifiedClassPath,
+            String classSource) {
+        // Find the location of the start of classname in the qualifiedClassPath
+        int classNameSt = qualifiedClassPath.lastIndexOf(".") + 1;
+        // Replace the classname in qualifiedClassPath with classSource
+        return qualifiedClassPath.substring(0, classNameSt) + classSource;
+    }
+
+    public static void writeContentsToJson(DependencyProto.FileDependencyList contents, Path jsonOut) {
+        Gson gson = new GsonBuilder().setPrettyPrinting().create();
+        Map<String, Set<String>> jsonMap = new HashMap<>();
+        for (DependencyProto.FileDependency fileDependency : contents.getFileDependencyList()) {
+            jsonMap.putIfAbsent(fileDependency.getFilePath(),
+                    Set.copyOf(fileDependency.getFileDependenciesList()));
+        }
+        String json = gson.toJson(jsonMap);
+        try (FileWriter file = new FileWriter(jsonOut.toFile())) {
+            file.write(json);
+        } catch (IOException e) {
+            System.err.println("Error writing json output to: " + jsonOut);
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void writeContentsToProto(DependencyProto.FileDependencyList usages, Path protoOut) {
+        try {
+            OutputStream outputStream = Files.newOutputStream(protoOut);
+            usages.writeDelimitedTo(outputStream);
+        } catch (IOException e) {
+            System.err.println("Error writing proto output to: " + protoOut);
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static Set<String> listClassesInJar(Path classesJarPath) {
+        Set<String> classes = new HashSet<>();
+        try (JarFile jarFile = new JarFile(classesJarPath.toFile())) {
+            Enumeration<JarEntry> entries = jarFile.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry entry = entries.nextElement();
+                if (entry.getName().endsWith(".class")) {
+                    String name = Utils.trimAndConvertToPackageBasedPath(entry.getName());
+                    classes.add(name);
+                }
+            }
+        } catch (IOException e) {
+            System.err.println("Error reading the jar file at: " + classesJarPath);
+            throw new RuntimeException(e);
+        }
+        return classes;
+    }
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/annotation/AnnotationUsage.java b/tools/dependency_mapper/tests/res/testdata/annotation/AnnotationUsage.java
new file mode 100644
index 0000000..bb40776
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/annotation/AnnotationUsage.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.annotation;
+
+@res.testdata.annotation.RuntimeAnnotation
+public class AnnotationUsage {
+
+    private final int mSourceAnnField;
+
+    public AnnotationUsage(@res.testdata.annotation.SourceAnnotation int sourceAnnField) {
+        mSourceAnnField = sourceAnnField;
+    }
+
+    public @res.testdata.annotation.SourceAnnotation int getSourceAnnField() {
+        return mSourceAnnField;
+    }
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/annotation/RuntimeAnnotation.java b/tools/dependency_mapper/tests/res/testdata/annotation/RuntimeAnnotation.java
new file mode 100644
index 0000000..99a6074
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/annotation/RuntimeAnnotation.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RuntimeAnnotation {
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/annotation/SourceAnnotation.java b/tools/dependency_mapper/tests/res/testdata/annotation/SourceAnnotation.java
new file mode 100644
index 0000000..dec3e83
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/annotation/SourceAnnotation.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.SOURCE)
+public @interface SourceAnnotation {
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/constants/ConstantDefinition.java b/tools/dependency_mapper/tests/res/testdata/constants/ConstantDefinition.java
new file mode 100644
index 0000000..3f0a789
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/constants/ConstantDefinition.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.constants;
+
+public class ConstantDefinition {
+    public static final String TEST_CONSTANT = "test_constant";
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/constants/ConstantUsage.java b/tools/dependency_mapper/tests/res/testdata/constants/ConstantUsage.java
new file mode 100644
index 0000000..852e4d5
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/constants/ConstantUsage.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.constants;
+
+public class ConstantUsage {
+
+    public ConstantUsage(){}
+
+    public String useConstantInMethodBody() {
+        return res.testdata.constants.ConstantDefinition.TEST_CONSTANT;
+    }
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/inheritance/BaseClass.java b/tools/dependency_mapper/tests/res/testdata/inheritance/BaseClass.java
new file mode 100644
index 0000000..3b11eb1
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/inheritance/BaseClass.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.inheritance;
+
+public class BaseClass {
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/inheritance/BaseImpl.java b/tools/dependency_mapper/tests/res/testdata/inheritance/BaseImpl.java
new file mode 100644
index 0000000..7c2698b
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/inheritance/BaseImpl.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.inheritance;
+
+public interface BaseImpl {
+
+    void baseImpl();
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/inheritance/InheritanceUsage.java b/tools/dependency_mapper/tests/res/testdata/inheritance/InheritanceUsage.java
new file mode 100644
index 0000000..f892479
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/inheritance/InheritanceUsage.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.inheritance;
+
+public class InheritanceUsage extends res.testdata.inheritance.BaseClass implements
+        res.testdata.inheritance.BaseImpl {
+    @Override
+    public void baseImpl() {
+
+    }
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/methods/FieldUsage.java b/tools/dependency_mapper/tests/res/testdata/methods/FieldUsage.java
new file mode 100644
index 0000000..0d97312
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/methods/FieldUsage.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.methods;
+
+public class FieldUsage {
+
+    private res.testdata.methods.ReferenceClass1 mReferenceClass1;
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/methods/MethodUsage.java b/tools/dependency_mapper/tests/res/testdata/methods/MethodUsage.java
new file mode 100644
index 0000000..9dd0223
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/methods/MethodUsage.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.methods;
+
+public class MethodUsage {
+
+    public void methodReferences(res.testdata.methods.ReferenceClass1 mReferenceClass1) {
+        res.testdata.methods.ReferenceClass2 referenceClass2 =
+                new res.testdata.methods.ReferenceClass2();
+    }
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass1.java b/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass1.java
new file mode 100644
index 0000000..f56c0a9
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass1.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.methods;
+
+public class ReferenceClass1 {
+
+    public ReferenceClass1(){}
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass2.java b/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass2.java
new file mode 100644
index 0000000..09e7422
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass2.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package res.testdata.methods;
+
+public class ReferenceClass2 {
+    public ReferenceClass2(){}
+}
diff --git a/tools/dependency_mapper/tests/res/testfiles/dependency-mapper-test-data.jar b/tools/dependency_mapper/tests/res/testfiles/dependency-mapper-test-data.jar
new file mode 100644
index 0000000..98f5893
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testfiles/dependency-mapper-test-data.jar
Binary files differ
diff --git a/tools/dependency_mapper/tests/res/testfiles/sources.rsp b/tools/dependency_mapper/tests/res/testfiles/sources.rsp
new file mode 100644
index 0000000..d895033
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testfiles/sources.rsp
@@ -0,0 +1,12 @@
+tests/res/testdata/annotation/AnnotationUsage.java
+tests/res/testdata/annotation/SourceAnnotation.java
+tests/res/testdata/annotation/RuntimeAnnotation.java
+tests/res/testdata/constants/ConstantDefinition.java
+tests/res/testdata/constants/ConstantUsage.java
+tests/res/testdata/inheritance/InheritanceUsage.java
+tests/res/testdata/inheritance/BaseClass.java
+tests/res/testdata/inheritance/BaseImpl.java
+tests/res/testdata/methods/FieldUsage.java
+tests/res/testdata/methods/MethodUsage.java
+tests/res/testdata/methods/ReferenceClass1.java
+tests/res/testdata/methods/ReferenceClass2.java
\ No newline at end of file
diff --git a/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassDependencyAnalyzerTest.java b/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassDependencyAnalyzerTest.java
new file mode 100644
index 0000000..95492c8
--- /dev/null
+++ b/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassDependencyAnalyzerTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import static com.android.dependencymapper.Utils.listClassesInJar;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ClassDependencyAnalyzerTest {
+
+    private static List<ClassDependencyData> mClassDependencyDataList;
+
+    private static final String CLASSES_JAR_PATH =
+            "tests/res/testfiles/dependency-mapper-test-data.jar";
+
+    @BeforeClass
+    public static void beforeClass() throws URISyntaxException {
+        Path path = Paths.get(CLASSES_JAR_PATH);
+        Set<String> classesInJar = listClassesInJar(path);
+        // Perform dependency analysis.
+        mClassDependencyDataList = ClassDependencyAnalyzer.analyze(path,
+                new ClassRelevancyFilter(classesInJar));
+    }
+
+    @Test
+    public void testAnnotationDeps(){
+        String annoClass = "res.testdata.annotation.AnnotationUsage";
+        String sourceAnno = "res.testdata.annotation.SourceAnnotation";
+        String runTimeAnno = "res.testdata.annotation.RuntimeAnnotation";
+
+        dependencyVerifier(annoClass,
+                new HashSet<>(List.of(runTimeAnno)), new HashSet<>(List.of(sourceAnno)));
+
+        for (ClassDependencyData dep : mClassDependencyDataList) {
+            if (dep.getQualifiedName().equals(sourceAnno)) {
+                assertTrue(sourceAnno + " is not dependencyToAll ", dep.isDependencyToAll());
+            }
+            if (dep.getQualifiedName().equals(runTimeAnno)) {
+                assertFalse(runTimeAnno + " is dependencyToAll ", dep.isDependencyToAll());
+            }
+        }
+    }
+
+    @Test
+    public void testConstantsDeps(){
+        String constDefined = "test_constant";
+        String constDefClass = "res.testdata.constants.ConstantDefinition";
+        String constUsageClass = "res.testdata.constants.ConstantUsage";
+
+        boolean constUsageClassFound = false;
+        boolean constDefClassFound = false;
+        for (ClassDependencyData dep : mClassDependencyDataList) {
+            if (dep.getQualifiedName().equals(constUsageClass)) {
+                constUsageClassFound = true;
+                assertTrue("InlinedUsage of : " + constDefined + " not found",
+                        dep.inlinedUsages().contains(constDefined));
+            }
+            if (dep.getQualifiedName().equals(constDefClass)) {
+                constDefClassFound = true;
+                assertTrue("Constant " + constDefined + " not defined",
+                        dep.getConstantsDefined().contains(constDefined));
+            }
+        }
+        assertTrue("Class " + constUsageClass + " not found", constUsageClassFound);
+        assertTrue("Class " + constDefClass + " not found", constDefClassFound);
+    }
+
+    @Test
+    public void testInheritanceDeps(){
+        String sourceClass = "res.testdata.inheritance.InheritanceUsage";
+        String baseClass = "res.testdata.inheritance.BaseClass";
+        String baseImpl = "res.testdata.inheritance.BaseImpl";
+
+        dependencyVerifier(sourceClass,
+                new HashSet<>(List.of(baseClass, baseImpl)), new HashSet<>());
+    }
+
+
+    @Test
+    public void testMethodDeps(){
+        String fieldUsage = "res.testdata.methods.FieldUsage";
+        String methodUsage = "res.testdata.methods.MethodUsage";
+        String ref1 = "res.testdata.methods.ReferenceClass1";
+        String ref2 = "res.testdata.methods.ReferenceClass2";
+
+        dependencyVerifier(fieldUsage,
+                new HashSet<>(List.of(ref1)), new HashSet<>(List.of(ref2)));
+        dependencyVerifier(methodUsage,
+                new HashSet<>(List.of(ref1, ref2)), new HashSet<>());
+    }
+
+    private void dependencyVerifier(String qualifiedName, Set<String> deps, Set<String> nonDeps) {
+        boolean depFound = false;
+        for (ClassDependencyData classDependencyData : mClassDependencyDataList) {
+            if (classDependencyData.getQualifiedName().equals(qualifiedName)) {
+                depFound = true;
+                for (String dep : deps) {
+                    assertTrue(qualifiedName + " does not depends on " + dep,
+                            classDependencyData.getClassDependencies().contains(dep));
+                }
+                for (String nonDep : nonDeps) {
+                    assertFalse(qualifiedName + " depends on " + nonDep,
+                            classDependencyData.getClassDependencies().contains(nonDep));
+                }
+            }
+        }
+        assertTrue("Class " + qualifiedName + " not found", depFound);
+    }
+}
diff --git a/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassRelevancyFilterTest.java b/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassRelevancyFilterTest.java
new file mode 100644
index 0000000..9a80c4b
--- /dev/null
+++ b/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassRelevancyFilterTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import static com.android.dependencymapper.Utils.listClassesInJar;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.dependencymapper.ClassDependencyAnalyzer;
+import com.android.dependencymapper.ClassDependencyData;
+import com.android.dependencymapper.ClassRelevancyFilter;
+
+import org.junit.Test;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Set;
+
+public class ClassRelevancyFilterTest {
+
+    private static final String CLASSES_JAR_PATH =
+            "tests/res/testfiles/dependency-mapper-test-data.jar";
+
+    @Test
+    public void testClassRelevancyFilter() {
+        Path path = Paths.get(CLASSES_JAR_PATH);
+        Set<String> classesInJar = listClassesInJar(path);
+
+        // Add a relevancy filter that skips a class.
+        String skippedClass = "res.testdata.BaseClass";
+        classesInJar.remove(skippedClass);
+
+        // Perform dependency analysis.
+        List<ClassDependencyData> classDependencyDataList =
+                ClassDependencyAnalyzer.analyze(path, new ClassRelevancyFilter(classesInJar));
+
+        // check that the skipped class is not present in classDepsList
+        for (ClassDependencyData dep : classDependencyDataList) {
+            assertNotEquals("SkippedClass " + skippedClass + " is present",
+                    skippedClass, dep.getQualifiedName());
+            assertFalse("SkippedClass " + skippedClass + " is present as dependency of " + dep,
+                    dep.getClassDependencies().contains(skippedClass));
+        }
+    }
+}
diff --git a/tools/dependency_mapper/tests/src/com/android/dependencymapper/DependencyMapperTest.java b/tools/dependency_mapper/tests/src/com/android/dependencymapper/DependencyMapperTest.java
new file mode 100644
index 0000000..9c08e79
--- /dev/null
+++ b/tools/dependency_mapper/tests/src/com/android/dependencymapper/DependencyMapperTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+public class DependencyMapperTest {
+
+    private static final List<JavaSourceData> mJavaSourceData = new ArrayList<>();
+    private static final List<ClassDependencyData> mClassDependencyData = new ArrayList<>();
+
+    private static Map<String, DependencyProto.FileDependency>  mFileDependencyMap;
+
+    public static String AUDIO_CONS = "AUDIO_CONS";
+    public static String AUDIO_CONS_PATH = "frameworks/base/audio/AudioPermission.java";
+    public static String AUDIO_CONS_PACKAGE = "com.android.audio.AudioPermission";
+
+    public static String AUDIO_TONE_CONS_1 = "AUDIO_TONE_CONS_1";
+    public static String AUDIO_TONE_CONS_2 = "AUDIO_TONE_CONS_2";
+    public static String AUDIO_TONE_CONS_PATH = "frameworks/base/audio/Audio$Tones.java";
+    public static String AUDIO_TONE_CONS_PACKAGE = "com.android.audio.Audio$Tones";
+
+    public static String ST_MANAGER_PATH = "frameworks/base/core/storage/StorageManager.java";
+    public static String ST_MANAGER_PACKAGE = "com.android.storage.StorageManager";
+
+    public static String CONST_OUTSIDE_SCOPE = "CONST_OUTSIDE_SCOPE";
+    public static String PERM_MANAGER_PATH =  "frameworks/base/core/permission/PermissionManager.java";
+    public static String PERM_MANAGER_PACKAGE =  "com.android.permission.PermissionManager";
+
+    public static String SOURCE_ANNO_PATH = "frameworks/base/anno/SourceAnno.java";
+    public static String SOURCE_ANNO_PACKAGE = "com.android.anno.SourceAnno";
+
+    public static String PERM_SOURCE_PATH = "frameworks/base/core/permission/PermissionSources.java";
+    public static String PERM_SOURCE_PACKAGE = "com.android.permission.PermissionSources";
+
+    public static String PERM_DATA_PATH = "frameworks/base/core/permission/PermissionSources$Data.java";
+    public static String PERM_DATA_PACKAGE = "com.android.permission.PermissionSources$Data";
+
+    static {
+        JavaSourceData audioConstants = new JavaSourceData(AUDIO_CONS_PATH, AUDIO_CONS_PACKAGE + ".java");
+        JavaSourceData audioToneConstants =
+                new JavaSourceData(AUDIO_TONE_CONS_PATH, AUDIO_TONE_CONS_PACKAGE + ".java"); //f2
+        JavaSourceData stManager = new JavaSourceData( ST_MANAGER_PATH, ST_MANAGER_PACKAGE + ".java");
+        JavaSourceData permManager = new JavaSourceData(PERM_MANAGER_PATH, PERM_MANAGER_PACKAGE + ".java");
+        JavaSourceData permSource = new JavaSourceData(PERM_SOURCE_PATH, PERM_SOURCE_PACKAGE + ".java");
+        JavaSourceData permSourceData = new JavaSourceData(PERM_DATA_PATH, PERM_DATA_PACKAGE + ".java");
+
+        JavaSourceData sourceNotPresentInClass =
+                new JavaSourceData(SOURCE_ANNO_PATH, SOURCE_ANNO_PACKAGE);
+
+        mJavaSourceData.addAll(List.of(audioConstants, audioToneConstants, stManager,
+                permManager, permSource, permSourceData, sourceNotPresentInClass));
+
+        ClassDependencyData audioConstantsDeps =
+                new ClassDependencyData(AUDIO_CONS_PACKAGE + ".java",
+                        AUDIO_CONS_PACKAGE, new HashSet<>(), false,
+                        new HashSet<>(List.of(AUDIO_CONS)), new HashSet<>());
+
+        ClassDependencyData audioToneConstantsDeps =
+                new ClassDependencyData(AUDIO_TONE_CONS_PACKAGE + ".java",
+                        AUDIO_TONE_CONS_PACKAGE, new HashSet<>(), false,
+                        new HashSet<>(List.of(AUDIO_TONE_CONS_1, AUDIO_TONE_CONS_2)),
+                        new HashSet<>());
+
+        ClassDependencyData stManagerDeps =
+                new ClassDependencyData(ST_MANAGER_PACKAGE + ".java",
+                        ST_MANAGER_PACKAGE, new HashSet<>(List.of(PERM_SOURCE_PACKAGE)), false,
+                        new HashSet<>(), new HashSet<>(List.of(AUDIO_CONS, AUDIO_TONE_CONS_1)));
+
+        ClassDependencyData permManagerDeps =
+                new ClassDependencyData(PERM_MANAGER_PACKAGE + ".java", PERM_MANAGER_PACKAGE,
+                        new HashSet<>(List.of(PERM_SOURCE_PACKAGE, PERM_DATA_PACKAGE)), false,
+                        new HashSet<>(), new HashSet<>(List.of(CONST_OUTSIDE_SCOPE)));
+
+        ClassDependencyData permSourceDeps =
+                new ClassDependencyData(PERM_SOURCE_PACKAGE + ".java",
+                        PERM_SOURCE_PACKAGE, new HashSet<>(), false,
+                        new HashSet<>(), new HashSet<>());
+
+        ClassDependencyData permSourceDataDeps =
+                new ClassDependencyData(PERM_DATA_PACKAGE + ".java",
+                        PERM_DATA_PACKAGE, new HashSet<>(), false,
+                        new HashSet<>(), new HashSet<>());
+
+        mClassDependencyData.addAll(List.of(audioConstantsDeps, audioToneConstantsDeps,
+                stManagerDeps, permManagerDeps, permSourceDeps, permSourceDataDeps));
+    }
+
+    @BeforeClass
+    public static void beforeAll(){
+        mFileDependencyMap = buildActualDepsMap(
+                new DependencyMapper(mClassDependencyData, mJavaSourceData).buildDependencyMaps());
+    }
+
+    @Test
+    public void testFileDependencies() {
+        // Test for AUDIO_CONS_PATH
+        DependencyProto.FileDependency audioDepsActual = mFileDependencyMap.get(AUDIO_CONS_PATH);
+        assertNotNull(AUDIO_CONS_PATH + " not found in dependencyList", audioDepsActual);
+        // This file should have 0 dependencies.
+        validateDependencies(audioDepsActual, AUDIO_CONS_PATH, 0, new ArrayList<>());
+
+        // Test for AUDIO_TONE_CONS_PATH
+        DependencyProto.FileDependency audioToneDepsActual =
+                mFileDependencyMap.get(AUDIO_TONE_CONS_PATH);
+        assertNotNull(AUDIO_TONE_CONS_PATH + " not found in dependencyList", audioDepsActual);
+        // This file should have 0 dependencies.
+        validateDependencies(audioToneDepsActual, AUDIO_TONE_CONS_PATH, 0, new ArrayList<>());
+
+        // Test for ST_MANAGER_PATH
+        DependencyProto.FileDependency stManagerDepsActual =
+                mFileDependencyMap.get(ST_MANAGER_PATH);
+        assertNotNull(ST_MANAGER_PATH + " not found in dependencyList", audioDepsActual);
+        // This file should have 3 dependencies.
+        validateDependencies(stManagerDepsActual, ST_MANAGER_PATH, 3,
+                new ArrayList<>(List.of(AUDIO_CONS_PATH, AUDIO_TONE_CONS_PATH, PERM_SOURCE_PATH)));
+
+        // Test for PERM_MANAGER_PATH
+        DependencyProto.FileDependency permManagerDepsActual =
+                mFileDependencyMap.get(PERM_MANAGER_PATH);
+        assertNotNull(PERM_MANAGER_PATH + " not found in dependencyList", audioDepsActual);
+        // This file should have 2 dependencies.
+        validateDependencies(permManagerDepsActual, PERM_MANAGER_PATH, 2,
+                new ArrayList<>(List.of(PERM_SOURCE_PATH, PERM_DATA_PATH)));
+
+        // Test for PERM_SOURCE_PATH
+        DependencyProto.FileDependency permSourceDepsActual =
+                mFileDependencyMap.get(PERM_SOURCE_PATH);
+        assertNotNull(PERM_SOURCE_PATH + " not found in dependencyList", audioDepsActual);
+        // This file should have 0 dependencies.
+        validateDependencies(permSourceDepsActual, PERM_SOURCE_PATH, 0, new ArrayList<>());
+
+        // Test for PERM_DATA_PATH
+        DependencyProto.FileDependency permDataDepsActual =
+                mFileDependencyMap.get(PERM_DATA_PATH);
+        assertNotNull(PERM_DATA_PATH + " not found in dependencyList", audioDepsActual);
+        // This file should have 0 dependencies.
+        validateDependencies(permDataDepsActual, PERM_DATA_PATH, 0, new ArrayList<>());
+    }
+
+    private void validateDependencies(DependencyProto.FileDependency dependency, String fileName, int fileDepsCount, List<String> fileDeps) {
+        assertEquals(fileName + " does not have expected dependencies", fileDepsCount, dependency.getFileDependenciesCount());
+        assertTrue(fileName + " does not have expected dependencies", dependency.getFileDependenciesList().containsAll(fileDeps));
+    }
+
+    private static Map<String, DependencyProto.FileDependency> buildActualDepsMap(
+            DependencyProto.FileDependencyList fileDependencyList) {
+        Map<String, DependencyProto.FileDependency> dependencyMap = new HashMap<>();
+        for (DependencyProto.FileDependency fileDependency : fileDependencyList.getFileDependencyList()) {
+            if (fileDependency.getFilePath().equals(AUDIO_CONS_PATH)) {
+                dependencyMap.put(AUDIO_CONS_PATH, fileDependency);
+            }
+            if (fileDependency.getFilePath().equals(AUDIO_TONE_CONS_PATH)) {
+                dependencyMap.put(AUDIO_TONE_CONS_PATH, fileDependency);
+            }
+            if (fileDependency.getFilePath().equals(ST_MANAGER_PATH)) {
+                dependencyMap.put(ST_MANAGER_PATH, fileDependency);
+            }
+            if (fileDependency.getFilePath().equals(PERM_MANAGER_PATH)) {
+                dependencyMap.put(PERM_MANAGER_PATH, fileDependency);
+            }
+            if (fileDependency.getFilePath().equals(PERM_SOURCE_PATH)) {
+                dependencyMap.put(PERM_SOURCE_PATH, fileDependency);
+            }
+            if (fileDependency.getFilePath().equals(PERM_DATA_PATH)) {
+                dependencyMap.put(PERM_DATA_PATH, fileDependency);
+            }
+            if (fileDependency.getFilePath().equals(SOURCE_ANNO_PATH)) {
+                dependencyMap.put(SOURCE_ANNO_PATH, fileDependency);
+            }
+        }
+        assertFalse(SOURCE_ANNO_PATH + " found in dependencyList",
+                dependencyMap.containsKey(SOURCE_ANNO_PATH));
+        return dependencyMap;
+    }
+}
diff --git a/tools/dependency_mapper/tests/src/com/android/dependencymapper/JavaSourceAnalyzerTest.java b/tools/dependency_mapper/tests/src/com/android/dependencymapper/JavaSourceAnalyzerTest.java
new file mode 100644
index 0000000..1ca2b2a
--- /dev/null
+++ b/tools/dependency_mapper/tests/src/com/android/dependencymapper/JavaSourceAnalyzerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class JavaSourceAnalyzerTest {
+    private static List<JavaSourceData> mJavaSourceDataList;
+
+    private static final String SOURCES_RSP_PATH =
+            "tests/res/testfiles/sources.rsp";
+
+    @BeforeClass
+    public static void beforeClass() throws URISyntaxException {
+        Path path = Paths.get(SOURCES_RSP_PATH);
+        // Perform source analysis.
+        mJavaSourceDataList = JavaSourceAnalyzer.analyze(path);
+    }
+
+    @Test
+    public void validateSourceData() {
+        Map<String, String> expectedSourceData = expectedSourceData();
+        int expectedFileCount = expectedSourceData.size();
+        int actualFileCount = 0;
+        for (JavaSourceData javaSourceData : mJavaSourceDataList) {
+            String file =  javaSourceData.getFilePath();
+            if (expectedSourceData.containsKey(file)) {
+                actualFileCount++;
+                assertEquals("Source Data not generated correctly for " + file,
+                        expectedSourceData.get(file), javaSourceData.getPackagePrependedFileName());
+            }
+        }
+        assertEquals("Not all source files processed", expectedFileCount, actualFileCount);
+    }
+
+    private Map<String, String> expectedSourceData() {
+        Map<String, String> expectedSourceData = new HashMap<>();
+        expectedSourceData.put("tests/res/testdata/annotation/AnnotationUsage.java",
+                "res.testdata.annotation.AnnotationUsage.java");
+        expectedSourceData.put("tests/res/testdata/constants/ConstantUsage.java",
+                "res.testdata.constants.ConstantUsage.java");
+        expectedSourceData.put("tests/res/testdata/inheritance/BaseClass.java",
+                "res.testdata.inheritance.BaseClass.java");
+        expectedSourceData.put("tests/res/testdata/methods/FieldUsage.java",
+                "res.testdata.methods.FieldUsage.java");
+        return expectedSourceData;
+    }
+}
diff --git a/tools/dependency_mapper/tests/src/com/android/dependencymapper/UtilsTest.java b/tools/dependency_mapper/tests/src/com/android/dependencymapper/UtilsTest.java
new file mode 100644
index 0000000..39c5190
--- /dev/null
+++ b/tools/dependency_mapper/tests/src/com/android/dependencymapper/UtilsTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dependencymapper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.dependencymapper.Utils;
+
+public class UtilsTest {
+
+    @Test
+    public void testTrimAndConvertToPackageBasedPath() {
+        String testPath1 = "com/android/storage/StorageManager.class";
+        String testPath2 = "com/android/package/PackageManager$Package.class";
+
+        String expectedPackageBasedPath1 = "com.android.storage.StorageManager";
+        String expectedPackageBasedPath2 = "com.android.package.PackageManager$Package";
+
+        assertEquals("Package Based Path not constructed correctly",
+                expectedPackageBasedPath1, Utils.trimAndConvertToPackageBasedPath(testPath1));
+        assertEquals("Package Based Path not constructed correctly",
+                expectedPackageBasedPath2, Utils.trimAndConvertToPackageBasedPath(testPath2));
+    }
+
+    @Test
+    public void testBuildPackagePrependedClassSource() {
+        String qualifiedClassPath1 = "com.android.storage.StorageManager";
+        String sourcePath1 = "StorageManager.java";
+        String qualifiedClassPath2 = "com.android.package.PackageManager$Package";
+        String sourcePath2 = "PackageManager.java";
+        String qualifiedClassPath3 = "com.android.storage.StorageManager$Storage";
+        String sourcePath3 = "StorageManager$Storage.java";
+
+
+        String expectedPackagePrependedPath1 = "com.android.storage.StorageManager.java";
+        String expectedPackagePrependedPath2 = "com.android.package.PackageManager.java";
+        String expectedPackagePrependedPath3 = "com.android.storage.StorageManager$Storage.java";
+
+        assertEquals("Package Prepended Class Source not constructed correctly",
+                expectedPackagePrependedPath1,
+                Utils.buildPackagePrependedClassSource(qualifiedClassPath1, sourcePath1));
+        assertEquals("Package Prepended Class Source not constructed correctly",
+                expectedPackagePrependedPath2,
+                Utils.buildPackagePrependedClassSource(qualifiedClassPath2, sourcePath2));
+        assertEquals("Package Prepended Class Source not constructed correctly",
+                expectedPackagePrependedPath3,
+                Utils.buildPackagePrependedClassSource(qualifiedClassPath3, sourcePath3));
+    }
+}
diff --git a/tools/perf/benchmarks b/tools/perf/benchmarks
index 228c1d0..8cb26c8 100755
--- a/tools/perf/benchmarks
+++ b/tools/perf/benchmarks
@@ -573,7 +573,7 @@
 
         # --dist-one requires that only one benchmark be supplied
         if self._args.dist_one and len(self.Benchmarks()) != 1:
-            self._error("--dist-one requires that exactly one --benchmark.")
+            self._error("--dist-one requires exactly one --benchmark.")
 
         if self._had_error:
             raise FatalError()
diff --git a/tools/product_config/TEST_MAPPING b/tools/product_config/TEST_MAPPING
deleted file mode 100644
index d3568f1..0000000
--- a/tools/product_config/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "product_config_test"
-    }
-  ]
-}
diff --git a/tools/protos/Android.bp b/tools/protos/Android.bp
index c6ad19e..65f13cb 100644
--- a/tools/protos/Android.bp
+++ b/tools/protos/Android.bp
@@ -18,11 +18,6 @@
 
 python_library_host {
     name: "metadata_file_proto_py",
-    version: {
-        py3: {
-            enabled: true,
-        },
-    },
     srcs: [
         "metadata_file.proto",
     ],
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index e371b23..3467152 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -296,11 +296,6 @@
 
 python_defaults {
     name: "releasetools_binary_defaults",
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     // TODO (b/140144201) Build imgdiff from releasetools_common
     required: [
         "aapt2",
@@ -338,11 +333,6 @@
 
 python_binary_host {
     name: "merge_ota",
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     srcs: [
         "merge_ota.py",
     ],
@@ -357,11 +347,6 @@
 
 python_binary_host {
     name: "create_brick_ota",
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     srcs: [
         "create_brick_ota.py",
     ],
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 30a6acc..180bf15 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -572,7 +572,7 @@
 
   default = os.path.join(OPTIONS.input_tmp, "IMAGES", partition_name + ".img")
   assert os.path.exists(default), \
-      "There should be one %s.img" % (partition_name)
+      "Can't find %s for image %s" % (default, partition_name)
   return default
 
 
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index b6c96c4..08b4d6a 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -49,8 +49,8 @@
 # Use a fixed timestamp (01/01/2009 00:00:00 UTC) for files when packaging
 # images. (b/24377993, b/80600931)
 FIXED_FILE_TIMESTAMP = int((
-    datetime.datetime(2009, 1, 1, 0, 0, 0, 0, None) -
-    datetime.datetime.utcfromtimestamp(0)).total_seconds())
+    datetime.datetime(2009, 1, 1, 0, 0, 0, 0, datetime.UTC) -
+    datetime.datetime.fromtimestamp(0, datetime.UTC)).total_seconds())
 
 
 class BuildImageError(Exception):
diff --git a/tools/releasetools/check_partition_sizes.py b/tools/releasetools/check_partition_sizes.py
index 738d77d..5d7dd8c 100644
--- a/tools/releasetools/check_partition_sizes.py
+++ b/tools/releasetools/check_partition_sizes.py
@@ -57,7 +57,8 @@
       logger.info("%s is less than or equal to %s:\n%s == %d <= %s == %d",
                   *format_args)
     else:
-      msg = "{} is greater than {}:\n{} == {} > {} == {}".format(*format_args)
+      msg = ("If setting \"SOONG_RUSTC_INCREMENTAL\" try building without it. "
+             "{} is greater than {}:\n{} == {} > {} == {}".format(*format_args))
       if level == logging.ERROR:
         raise RuntimeError(msg)
       else:
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index b6cbb15..3fc08c6 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -1410,7 +1410,22 @@
   return errors
 
 
-def RunHostInitVerifier(product_out, partition_map):
+def RunVendoredHostInitVerifier(product_out, partition_map):
+  """Runs vendor host_init_verifier on the init rc files within selected partitions.
+
+  host_init_verifier searches the etc/init path within each selected partition.
+
+  Args:
+    product_out: PRODUCT_OUT directory, containing partition directories.
+    partition_map: A map of partition name -> relative path within product_out.
+  """
+  return RunHostInitVerifier(
+      product_out,
+      partition_map,
+      tool=os.path.join(OPTIONS.vendor_otatools, 'bin', 'host_init_verifier'))
+
+
+def RunHostInitVerifier(product_out, partition_map, tool="host_init_verifier"):
   """Runs host_init_verifier on the init rc files within partitions.
 
   host_init_verifier searches the etc/init path within each partition.
@@ -1418,9 +1433,10 @@
   Args:
     product_out: PRODUCT_OUT directory, containing partition directories.
     partition_map: A map of partition name -> relative path within product_out.
+    tool: Full path to host_init_verifier or binary name
   """
   allowed_partitions = ("system", "system_ext", "product", "vendor", "odm")
-  cmd = ["host_init_verifier"]
+  cmd = [tool]
   for partition, path in partition_map.items():
     if partition not in allowed_partitions:
       raise ExternalError("Unable to call host_init_verifier for partition %s" %
@@ -2993,7 +3009,7 @@
     os.chmod(filename, perms)
 
     # Use a fixed timestamp so the output is repeatable.
-    # Note: Use of fromtimestamp rather than utcfromtimestamp here is
+    # Note: Use of fromtimestamp without specifying a timezone here is
     # intentional. zip stores datetimes in local time without a time zone
     # attached, so we need "epoch" but in the local time zone to get 2009/01/01
     # in the zip archive.
diff --git a/tools/releasetools/merge/merge_compatibility_checks.py b/tools/releasetools/merge/merge_compatibility_checks.py
index 8c9993f..80b5caa 100644
--- a/tools/releasetools/merge/merge_compatibility_checks.py
+++ b/tools/releasetools/merge/merge_compatibility_checks.py
@@ -95,8 +95,19 @@
 def CheckInitRcFiles(target_files_dir, partition_map):
   """Check for any init.rc issues using host_init_verifier."""
   try:
+    vendor_partitions = set()
+    if OPTIONS.vendor_otatools:
+      vendor_partitions = {"vendor", "odm"}
+      common.RunVendoredHostInitVerifier(
+          product_out=target_files_dir,
+          partition_map={p: partition_map[p] for p in vendor_partitions})
+
     common.RunHostInitVerifier(
-        product_out=target_files_dir, partition_map=partition_map)
+        product_out=target_files_dir,
+        partition_map={
+            p: partition_map[p]
+            for p in partition_map.keys() - vendor_partitions
+        })
   except RuntimeError as err:
     return [str(err)]
   return []
diff --git a/tools/releasetools/merge/merge_target_files.py b/tools/releasetools/merge/merge_target_files.py
index fdba927..de4d9a8 100755
--- a/tools/releasetools/merge/merge_target_files.py
+++ b/tools/releasetools/merge/merge_target_files.py
@@ -87,8 +87,8 @@
       If provided, rebuilds odm.img or vendor.img to include merged sepolicy
       files. If odm is present then odm is preferred.
 
-  --vendor-otatools otatools.zip
-      If provided, use this otatools.zip when recompiling the odm or vendor
+  --vendor-otatools otatools.zip or directory
+      If provided, use these otatools when recompiling the odm or vendor
       image to include sepolicy.
 
   --keep-tmp
@@ -312,12 +312,9 @@
       '%s recompilation will be performed using the vendor otatools.zip',
       partition_img)
 
-  # Unzip the vendor build's otatools.zip and target-files archive.
-  vendor_otatools_dir = common.MakeTempDir(
-      prefix='merge_target_files_vendor_otatools_')
+  # Unzip the vendor build's target-files archive.
   vendor_target_files_dir = common.MakeTempDir(
       prefix='merge_target_files_vendor_target_files_')
-  common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
   merge_utils.CollectTargetFiles(
       input_zipfile_or_dir=OPTIONS.vendor_target_files,
       output_dir=vendor_target_files_dir,
@@ -335,7 +332,7 @@
   remove_file_if_exists(
       os.path.join(vendor_target_files_dir, 'IMAGES', partition_img))
   rebuild_partition_command = [
-      os.path.join(vendor_otatools_dir, 'bin', 'add_img_to_target_files'),
+      os.path.join(OPTIONS.vendor_otatools, 'bin', 'add_img_to_target_files'),
       '--verbose',
       '--add_missing',
   ]
@@ -669,6 +666,12 @@
   if OPTIONS.output_item_list:
     OPTIONS.output_item_list = common.LoadListFromFile(OPTIONS.output_item_list)
 
+  if OPTIONS.vendor_otatools and zipfile.is_zipfile(OPTIONS.vendor_otatools):
+    vendor_otatools_dir = common.MakeTempDir(
+        prefix='merge_target_files_vendor_otatools_')
+    common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
+    OPTIONS.vendor_otatools = vendor_otatools_dir
+
   if not merge_utils.ValidateConfigLists():
     sys.exit(1)
 
diff --git a/tools/sbom/Android.bp b/tools/sbom/Android.bp
index 7e2840f..d2e6b55 100644
--- a/tools/sbom/Android.bp
+++ b/tools/sbom/Android.bp
@@ -21,11 +21,6 @@
     srcs: [
         "generate-sbom.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     libs: [
         "metadata_file_proto_py",
         "libprotobuf-python",
@@ -45,11 +40,6 @@
     srcs: [
         "gen_sbom.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     libs: [
         "compliance_metadata",
         "metadata_file_proto_py",
@@ -78,11 +68,6 @@
     libs: [
         "sbom_lib",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     test_suites: ["general-tests"],
 }
 
@@ -95,11 +80,6 @@
     libs: [
         "sbom_lib",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     test_suites: ["general-tests"],
 }
 
@@ -108,11 +88,6 @@
     srcs: [
         "generate-sbom-framework_res.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     libs: [
         "sbom_lib",
     ],
@@ -123,11 +98,6 @@
     srcs: [
         "gen_notice_xml.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     libs: [
         "compliance_metadata",
         "metadata_file_proto_py",
diff --git a/tools/sbom/compliance_metadata.py b/tools/sbom/compliance_metadata.py
index 502c110..2f0b180 100644
--- a/tools/sbom/compliance_metadata.py
+++ b/tools/sbom/compliance_metadata.py
@@ -123,7 +123,7 @@
 
   def get_installed_files(self):
     # Get all records from table make_metadata, which contains all installed files and corresponding make modules' metadata
-    cursor = self.conn.execute('select installed_file, module_path, is_prebuilt_make_module, product_copy_files, kernel_module_copy_files, is_platform_generated, license_text from make_metadata')
+    cursor = self.conn.execute('select installed_file, module_path, is_soong_module, is_prebuilt_make_module, product_copy_files, kernel_module_copy_files, is_platform_generated, license_text from make_metadata')
     rows = cursor.fetchall()
     cursor.close()
     installed_files_metadata = []
@@ -135,7 +135,7 @@
   def get_installed_file_in_dir(self, dir):
     dir = dir.removesuffix('/')
     cursor = self.conn.execute(
-        'select installed_file, module_path, is_prebuilt_make_module, product_copy_files, '
+        'select installed_file, module_path, is_soong_module, is_prebuilt_make_module, product_copy_files, '
         '       kernel_module_copy_files, is_platform_generated, license_text '
         'from make_metadata '
         'where installed_file like ?', (dir + '/%',))
diff --git a/tools/sbom/gen_sbom.py b/tools/sbom/gen_sbom.py
index 77bccbb..e875ddb 100644
--- a/tools/sbom/gen_sbom.py
+++ b/tools/sbom/gen_sbom.py
@@ -415,11 +415,13 @@
 def installed_file_has_metadata(installed_file_metadata, report):
   installed_file = installed_file_metadata['installed_file']
   module_path = installed_file_metadata['module_path']
+  is_soong_module = installed_file_metadata['is_soong_module']
   product_copy_files = installed_file_metadata['product_copy_files']
   kernel_module_copy_files = installed_file_metadata['kernel_module_copy_files']
   is_platform_generated = installed_file_metadata['is_platform_generated']
 
   if (not module_path and
+      not is_soong_module and
       not product_copy_files and
       not kernel_module_copy_files and
       not is_platform_generated and
diff --git a/tools/tool_event_logger/Android.bp b/tools/tool_event_logger/Android.bp
index 7a1d2aa..d242db8 100644
--- a/tools/tool_event_logger/Android.bp
+++ b/tools/tool_event_logger/Android.bp
@@ -58,10 +58,4 @@
         "asuite_cc_client",
         "tool_event_proto",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-            enabled: true,
-        },
-    },
 }
diff --git a/tools/warn/OWNERS b/tools/warn/OWNERS
index 8551802..93ccd28 100644
--- a/tools/warn/OWNERS
+++ b/tools/warn/OWNERS
@@ -1 +1 @@
-per-file * = chh@google.com,srhines@google.com
+per-file * =srhines@google.com