Merge "Set `TARGET_USES_64_BIT_BINDER` for mainline_sdk."
diff --git a/core/Makefile b/core/Makefile
index a6f6dd2..86324cb 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -5238,7 +5238,7 @@
 endif # BOARD_AVB_VBMETA_SYSTEM
 ifneq (,$(strip $(BOARD_AVB_VBMETA_VENDOR)))
 	$(hide) echo "avb_vbmeta_vendor=$(BOARD_AVB_VBMETA_VENDOR)" >> $@
-	$(hide) echo "avb_vbmeta_vendor_args=$(BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS)" >> $@
+	$(hide) echo "avb_vbmeta_vendor_args=$(BOARD_AVB_MAKE_VBMETA_VENDOR_IMAGE_ARGS)" >> $@
 	$(hide) echo "avb_vbmeta_vendor_key_path=$(BOARD_AVB_VBMETA_VENDOR_KEY_PATH)" >> $@
 	$(hide) echo "avb_vbmeta_vendor_algorithm=$(BOARD_AVB_VBMETA_VENDOR_ALGORITHM)" >> $@
 	$(hide) echo "avb_vbmeta_vendor_rollback_index_location=$(BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION)" >> $@
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 6c32da4..c912d5b 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -71,6 +71,15 @@
 
 $(call soong_config_set,art_module,source_build,$(ART_MODULE_BUILD_FROM_SOURCE))
 
+# Ensure that those mainline modules who have individually toggleable prebuilts
+# are controlled by the MODULE_BUILD_FROM_SOURCE environment variable by
+# default.
+INDIVIDUALLY_TOGGLEABLE_PREBUILT_MODULES := \
+  wifi \
+
+$(foreach m, $(INDIVIDUALLY_TOGGLEABLE_PREBUILT_MODULES),\
+  $(call soong_config_set,$(m)_module,source_build,$(MODULE_BUILD_FROM_SOURCE)))
+
 # Apex build mode variables
 ifdef APEX_BUILD_FOR_PRE_S_DEVICES
 $(call add_soong_config_var_value,ANDROID,library_linking_strategy,prefer_static)
diff --git a/core/config.mk b/core/config.mk
index 247103d..c0dea95 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -155,8 +155,8 @@
 $(KATI_obsolete_var COVERAGE_EXCLUDE_PATHS,Use NATIVE_COVERAGE_EXCLUDE_PATHS instead)
 $(KATI_obsolete_var BOARD_VNDK_RUNTIME_DISABLE,VNDK-Lite is no longer supported)
 $(KATI_obsolete_var LOCAL_SANITIZE_BLACKLIST,Use LOCAL_SANITIZE_BLOCKLIST instead)
-$(KATI_deprecated_var BOARD_PLAT_PUBLIC_SEPOLICY_DIR,Use SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS instead)
-$(KATI_deprecated_var BOARD_PLAT_PRIVATE_SEPOLICY_DIR,Use SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS instead)
+$(KATI_obsolete_var BOARD_PLAT_PUBLIC_SEPOLICY_DIR,Use SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS instead)
+$(KATI_obsolete_var BOARD_PLAT_PRIVATE_SEPOLICY_DIR,Use SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS instead)
 $(KATI_obsolete_var TARGET_NO_VENDOR_BOOT,Use PRODUCT_BUILD_VENDOR_BOOT_IMAGE instead)
 $(KATI_obsolete_var PRODUCT_CHECK_ELF_FILES,Use BUILD_BROKEN_PREBUILT_ELF_FILES instead)
 $(KATI_obsolete_var ALL_GENERATED_SOURCES,ALL_GENERATED_SOURCES is no longer used)
diff --git a/core/definitions.mk b/core/definitions.mk
index c5423e7..1754713 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -573,16 +573,21 @@
 $(call generated-sources-dir-for,META,lic,)
 endef
 
+TARGETS_MISSING_LICENSE_METADATA:=
+
 ###########################################################
 # License metadata targets corresponding to targets in $(1)
 ###########################################################
 define corresponding-license-metadata
-$(strip $(foreach target, $(sort $(1)), \
+$(strip $(filter-out 0p,$(foreach target, $(sort $(1)), \
   $(if $(strip $(ALL_MODULES.$(target).META_LIC)), \
     $(ALL_MODULES.$(target).META_LIC), \
     $(if $(strip $(ALL_TARGETS.$(target).META_LIC)), \
       $(ALL_TARGETS.$(target).META_LIC), \
-      $(call append-path,$(call license-metadata-dir),$(patsubst $(OUT_DIR)%,out%,$(target).meta_lic))))))
+      $(eval TARGETS_MISSING_LICENSE_METADATA += $(target)) \
+    ) \
+  ) \
+)))
 endef
 
 ###########################################################
@@ -751,7 +756,7 @@
 $(_meta): PRIVATE_SOURCE_TARGETS := $(ALL_COPIED_TARGETS.$(1).SOURCES)
 $(_meta): PRIVATE_SOURCE_METADATA := $(_dep)
 $(_meta): PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,PACKAGING,copynotice)/$(_meta)/arguments
-$(_meta) : $(_dep)
+$(_meta) : $(_dep) $(COPY_LICENSE_METADATA)
 	rm -f $$@
 	mkdir -p $$(dir $$@)
 	mkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE))
@@ -780,6 +785,7 @@
 $(strip \
   $(eval _tgt := $(subst //,/,$(strip $(1)))) \
   $(eval ALL_NON_MODULES += $(_tgt)) \
+  $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir)/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \
   $(eval ALL_NON_MODULES.$(_tgt).LICENSE_KINDS := $(strip $(2))) \
   $(eval ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS := $(strip $(3))) \
   $(eval ALL_NON_MODULES.$(_tgt).NOTICES := $(strip $(4))) \
@@ -820,6 +826,7 @@
 $(strip \
   $(eval _tgt := $(subst //,/,$(strip $(1)))) \
   $(eval ALL_NON_MODULES += $(_tgt)) \
+  $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir)/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \
   $(eval ALL_NON_MODULES.$(_tgt).LICENSE_KINDS := $(strip $(2))) \
   $(eval ALL_NON_MODULES.$(_tgt).LICENSE_CONDITIONS := $(strip $(3))) \
   $(eval ALL_NON_MODULES.$(_tgt).NOTICES := $(strip $(4))) \
@@ -892,6 +899,7 @@
 $(strip \
   $(eval _tgt := $(strip $(1))) \
   $(eval ALL_NON_MODULES += $(_tgt)) \
+  $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir)/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \
   $(eval ALL_NON_MODULES.$(_tgt).DEPENDENCIES := $(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) $(2))) \
 )
 endef
@@ -908,6 +916,7 @@
 $(strip \
   $(eval _tgt := $(strip $(1))) \
   $(eval ALL_NON_MODULES += $(_tgt)) \
+  $(eval ALL_TARGETS.$(_tgt).META_LIC := $(call license-metadata-dir)/$(patsubst $(OUT_DIR)%,out%,$(_tgt)).meta_lic) \
   $(eval ALL_NON_MODULES.$(_tgt).DEPENDENCIES := $(strip $(ALL_NON_MODULES.$(_tgt).DEPENDENCIES) $(2))) \
   $(eval ALL_NON_MODULES.$(_tgt).IS_CONTAINER := true) \
   $(eval ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS := $(strip $(ALL_NON_MODULES.$(_tgt).ROOT_MAPPINGS) $(3))) \
@@ -919,12 +928,14 @@
 ###########################################################
 define report-missing-licenses-rule
 .PHONY: reportmissinglicenses
-reportmissinglicenses: PRIVATE_NON_MODULES:=$(sort $(NON_MODULES_WITHOUT_LICENSE_METADATA))
-reportmissinglicenses: PRIVATE_COPIED_FILES:=$(sort $(filter $(NON_MODULES_WITHOUT_LICENSE_METADATA),$(foreach _pair,$(PRODUCT_COPY_FILES), $(PRODUCT_OUT)/$(call word-colon,2,$(_pair)))))
+reportmissinglicenses: PRIVATE_NON_MODULES:=$(sort $(NON_MODULES_WITHOUT_LICENSE_METADATA) $(TARGETS_MISSING_LICENSE_METADATA))
+reportmissinglicenses: PRIVATE_COPIED_FILES:=$(sort $(filter $(NON_MODULES_WITHOUT_LICENSE_METADATA) $(TARGETS_MISSING_LICENSE_METADATA),\
+  $(foreach _pair,$(PRODUCT_COPY_FILES), $(PRODUCT_OUT)/$(call word-colon,2,$(_pair)))))
 reportmissinglicenses:
 	@echo Reporting $$(words $$(PRIVATE_NON_MODULES)) targets without license metadata
 	$$(foreach t,$$(PRIVATE_NON_MODULES),if ! [ -h $$(t) ]; then echo No license metadata for $$(t) >&2; fi;)
 	$$(foreach t,$$(PRIVATE_COPIED_FILES),if ! [ -h $$(t) ]; then echo No license metadata for copied file $$(t) >&2; fi;)
+	echo $$(words $$(PRIVATE_NON_MODULES)) targets missing license metadata >&2
 
 endef
 
@@ -977,15 +988,9 @@
   $(foreach t,$(sort $(ALL_0P_TARGETS)), \
     $(eval ALL_TARGETS.$(t).META_LIC := 0p) \
   ) \
-  $(foreach t,$(sort $(ALL_NON_MODULES)), \
-    $(eval ALL_TARGETS.$(t).META_LIC := $(call append-path,$(_dir),$(patsubst $(OUT_DIR)%,out%,$(t).meta_lic))) \
-  ) \
   $(foreach t,$(sort $(ALL_NON_MODULES)),$(eval $(call non-module-license-metadata-rule,$(t)))) \
   $(foreach m,$(sort $(ALL_MODULES)),$(eval $(call license-metadata-rule,$(m)))) \
   $(foreach t,$(sort $(ALL_COPIED_TARGETS)),$(eval $(call copied-target-license-metadata-rule,$(t)))) \
-  $(foreach t,$(sort $(ALL_NON_MODULES)),$(call record-missing-non-module-dependencies,$(t))) \
-  $(eval $(call report-missing-licenses-rule)) \
-  $(eval $(call report-all-notice-library-names-rule)) \
   $(eval $(call build-all-license-metadata-rule)))
 endef
 
@@ -1057,6 +1062,22 @@
 )
 endef
 
+# Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE
+# to determine the intermediates directory.
+#
+# $(1): if non-empty, force the intermediates to be COMMON
+# $(2): if non-empty, force the intermediates to be for the 2nd arch
+# $(3): if non-empty, force the intermediates to be for the host cross os
+define local-meta-intermediates-dir
+$(strip \
+    $(if $(strip $(LOCAL_MODULE_CLASS)),, \
+        $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-meta-intermediates-dir)) \
+    $(if $(strip $(LOCAL_MODULE)),, \
+        $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-meta-intermediates-dir)) \
+    $(call intermediates-dir-for,META$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST),$(1),$(2),$(3)) \
+)
+endef
+
 ###########################################################
 ## The generated sources directory.  Placing generated
 ## source files directly in the intermediates directory
@@ -2476,7 +2497,47 @@
         @$(call emit-line,$(wordlist 38001,38500,$(1)),$(2))
         @$(call emit-line,$(wordlist 38501,39000,$(1)),$(2))
         @$(call emit-line,$(wordlist 39001,39500,$(1)),$(2))
-        @$(if $(wordlist 39501,39502,$(1)),$(error Too many words ($(words $(1)))))
+        @$(call emit-line,$(wordlist 39501,40000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 40001,40500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 40501,41000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 41001,41500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 41501,42000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 42001,42500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 42501,43000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 43001,43500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 43501,44000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 44001,44500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 44501,45000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 45001,45500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 45501,46000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 46001,46500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 46501,47000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 47001,47500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 47501,48000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 48001,48500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 48501,49000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 49001,49500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 49501,50000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 50001,50500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 50501,51000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 51001,51500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 51501,52000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 52001,52500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 52501,53000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 53001,53500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 53501,54000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 54001,54500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 54501,55000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 55001,55500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 55501,56000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 56001,56500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 56501,57000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 57001,57500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 57501,58000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 58001,58500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 58501,59000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 59001,59500,$(1)),$(2))
+        @$(if $(wordlist 59501,59502,$(1)),$(error Too many words ($(words $(1)))))
 endef
 # Return jar arguments to compress files in a given directory
 # $(1): directory
diff --git a/core/dex_preopt_config_merger.py b/core/dex_preopt_config_merger.py
index 4efcc17..401e8a8 100755
--- a/core/dex_preopt_config_merger.py
+++ b/core/dex_preopt_config_merger.py
@@ -31,6 +31,7 @@
 
 import json
 from collections import OrderedDict
+import os
 import sys
 
 
@@ -42,8 +43,9 @@
   # Read all JSON configs.
   cfgs = []
   for arg in sys.argv[1:]:
-    with open(arg, 'r') as f:
-      cfgs.append(json.load(f, object_pairs_hook=OrderedDict))
+    if os.stat(arg).st_size != 0:
+      with open(arg, 'r') as f:
+        cfgs.append(json.load(f, object_pairs_hook=OrderedDict))
 
   # The first config is the dexpreopted library/app, the rest are its
   # <uses-library> dependencies.
@@ -88,6 +90,33 @@
       clcs2.append(clc)
     clc_map2[sdk_ver] = clcs2
 
+  # Go over all uses-libraries in dependency dexpreopt.config files (these don't
+  # have to be uses-libraries themselves, they can be e.g. transitive static
+  # library dependencies) and merge their CLC to the current one
+  for ulib, cfg in uses_libs.items():
+    any_sdk_ver = 'any' # not interested in compatibility libraries
+    clcs = cfg['ClassLoaderContexts'].get(any_sdk_ver, [])
+
+    # If the dependency is a uses-library itself, its uses-library dependencies
+    # are added as a subcontext, so don't add them to top-level CLC.
+    dep_is_a_uses_lib = False
+    for clc2 in clc_map2[any_sdk_ver]:
+      if clc2['Name'] == cfg['ProvidesUsesLibrary']:
+        dep_is_a_uses_lib = True
+    if dep_is_a_uses_lib:
+      continue
+
+    # Check if CLC for these libraries is already present (avoid duplicates).
+    # Don't bother optimizing quadratic loop, since CLC is typically small.
+    for clc in clcs:
+      already_in_clc = False
+      for clc2 in clc_map2[any_sdk_ver]:
+        if clc2['Name'] == clc['Name']:
+          already_in_clc = True
+          break
+      if not already_in_clc:
+        clc_map2[any_sdk_ver] += clcs
+
   # Overwrite the original class loader context with the patched one.
   cfg0['ClassLoaderContexts'] = clc_map2
 
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index 216168b..9d6436f 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -110,7 +110,8 @@
 # Local module variables and functions used in dexpreopt and manifest_check.
 ################################################################################
 
-my_filtered_optional_uses_libraries := $(filter-out $(INTERNAL_PLATFORM_MISSING_USES_LIBRARIES), \
+my_dexpreopt_libs_required := $(LOCAL_USES_LIBRARIES)
+my_dexpreopt_libs_optional := $(filter-out $(INTERNAL_PLATFORM_MISSING_USES_LIBRARIES), \
   $(LOCAL_OPTIONAL_USES_LIBRARIES))
 
 # TODO(b/132357300): This may filter out too much, as PRODUCT_PACKAGES doesn't
@@ -120,8 +121,7 @@
 # to load dexpreopt code on device. We should fix this, either by deferring
 # dependency computation until the full list of product packages is known, or
 # by adding product-specific lists of missing libraries.
-my_filtered_optional_uses_libraries := $(filter $(PRODUCT_PACKAGES), \
-  $(my_filtered_optional_uses_libraries))
+my_dexpreopt_libs_optional := $(filter $(PRODUCT_PACKAGES), $(my_dexpreopt_libs_optional))
 
 ifeq ($(LOCAL_MODULE_CLASS),APPS)
   # compatibility libraries are added to class loader context of an app only if
@@ -146,10 +146,6 @@
   my_dexpreopt_libs_compat :=
 endif
 
-my_dexpreopt_libs := \
-  $(LOCAL_USES_LIBRARIES) \
-  $(my_filtered_optional_uses_libraries)
-
 # Module dexpreopt.config depends on dexpreopt.config files of each
 # <uses-library> dependency, because these libraries may be processed after
 # the current module by Make (there's no topological order), so the dependency
@@ -157,11 +153,12 @@
 # this dexpreopt.config is generated. So it's necessary to add file-level
 # dependencies between dexpreopt.config files.
 my_dexpreopt_dep_configs := $(foreach lib, \
-  $(filter-out $(my_dexpreopt_libs_compat),$(LOCAL_USES_LIBRARIES) $(my_filtered_optional_uses_libraries)), \
+  $(filter-out $(my_dexpreopt_libs_compat),$(my_dexpreopt_libs_required) $(my_dexpreopt_libs_optional)), \
   $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,)/dexpreopt.config)
 
 # 1: SDK version
 # 2: list of libraries
+# 3: boolean, true if optional, else required
 #
 # Make does not process modules in topological order wrt. <uses-library>
 # dependencies, therefore we cannot rely on variables to get the information
@@ -180,12 +177,48 @@
   $(foreach lib, $(2),\
     $(call add_json_map_anon) \
     $(call add_json_str, Name, $(lib)) \
+    $(call add_json_bool, Optional, $(filter true,$(3))) \
     $(call add_json_str, Host, $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar) \
     $(call add_json_str, Device, /system/framework/$(lib).jar) \
     $(call add_json_val, Subcontexts, null) \
     $(call end_json_map)) \
   $(call end_json_array)
 
+my_dexpreopt_archs :=
+my_dexpreopt_images :=
+my_dexpreopt_images_deps :=
+my_dexpreopt_image_locations_on_host :=
+my_dexpreopt_image_locations_on_device :=
+# Infix can be 'boot' or 'art'. Soong creates a set of variables for Make, one
+# for each boot image (primary and the framework extension). The only reason why
+# the primary image is exposed to Make is testing (art gtests) and benchmarking
+# (art golem benchmarks). Install rules that use those variables are in
+# dex_preopt_libart.mk. Here for dexpreopt purposes the infix is always 'boot'.
+my_dexpreopt_infix := boot
+
+ifdef LOCAL_DEX_PREOPT
+  ifeq (,$(filter PRESIGNED,$(LOCAL_CERTIFICATE)))
+    # Store uncompressed dex files preopted in /system
+    ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true)
+      ifeq ($(call install-on-system-other, $(my_module_path)),)
+        LOCAL_UNCOMPRESS_DEX := true
+      endif  # install-on-system-other
+    else  # BOARD_USES_SYSTEM_OTHER_ODEX
+      LOCAL_UNCOMPRESS_DEX := true
+    endif
+  endif
+  my_create_dexpreopt_config := true
+endif
+
+# dexpreopt is disabled when TARGET_BUILD_UNBUNDLED_IMAGE is true,
+# but dexpreopt config files are required to dexpreopt in post-processing.
+ifeq ($(TARGET_BUILD_UNBUNDLED_IMAGE),true)
+  my_create_dexpreopt_config := true
+endif
+
+# This is needed for both <uses-library> check and dexpreopt command.
+my_dexpreopt_config := $(intermediates)/dexpreopt.config
+
 ################################################################################
 # Verify <uses-library> coherence between the build system and the manifest.
 ################################################################################
@@ -238,7 +271,13 @@
     $(LOCAL_OPTIONAL_USES_LIBRARIES))
   my_relax_check_arg := $(if $(filter true,$(RELAX_USES_LIBRARY_CHECK)), \
     --enforce-uses-libraries-relax,)
-  my_dexpreopt_config_args := $(patsubst %,--dexpreopt-config %,$(my_dexpreopt_dep_configs))
+
+  my_dexpreopt_config_deps := $(my_dexpreopt_dep_configs)
+  my_dexpreopt_config_args := $(patsubst %,--dexpreopt-dep-config %,$(my_dexpreopt_dep_configs))
+  ifeq ($(my_create_dexpreopt_config), true)
+    my_dexpreopt_config_deps += $(my_dexpreopt_config)
+    my_dexpreopt_config_args += --dexpreopt-config $(my_dexpreopt_config)
+  endif
 
   my_enforced_uses_libraries := $(intermediates.COMMON)/enforce_uses_libraries.status
   $(my_enforced_uses_libraries): PRIVATE_USES_LIBRARIES := $(my_uses_libs_args)
@@ -247,7 +286,7 @@
   $(my_enforced_uses_libraries): PRIVATE_RELAX_CHECK := $(my_relax_check_arg)
   $(my_enforced_uses_libraries): $(AAPT)
   $(my_enforced_uses_libraries): $(my_verify_script)
-  $(my_enforced_uses_libraries): $(my_dexpreopt_dep_configs)
+  $(my_enforced_uses_libraries): $(my_dexpreopt_config_deps)
   $(my_enforced_uses_libraries): $(my_manifest_or_apk)
 	@echo Verifying uses-libraries: $<
 	rm -f $@
@@ -267,39 +306,6 @@
 # Dexpreopt command.
 ################################################################################
 
-my_dexpreopt_archs :=
-my_dexpreopt_images :=
-my_dexpreopt_images_deps :=
-my_dexpreopt_image_locations_on_host :=
-my_dexpreopt_image_locations_on_device :=
-# Infix can be 'boot' or 'art'. Soong creates a set of variables for Make, one
-# for each boot image (primary and the framework extension). The only reason why
-# the primary image is exposed to Make is testing (art gtests) and benchmarking
-# (art golem benchmarks). Install rules that use those variables are in
-# dex_preopt_libart.mk. Here for dexpreopt purposes the infix is always 'boot'.
-my_dexpreopt_infix := boot
-my_create_dexpreopt_config :=
-
-ifdef LOCAL_DEX_PREOPT
-  ifeq (,$(filter PRESIGNED,$(LOCAL_CERTIFICATE)))
-    # Store uncompressed dex files preopted in /system
-    ifeq ($(BOARD_USES_SYSTEM_OTHER_ODEX),true)
-      ifeq ($(call install-on-system-other, $(my_module_path)),)
-        LOCAL_UNCOMPRESS_DEX := true
-      endif  # install-on-system-other
-    else  # BOARD_USES_SYSTEM_OTHER_ODEX
-      LOCAL_UNCOMPRESS_DEX := true
-    endif
-  endif
-  my_create_dexpreopt_config := true
-endif
-
-# dexpreopt is disabled when TARGET_BUILD_UNBUNDLED_IMAGE is true,
-# but dexpreopt config files are required to dexpreopt in post-processing.
-ifeq ($(TARGET_BUILD_UNBUNDLED_IMAGE),true)
-  my_create_dexpreopt_config := true
-endif
-
 ifeq ($(my_create_dexpreopt_config), true)
   ifeq ($(LOCAL_MODULE_CLASS),JAVA_LIBRARIES)
     my_module_multilib := $(LOCAL_MULTILIB)
@@ -389,10 +395,11 @@
   $(call add_json_bool, EnforceUsesLibraries,           $(filter true,$(LOCAL_ENFORCE_USES_LIBRARIES)))
   $(call add_json_str,  ProvidesUsesLibrary,            $(firstword $(LOCAL_PROVIDES_USES_LIBRARY) $(LOCAL_MODULE)))
   $(call add_json_map,  ClassLoaderContexts)
-  $(call add_json_class_loader_context, any, $(my_dexpreopt_libs))
-  $(call add_json_class_loader_context,  28, $(my_dexpreopt_libs_compat_28))
-  $(call add_json_class_loader_context,  29, $(my_dexpreopt_libs_compat_29))
-  $(call add_json_class_loader_context,  30, $(my_dexpreopt_libs_compat_30))
+  $(call add_json_class_loader_context, any, $(my_dexpreopt_libs_required),)
+  $(call add_json_class_loader_context, any, $(my_dexpreopt_libs_optional),true)
+  $(call add_json_class_loader_context,  28, $(my_dexpreopt_libs_compat_28),)
+  $(call add_json_class_loader_context,  29, $(my_dexpreopt_libs_compat_29),)
+  $(call add_json_class_loader_context,  30, $(my_dexpreopt_libs_compat_30),)
   $(call end_json_map)
   $(call add_json_list, Archs,                          $(my_dexpreopt_archs))
   $(call add_json_list, DexPreoptImages,                $(my_dexpreopt_images))
@@ -407,7 +414,6 @@
 
   $(call json_end)
 
-  my_dexpreopt_config := $(intermediates)/dexpreopt.config
   my_dexpreopt_config_for_postprocessing := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config
   my_dexpreopt_config_merger := $(BUILD_SYSTEM)/dex_preopt_config_merger.py
 
@@ -466,7 +472,7 @@
   my_dexpreopt_deps := $(my_dex_jar)
   my_dexpreopt_deps += $(if $(my_process_profile),$(LOCAL_DEX_PREOPT_PROFILE))
   my_dexpreopt_deps += \
-    $(foreach lib, $(my_dexpreopt_libs) $(my_dexpreopt_libs_compat), \
+    $(foreach lib, $(my_dexpreopt_libs_required) $(my_dexpreopt_libs_optional) $(my_dexpreopt_libs_compat), \
       $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar)
   my_dexpreopt_deps += $(my_dexpreopt_images_deps)
   my_dexpreopt_deps += $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)
@@ -506,4 +512,4 @@
   my_dexpreopt_zip :=
   my_dexpreopt_config_for_postprocessing :=
 endif # LOCAL_DEX_PREOPT
-endif # my_create_dexpreopt_config
\ No newline at end of file
+endif # my_create_dexpreopt_config
diff --git a/core/distdir.mk b/core/distdir.mk
index aad8ff3..3d48a48 100644
--- a/core/distdir.mk
+++ b/core/distdir.mk
@@ -45,6 +45,105 @@
     $(eval _all_dist_goal_output_pairs += $$(goal):$$(dst))))
 endef
 
+.PHONY: shareprojects
+
+define __share-projects-rule
+$(1) : PRIVATE_TARGETS := $(2)
+$(1) : PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,METAPACKAGING,codesharing)/$(1)/arguments
+$(1): $(2) $(COMPLIANCE_LISTSHARE)
+	$(hide) rm -f $$@
+	mkdir -p $$(dir $$@)
+	mkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE))
+	$$(if $$(strip $$(PRIVATE_TARGETS)),$$(call dump-words-to-file,$$(PRIVATE_TARGETS),$$(PRIVATE_ARGUMENT_FILE)))
+	$$(if $$(strip $$(PRIVATE_TARGETS)),OUT_DIR=$(OUT_DIR) $(COMPLIANCE_LISTSHARE) -o $$@ @$$(PRIVATE_ARGUMENT_FILE),touch $$@)
+endef
+
+# build list of projects to share in $(1) for dist targets in $(2)
+#
+# $(1): the intermediate project sharing file
+# $(2): the dist files to base the sharing on
+define _share-projects-rule
+$(eval $(call __share-projects-rule,$(1),$(call corresponding-license-metadata,$(2))))
+endef
+
+# Add a build dependency
+#
+# $(1): the goal phony target
+# $(2): the intermediate shareprojects file
+define _share-projects-dep
+$(1): $(2)
+endef
+
+.PHONY: alllicensetexts
+
+define __license-texts-rule
+$(2) : PRIVATE_GOAL := $(1)
+$(2) : PRIVATE_TARGETS := $(3)
+$(2) : PRIVATE_ROOTS := $(4)
+$(2) : PRIVATE_ARGUMENT_FILE := $(call intermediates-dir-for,METAPACKAGING,licensetexts)/$(2)/arguments
+$(2): $(3) $(TEXTNOTICE)
+	$(hide) rm -f $$@
+	mkdir -p $$(dir $$@)
+	mkdir -p $$(dir $$(PRIVATE_ARGUMENT_FILE))
+	$$(if $$(strip $$(PRIVATE_TARGETS)),$$(call dump-words-to-file,\
+            -product="$$(PRIVATE_GOAL)" -title="$$(PRIVATE_GOAL)" \
+            $$(addprefix -strip_prefix ,$$(PRIVATE_ROOTS)) \
+            -strip_prefix=$(PRODUCT_OUT)/ -strip_prefix=$(HOST_OUT)/\
+            $$(PRIVATE_TARGETS),\
+            $$(PRIVATE_ARGUMENT_FILE)))
+	$$(if $$(strip $$(PRIVATE_TARGETS)),OUT_DIR=$(OUT_DIR) $(TEXTNOTICE) -o $$@ @$$(PRIVATE_ARGUMENT_FILE),touch $$@)
+endef
+
+# build list of projects to share in $(2) for dist targets in $(3) for dist goal $(1)
+#
+# $(1): the name of the dist goal
+# $(2): the intermediate project sharing file
+# $(3): the dist files to base the sharing on
+define _license-texts-rule
+$(eval $(call __license-texts-rule,$(1),$(2),$(call corresponding-license-metadata,$(3)),$(sort $(dir $(3)))))
+endef
+
+# Add a build dependency
+#
+# $(1): the goal phony target
+# $(2): the intermediate shareprojects file
+define _license-texts-dep
+$(1): $(2)
+endef
+
+define _add_projects_to_share
+$(strip $(eval _idir := $(call intermediates-dir-for,METAPACKAGING,shareprojects))) \
+$(strip $(eval _tdir := $(call intermediates-dir-for,METAPACKAGING,licensetexts))) \
+$(strip $(eval _goals := $(sort $(_all_dist_goals)))) \
+$(strip $(eval _opairs := $(sort $(_all_dist_goal_output_pairs)))) \
+$(strip $(eval _dpairs := $(sort $(_all_dist_src_dst_pairs)))) \
+$(strip $(eval _allt :=)) \
+$(foreach goal,$(_goals), \
+  $(eval _f := $(_idir)/$(goal).shareprojects) \
+  $(eval _n := $(_tdir)/$(goal).txt) \
+  $(call dist-for-goals,$(goal),$(_f):shareprojects/$(basename $(notdir $(_f)))) \
+  $(call dist-for-goals,$(goal),$(_n):licensetexts/$(basename $(notdir $(_n)))) \
+  $(eval _targets :=) \
+  $(foreach op,$(filter $(goal):%,$(_opairs)),$(foreach p,$(filter %:$(call word-colon,2,$(op)),$(_dpairs)),$(eval _targets += $(call word-colon,1,$(p))))) \
+  $(eval _allt += $(_targets)) \
+  $(eval $(call _share-projects-rule,$(_f),$(_targets))) \
+  $(eval $(call _license-texts-rule,$(goal),$(_n),$(_targets))) \
+)\
+$(eval _f := $(_idir)/all.shareprojects)\
+$(eval _n := $(_tdir)/all.txt)\
+$(eval _idir :=)\
+$(eval _tdir :=)\
+$(eval $(call _share-projects-dep,shareprojects,$(_f))) \
+$(eval $(call _license-texts-dep,alllicensetexts,$(_n))) \
+$(call dist-for-goals,droid shareprojects,$(_f):shareprojects/all)\
+$(call dist-for-goals,droid alllicensetexts,$(_n):licensetexts/all)\
+$(eval _allt := $(sort $(_allt)))\
+$(eval $(call _share-projects-rule,$(_f),$(_allt)))\
+$(eval $(call _license-texts-rule,droid,$(_n),$(_allt)))\
+$(eval _f :=)\
+$(evan _n :=)
+endef
+
 #------------------------------------------------------------------
 # To be used at the end of the build to collect all the uses of
 # dist-for-goals, and write them into a file for the packaging step to use.
@@ -52,6 +151,15 @@
 # $(1): The file to write
 define dist-write-file
 $(strip \
+  $(call _add_projects_to_share)\
+  $(if $(strip $(ANDROID_REQUIRE_LICENSE_METADATA)),\
+    $(foreach target,$(sort $(TARGETS_MISSING_LICENSE_METADATA)),$(warning target $(target) missing license metadata))\
+    $(if $(strip $(TARGETS_MISSING_LICENSE_METADATA)),\
+      $(if $(filter true error,$(ANDROID_REQUIRE_LICENSE_METADATA)),\
+        $(error $(words $(sort $(TARGETS_MISSING_LICENSE_METADATA))) targets need license metadata))))\
+  $(foreach t,$(sort $(ALL_NON_MODULES)),$(call record-missing-non-module-dependencies,$(t))) \
+  $(eval $(call report-missing-licenses-rule)) \
+  $(eval $(call report-all-notice-library-names-rule)) \
   $(KATI_obsolete_var dist-for-goals,Cannot be used after dist-write-file) \
   $(foreach goal,$(sort $(_all_dist_goals)), \
     $(eval $$(goal): _dist_$$(goal))) \
diff --git a/core/envsetup.mk b/core/envsetup.mk
index c32d380..d116aaf 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -326,8 +326,6 @@
 # raw ones.
 define dump-variables-rbc
 $(eval _dump_variables_rbc_excluded := \
-  BOARD_PLAT_PRIVATE_SEPOLICY_DIR \
-  BOARD_PLAT_PUBLIC_SEPOLICY_DIR \
   BUILD_NUMBER \
   DATE \
   LOCAL_PATH \
diff --git a/core/main.mk b/core/main.mk
index 2cfea45..cdbc3ef 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -460,6 +460,9 @@
 
 ADDITIONAL_SYSTEM_PROPERTIES += net.bt.name=Android
 
+# This property is set by flashing debug boot image, so default to false.
+ADDITIONAL_SYSTEM_PROPERTIES += ro.force.debuggable=0
+
 # ------------------------------------------------------------
 # Define a function that, given a list of module tags, returns
 # non-empty if that module should be installed in /system.
diff --git a/core/notice_files.mk b/core/notice_files.mk
index 84523af..cbfcaa4 100644
--- a/core/notice_files.mk
+++ b/core/notice_files.mk
@@ -125,7 +125,7 @@
 module_license_metadata :=
 
 ifdef my_register_name
-  module_license_metadata := $(call local-intermediates-dir)/$(my_register_name).meta_lic
+  module_license_metadata := $(call local-meta-intermediates-dir)/$(my_register_name).meta_lic
 
   $(foreach target,$(ALL_MODULES.$(my_register_name).BUILT) $(ALL_MODULES.$(my_register_name).INSTALLED) $(foreach bi,$(LOCAL_SOONG_BUILT_INSTALLED),$(call word-colon,1,$(bi))) \
       $(my_test_data) $(my_test_config),\
diff --git a/core/soong_config.mk b/core/soong_config.mk
index c84676b..7b8f6df 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -207,9 +207,8 @@
 $(call add_json_list, BoardVendorDlkmSepolicyDirs,       $(BOARD_VENDOR_DLKM_SEPOLICY_DIRS))
 $(call add_json_list, BoardOdmDlkmSepolicyDirs,          $(BOARD_ODM_DLKM_SEPOLICY_DIRS))
 $(call add_json_list, BoardSystemDlkmSepolicyDirs,       $(BOARD_SYSTEM_DLKM_SEPOLICY_DIRS))
-# TODO: BOARD_PLAT_* dirs only kept for compatibility reasons. Will be a hard error on API level 31
-$(call add_json_list, SystemExtPublicSepolicyDirs,       $(SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS) $(BOARD_PLAT_PUBLIC_SEPOLICY_DIR))
-$(call add_json_list, SystemExtPrivateSepolicyDirs,      $(SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS) $(BOARD_PLAT_PRIVATE_SEPOLICY_DIR))
+$(call add_json_list, SystemExtPublicSepolicyDirs,       $(SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS))
+$(call add_json_list, SystemExtPrivateSepolicyDirs,      $(SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS))
 $(call add_json_list, BoardSepolicyM4Defs,               $(BOARD_SEPOLICY_M4DEFS))
 $(call add_json_str,  BoardSepolicyVers,                 $(BOARD_SEPOLICY_VERS))
 $(call add_json_str,  SystemExtSepolicyPrebuiltApiDir,   $(BOARD_SYSTEM_EXT_PREBUILT_DIR))
diff --git a/core/tasks/tools/package-modules.mk b/core/tasks/tools/package-modules.mk
index 20a1694..c41aec5 100644
--- a/core/tasks/tools/package-modules.mk
+++ b/core/tasks/tools/package-modules.mk
@@ -21,11 +21,13 @@
 LOCAL_MODULE := $(my_package_name)
 LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
 LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_LICENSE_PACKAGE_NAME := Android
+LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
 LOCAL_MODULE_CLASS := PACKAGING
 LOCAL_MODULE_STEM := $(my_package_name).zip
 LOCAL_UNINSTALLABLE_MODULE := true
 include $(BUILD_SYSTEM)/base_rules.mk
-my_staging_dir := $(intermediates)
+my_staging_dir := $(intermediates)/staging
 my_package_zip := $(LOCAL_BUILT_MODULE)
 
 my_built_modules := $(foreach p,$(my_copy_pairs),$(call word-colon,1,$(p)))
@@ -92,17 +94,18 @@
 endif
 
 $(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)
 	@echo "Package $@"
-	@rm -rf $(dir $@) && mkdir -p $(dir $@)
+	@rm -rf $(PRIVATE_STAGING_DIR) && mkdir -p $(PRIVATE_STAGING_DIR)
 	$(foreach p, $(PRIVATE_COPY_PAIRS),\
 	  $(eval pair := $(subst :,$(space),$(p)))\
 	  mkdir -p $(dir $(word 2,$(pair))) && \
 	  cp -Rf $(word 1,$(pair)) $(word 2,$(pair)) && ) true
 	$(hide) $(foreach f, $(PRIVATE_PICKUP_FILES),\
-	  cp -RfL $(f) $(dir $@) && ) true
-	$(hide) cd $(dir $@) && zip -rqX $(notdir $@) *
+	  cp -RfL $(f) $(PRIVATE_STAGING_DIR) && ) true
+	$(hide) cd $(PRIVATE_STAGING_DIR) && zip -rqX ../$(notdir $@) *
 
 my_makefile :=
 my_staging_dir :=
diff --git a/finalize_branch_for_release.sh b/finalize_branch_for_release.sh
index c942eb2..12b096f 100755
--- a/finalize_branch_for_release.sh
+++ b/finalize_branch_for_release.sh
@@ -1,30 +1,34 @@
 #!/bin/bash
 
-set -e
+set -ex
 
-source "$(dirname "$0")"/envsetup.sh
+function finalize_main() {
+    local top="$(dirname "$0")"/../..
 
-# default target to modify tree and build SDK
-lunch aosp_arm64-userdebug
+    # default target to modify tree and build SDK
+    local m="$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug"
 
-set -x
+    # This script is WIP and only finalizes part of the Android branch for release.
+    # The full process can be found at (INTERNAL) go/android-sdk-finalization.
 
-# This script is WIP and only finalizes part of the Android branch for release.
-# The full process can be found at (INTERNAL) go/android-sdk-finalization.
+    # VNDK snapshot (TODO)
+    # SDK snapshots (TODO)
+    # Update references in the codebase to new API version (TODO)
+    # ...
 
-# VNDK snapshot (TODO)
-# SDK snapshots (TODO)
-# Update references in the codebase to new API version (TODO)
-# ...
+    AIDL_TRANSITIVE_FREEZE=true $m aidl-freeze-api
 
-AIDL_TRANSITIVE_FREEZE=true m aidl-freeze-api
+    # Update new versions of files. See update-vndk-list.sh (which requires envsetup.sh)
+    $m check-vndk-list || \
+        { cp $top/out/soong/vndk/vndk.libraries.txt $top/build/make/target/product/gsi/current.txt; }
 
-m check-vndk-list || update-vndk-list.sh # for new versions of AIDL interfaces
+    # for now, we simulate the release state for AIDL, but in the future, we would want
+    # to actually turn the branch into the REL state and test with that
+    AIDL_FROZEN_REL=true $m nothing # test build
 
-# for now, we simulate the release state for AIDL, but in the future, we would want
-# to actually turn the branch into the REL state and test with that
-AIDL_FROZEN_REL=true m # test build
+    # Build SDK (TODO)
+    # lunch sdk...
+    # m ...
+}
 
-# Build SDK (TODO)
-# lunch sdk...
-# m ...
+finalize_main
diff --git a/rbesetup.sh b/rbesetup.sh
index 3b0e7cf..8386628 100644
--- a/rbesetup.sh
+++ b/rbesetup.sh
@@ -33,20 +33,15 @@
 # This function prefixes the given command with appropriate variables needed
 # for the build to be executed with RBE.
 function use_rbe() {
-  local RBE_LOG_DIR="/tmp"
   local RBE_BINARIES_DIR="prebuilts/remoteexecution-client/latest"
   local DOCKER_IMAGE="gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:582efb38f0c229ea39952fff9e132ccbe183e14869b39888010dacf56b360d62"
 
   # Do not set an invocation-ID and let reproxy auto-generate one.
   USE_RBE="true" \
-  FLAG_server_address="unix:///tmp/reproxy_$RANDOM.sock" \
   FLAG_exec_root="$(gettop)" \
   FLAG_platform="container-image=docker://${DOCKER_IMAGE}" \
   RBE_use_application_default_credentials="true" \
-  RBE_log_dir="${RBE_LOG_DIR}" \
   RBE_reproxy_wait_seconds="20" \
-  RBE_output_dir="${RBE_LOG_DIR}" \
-  RBE_log_path="text://${RBE_LOG_DIR}/reproxy_log.txt" \
   RBE_CXX_EXEC_STRATEGY="remote_local_fallback" \
   RBE_cpp_dependency_scanner_plugin="${RBE_BINARIES_DIR}/dependency_scanner_go_plugin.so" \
   RBE_DIR=${RBE_BINARIES_DIR} \
diff --git a/tools/compliance/Android.bp b/tools/compliance/Android.bp
index 7a6c4ba..225f3a5 100644
--- a/tools/compliance/Android.bp
+++ b/tools/compliance/Android.bp
@@ -20,14 +20,20 @@
 blueprint_go_binary {
     name: "compliance_checkshare",
     srcs: ["cmd/checkshare/checkshare.go"],
-    deps: ["compliance-module"],
+    deps: [
+        "compliance-module",
+        "soong-response",
+    ],
     testSrcs: ["cmd/checkshare/checkshare_test.go"],
 }
 
 blueprint_go_binary {
     name: "compliancenotice_bom",
     srcs: ["cmd/bom/bom.go"],
-    deps: ["compliance-module"],
+    deps: [
+        "compliance-module",
+        "soong-response",
+    ],
     testSrcs: ["cmd/bom/bom_test.go"],
 }
 
@@ -44,21 +50,30 @@
 blueprint_go_binary {
     name: "compliance_listshare",
     srcs: ["cmd/listshare/listshare.go"],
-    deps: ["compliance-module"],
+    deps: [
+        "compliance-module",
+        "soong-response",
+    ],
     testSrcs: ["cmd/listshare/listshare_test.go"],
 }
 
 blueprint_go_binary {
     name: "compliance_dumpgraph",
     srcs: ["cmd/dumpgraph/dumpgraph.go"],
-    deps: ["compliance-module"],
+    deps: [
+        "compliance-module",
+        "soong-response",
+    ],
     testSrcs: ["cmd/dumpgraph/dumpgraph_test.go"],
 }
 
 blueprint_go_binary {
     name: "compliance_dumpresolutions",
     srcs: ["cmd/dumpresolutions/dumpresolutions.go"],
-    deps: ["compliance-module"],
+    deps: [
+        "compliance-module",
+        "soong-response",
+    ],
     testSrcs: ["cmd/dumpresolutions/dumpresolutions_test.go"],
 }
 
@@ -68,6 +83,7 @@
     deps: [
         "compliance-module",
         "blueprint-deptools",
+        "soong-response",
     ],
     testSrcs: ["cmd/htmlnotice/htmlnotice_test.go"],
 }
@@ -75,7 +91,10 @@
 blueprint_go_binary {
     name: "compliance_rtrace",
     srcs: ["cmd/rtrace/rtrace.go"],
-    deps: ["compliance-module"],
+    deps: [
+        "compliance-module",
+        "soong-response",
+    ],
     testSrcs: ["cmd/rtrace/rtrace_test.go"],
 }
 
@@ -85,6 +104,7 @@
     deps: [
         "compliance-module",
         "blueprint-deptools",
+        "soong-response",
     ],
     testSrcs: ["cmd/textnotice/textnotice_test.go"],
 }
@@ -95,6 +115,7 @@
     deps: [
         "compliance-module",
         "blueprint-deptools",
+        "soong-response",
     ],
     testSrcs: ["cmd/xmlnotice/xmlnotice_test.go"],
 }
diff --git a/tools/compliance/cmd/bom/bom.go b/tools/compliance/cmd/bom/bom.go
index b613a1f..187f828 100644
--- a/tools/compliance/cmd/bom/bom.go
+++ b/tools/compliance/cmd/bom/bom.go
@@ -24,13 +24,11 @@
 	"path/filepath"
 	"strings"
 
+	"android/soong/response"
 	"android/soong/tools/compliance"
 )
 
 var (
-	outputFile  = flag.String("o", "-", "Where to write the bill of materials. (default stdout)")
-	stripPrefix = newMultiString("strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
-
 	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
 	failNoLicenses    = fmt.Errorf("No licenses found")
 )
@@ -55,22 +53,10 @@
 	return installPath
 }
 
-func init() {
-	flag.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]))
-		flag.PrintDefaults()
-	}
-}
-
 // newMultiString creates a flag that allows multiple values in an array.
-func newMultiString(name, usage string) *multiString {
+func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
 	var f multiString
-	flag.Var(&f, name, usage)
+	flags.Var(&f, name, usage)
 	return &f
 }
 
@@ -81,16 +67,52 @@
 func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
 
 func main() {
-	flag.Parse()
+	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 flag.NArg() == 0 {
-		flag.Usage()
+	if flags.NArg() == 0 {
+		flags.Usage()
 		os.Exit(2)
 	}
 
 	if len(*outputFile) == 0 {
-		flag.Usage()
+		flags.Usage()
 		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
 		os.Exit(2)
 	} else {
@@ -118,10 +140,10 @@
 
 	ctx := &context{ofile, os.Stderr, compliance.FS, *stripPrefix}
 
-	err := billOfMaterials(ctx, flag.Args()...)
+	err := billOfMaterials(ctx, flags.Args()...)
 	if err != nil {
 		if err == failNoneRequested {
-			flag.Usage()
+			flags.Usage()
 		}
 		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
 		os.Exit(1)
diff --git a/tools/compliance/cmd/checkshare/checkshare.go b/tools/compliance/cmd/checkshare/checkshare.go
index 73bdcb5..f7b4cd2 100644
--- a/tools/compliance/cmd/checkshare/checkshare.go
+++ b/tools/compliance/cmd/checkshare/checkshare.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"bytes"
 	"flag"
 	"fmt"
 	"io"
@@ -22,31 +23,12 @@
 	"os"
 	"path/filepath"
 	"sort"
+	"strings"
 
+	"android/soong/response"
 	"android/soong/tools/compliance"
 )
 
-func init() {
-	flag.Usage = func() {
-		fmt.Fprintf(os.Stderr, `Usage: %s file.meta_lic {file.meta_lic...}
-
-Reports on stderr any targets where policy says that the source both
-must and must not be shared. The error report indicates the target, the
-license condition that has a source privacy policy, and the license
-condition that has a source sharing policy.
-
-Any given target may appear multiple times with different combinations
-of conflicting license conditions.
-
-If all the source code that policy says must be shared may be shared,
-outputs "PASS" to stdout and exits with status 0.
-
-If policy says any source must both be shared and not be shared,
-outputs "FAIL" to stdout and exits with status 1.
-`, filepath.Base(os.Args[0]))
-	}
-}
-
 var (
 	failConflicts     = fmt.Errorf("conflicts")
 	failNoneRequested = fmt.Errorf("\nNo metadata files requested")
@@ -61,24 +43,105 @@
 func (l byError) Less(i, j int) bool { return l[i].Error() < l[j].Error() }
 
 func main() {
-	flag.Parse()
+	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 {-o outfile} file.meta_lic {file.meta_lic...}
+
+Reports on stderr any targets where policy says that the source both
+must and must not be shared. The error report indicates the target, the
+license condition that has a source privacy policy, and the license
+condition that has a source sharing policy.
+
+Any given target may appear multiple times with different combinations
+of conflicting license conditions.
+
+If all the source code that policy says must be shared may be shared,
+outputs "PASS" to stdout and exits with status 0.
+
+If policy says any source must both be shared and not be shared,
+outputs "FAIL" to stdout and exits with status 1.
+`, filepath.Base(os.Args[0]))
+		flags.PrintDefaults()
+	}
+
+	outputFile := flags.String("o", "-", "Where to write the output. (default stdout)")
+
+	flags.Parse(expandedArgs)
 
 	// Must specify at least one root target.
-	if flag.NArg() == 0 {
-		flag.Usage()
+	if flags.NArg() == 0 {
+		flags.Usage()
 		os.Exit(2)
 	}
 
-	err := checkShare(os.Stdout, os.Stderr, compliance.FS, flag.Args()...)
+	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
+	}
+
+	err := checkShare(ofile, os.Stderr, compliance.FS, flags.Args()...)
 	if err != nil {
 		if err != failConflicts {
 			if err == failNoneRequested {
-				flag.Usage()
+				flags.Usage()
 			}
 			fmt.Fprintf(os.Stderr, "%s\n", err.Error())
 		}
 		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 from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
+			os.Exit(1)
+		}
+	}
 	os.Exit(0)
 }
 
@@ -92,7 +155,7 @@
 	// Read the license graph from the license metadata files (*.meta_lic).
 	licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)
 	if err != nil {
-		return fmt.Errorf("Unable to read license metadata file(s) %q: %w\n", files, err)
+		return fmt.Errorf("Unable to read license metadata file(s) %q from %q: %w\n", files, os.Getenv("PWD"), err)
 	}
 	if licenseGraph == nil {
 		return failNoLicenses
diff --git a/tools/compliance/cmd/dumpgraph/dumpgraph.go b/tools/compliance/cmd/dumpgraph/dumpgraph.go
index 32a3fc4..5625779 100644
--- a/tools/compliance/cmd/dumpgraph/dumpgraph.go
+++ b/tools/compliance/cmd/dumpgraph/dumpgraph.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"bytes"
 	"flag"
 	"fmt"
 	"io"
@@ -24,14 +25,11 @@
 	"sort"
 	"strings"
 
+	"android/soong/response"
 	"android/soong/tools/compliance"
 )
 
 var (
-	graphViz        = flag.Bool("dot", false, "Whether to output graphviz (i.e. dot) format.")
-	labelConditions = flag.Bool("label_conditions", false, "Whether to label target nodes with conditions.")
-	stripPrefix     = newMultiString("strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
-
 	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
 	failNoLicenses    = fmt.Errorf("No licenses found")
 )
@@ -55,8 +53,44 @@
 	return installPath
 }
 
-func init() {
-	flag.Usage = func() {
+// 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 space-separated Target Dependency Annotations tuples for each
@@ -70,42 +104,68 @@
 
 Options:
 `, filepath.Base(os.Args[0]))
-		flag.PrintDefaults()
+		flags.PrintDefaults()
 	}
-}
 
-// newMultiString creates a flag that allows multiple values in an array.
-func newMultiString(name, usage string) *multiString {
-	var f multiString
-	flag.Var(&f, name, usage)
-	return &f
-}
+	graphViz := flags.Bool("dot", false, "Whether to output graphviz (i.e. dot) format.")
+	labelConditions := flags.Bool("label_conditions", false, "Whether to label target nodes with conditions.")
+	outputFile := flags.String("o", "-", "Where to write the output. (default stdout)")
+	stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
 
-// 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() {
-	flag.Parse()
+	flags.Parse(expandedArgs)
 
 	// Must specify at least one root target.
-	if flag.NArg() == 0 {
-		flag.Usage()
+	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{*graphViz, *labelConditions, *stripPrefix}
 
-	err := dumpGraph(ctx, os.Stdout, os.Stderr, compliance.FS, flag.Args()...)
+	err := dumpGraph(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...)
 	if err != nil {
 		if err == failNoneRequested {
-			flag.Usage()
+			flags.Usage()
 		}
 		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
 		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 from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
+			os.Exit(1)
+		}
+	}
 	os.Exit(0)
 }
 
diff --git a/tools/compliance/cmd/dumpresolutions/dumpresolutions.go b/tools/compliance/cmd/dumpresolutions/dumpresolutions.go
index d02c238..dc0cf88 100644
--- a/tools/compliance/cmd/dumpresolutions/dumpresolutions.go
+++ b/tools/compliance/cmd/dumpresolutions/dumpresolutions.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"bytes"
 	"flag"
 	"fmt"
 	"io"
@@ -24,15 +25,11 @@
 	"sort"
 	"strings"
 
+	"android/soong/response"
 	"android/soong/tools/compliance"
 )
 
 var (
-	conditions      = newMultiString("c", "License condition to resolve. (may be given multiple times)")
-	graphViz        = flag.Bool("dot", false, "Whether to output graphviz (i.e. dot) format.")
-	labelConditions = flag.Bool("label_conditions", false, "Whether to label target nodes with conditions.")
-	stripPrefix     = newMultiString("strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
-
 	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
 	failNoLicenses    = fmt.Errorf("No licenses found")
 )
@@ -57,8 +54,44 @@
 	return installPath
 }
 
-func init() {
-	flag.Usage = func() {
+// 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 space-separated Target ActsOn Origin Condition tuple for each
@@ -75,32 +108,52 @@
 
 Options:
 `, filepath.Base(os.Args[0]))
-		flag.PrintDefaults()
+		flags.PrintDefaults()
 	}
-}
 
-// newMultiString creates a flag that allows multiple values in an array.
-func newMultiString(name, usage string) *multiString {
-	var f multiString
-	flag.Var(&f, name, usage)
-	return &f
-}
+	conditions := newMultiString(flags, "c", "License condition to resolve. (may be given multiple times)")
+	graphViz := flags.Bool("dot", false, "Whether to output graphviz (i.e. dot) format.")
+	labelConditions := flags.Bool("label_conditions", false, "Whether to label target nodes with conditions.")
+	outputFile := flags.String("o", "-", "Where to write the output. (default stdout)")
+	stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
 
-// 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() {
-	flag.Parse()
+	flags.Parse(expandedArgs)
 
 	// Must specify at least one root target.
-	if flag.NArg() == 0 {
-		flag.Usage()
+	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
+	}
+
 	lcs := make([]compliance.LicenseCondition, 0, len(*conditions))
 	for _, name := range *conditions {
 		lcs = append(lcs, compliance.RecognizedConditionNames[name])
@@ -111,14 +164,21 @@
 		labelConditions: *labelConditions,
 		stripPrefix:     *stripPrefix,
 	}
-	_, err := dumpResolutions(ctx, os.Stdout, os.Stderr, compliance.FS, flag.Args()...)
+	_, err := dumpResolutions(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...)
 	if err != nil {
 		if err == failNoneRequested {
-			flag.Usage()
+			flags.Usage()
 		}
 		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
 		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 from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
+			os.Exit(1)
+		}
+	}
 	os.Exit(0)
 }
 
diff --git a/tools/compliance/cmd/htmlnotice/htmlnotice.go b/tools/compliance/cmd/htmlnotice/htmlnotice.go
index e98b272..1a49610 100644
--- a/tools/compliance/cmd/htmlnotice/htmlnotice.go
+++ b/tools/compliance/cmd/htmlnotice/htmlnotice.go
@@ -26,19 +26,13 @@
 	"path/filepath"
 	"strings"
 
+	"android/soong/response"
 	"android/soong/tools/compliance"
 
 	"github.com/google/blueprint/deptools"
 )
 
 var (
-	outputFile  = flag.String("o", "-", "Where to write the NOTICE text file. (default stdout)")
-	depsFile    = flag.String("d", "", "Where to write the deps file")
-	includeTOC  = flag.Bool("toc", true, "Whether to include a table of contents.")
-	product     = flag.String("product", "", "The name of the product for which the notice is generated.")
-	stripPrefix = newMultiString("strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
-	title       = flag.String("title", "", "The title of the notice file.")
-
 	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
 	failNoLicenses    = fmt.Errorf("No licenses found")
 )
@@ -70,23 +64,10 @@
 	return installPath
 }
 
-func init() {
-	flag.Usage = func() {
-		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
-
-Outputs an html NOTICE.html or gzipped NOTICE.html.gz file if the -o filename
-ends with ".gz".
-
-Options:
-`, filepath.Base(os.Args[0]))
-		flag.PrintDefaults()
-	}
-}
-
 // newMultiString creates a flag that allows multiple values in an array.
-func newMultiString(name, usage string) *multiString {
+func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
 	var f multiString
-	flag.Var(&f, name, usage)
+	flags.Var(&f, name, usage)
 	return &f
 }
 
@@ -97,16 +78,57 @@
 func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
 
 func main() {
-	flag.Parse()
+	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 html NOTICE.html or gzipped NOTICE.html.gz file if the -o filename
+ends with ".gz".
+
+Options:
+`, filepath.Base(os.Args[0]))
+		flags.PrintDefaults()
+	}
+
+	outputFile := flags.String("o", "-", "Where to write the NOTICE text file. (default stdout)")
+	depsFile := flags.String("d", "", "Where to write the deps file")
+	includeTOC := flags.Bool("toc", true, "Whether to include a table of contents.")
+	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)")
+	title := flags.String("title", "", "The title of the notice file.")
+
+	flags.Parse(expandedArgs)
 
 	// Must specify at least one root target.
-	if flag.NArg() == 0 {
-		flag.Usage()
+	if flags.NArg() == 0 {
+		flags.Usage()
 		os.Exit(2)
 	}
 
 	if len(*outputFile) == 0 {
-		flag.Usage()
+		flags.Usage()
 		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
 		os.Exit(2)
 	} else {
@@ -143,10 +165,10 @@
 
 	ctx := &context{ofile, os.Stderr, compliance.FS, *includeTOC, *product, *stripPrefix, *title, &deps}
 
-	err := htmlNotice(ctx, flag.Args()...)
+	err := htmlNotice(ctx, flags.Args()...)
 	if err != nil {
 		if err == failNoneRequested {
-			flag.Usage()
+			flags.Usage()
 		}
 		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
 		os.Exit(1)
diff --git a/tools/compliance/cmd/listshare/listshare.go b/tools/compliance/cmd/listshare/listshare.go
index 7f4038b..31bd1b2 100644
--- a/tools/compliance/cmd/listshare/listshare.go
+++ b/tools/compliance/cmd/listshare/listshare.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"bytes"
 	"flag"
 	"fmt"
 	"io"
@@ -24,12 +25,41 @@
 	"sort"
 	"strings"
 
+	"android/soong/response"
 	"android/soong/tools/compliance"
 )
 
-func init() {
-	flag.Usage = func() {
-		fmt.Fprintf(os.Stderr, `Usage: %s file.meta_lic {file.meta_lic...}
+var (
+	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
+	failNoLicenses    = fmt.Errorf("No licenses found")
+)
+
+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 {-o outfile} file.meta_lic {file.meta_lic...}
 
 Outputs a csv file with 1 project per line in the first field followed
 by target:condition pairs describing why the project must be shared.
@@ -39,30 +69,61 @@
 restricted (e.g. GPL) or reciprocal (e.g. MPL).
 `, filepath.Base(os.Args[0]))
 	}
-}
 
-var (
-	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
-	failNoLicenses    = fmt.Errorf("No licenses found")
-)
+	outputFile := flags.String("o", "-", "Where to write the list of projects to share. (default stdout)")
 
-func main() {
-	flag.Parse()
+	flags.Parse(expandedArgs)
 
 	// Must specify at least one root target.
-	if flag.NArg() == 0 {
-		flag.Usage()
+	if flags.NArg() == 0 {
+		flags.Usage()
 		os.Exit(2)
 	}
 
-	err := listShare(os.Stdout, os.Stderr, compliance.FS, flag.Args()...)
+	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
+	}
+
+	err := listShare(ofile, os.Stderr, compliance.FS, flags.Args()...)
 	if err != nil {
 		if err == failNoneRequested {
-			flag.Usage()
+			flags.Usage()
 		}
 		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
 		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 from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
+			os.Exit(1)
+		}
+	}
 	os.Exit(0)
 }
 
@@ -76,7 +137,7 @@
 	// Read the license graph from the license metadata files (*.meta_lic).
 	licenseGraph, err := compliance.ReadLicenseGraph(rootFS, stderr, files)
 	if err != nil {
-		return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
+		return fmt.Errorf("Unable to read license metadata file(s) %q from %q: %v\n", files, os.Getenv("PWD"), err)
 	}
 	if licenseGraph == nil {
 		return failNoLicenses
diff --git a/tools/compliance/cmd/rtrace/rtrace.go b/tools/compliance/cmd/rtrace/rtrace.go
index 91171c4..667cdce 100644
--- a/tools/compliance/cmd/rtrace/rtrace.go
+++ b/tools/compliance/cmd/rtrace/rtrace.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"bytes"
 	"flag"
 	"fmt"
 	"io"
@@ -24,21 +25,19 @@
 	"sort"
 	"strings"
 
+	"android/soong/response"
 	"android/soong/tools/compliance"
 )
 
 var (
-	sources         = newMultiString("rtrace", "Projects or metadata files to trace back from. (required; multiple allowed)")
-	stripPrefix     = newMultiString("strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
-
 	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
 	failNoSources     = fmt.Errorf("\nNo projects or metadata files to trace back from")
 	failNoLicenses    = fmt.Errorf("No licenses found")
 )
 
 type context struct {
-	sources         []string
-	stripPrefix     []string
+	sources     []string
+	stripPrefix []string
 }
 
 func (ctx context) strip(installPath string) string {
@@ -54,8 +53,44 @@
 	return installPath
 }
 
-func init() {
-	flag.Usage = func() {
+// 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 space-separated Target ActsOn Origin Condition tuple for each
@@ -72,50 +107,75 @@
 
 Options:
 `, filepath.Base(os.Args[0]))
-		flag.PrintDefaults()
+		flags.PrintDefaults()
 	}
-}
 
-// newMultiString creates a flag that allows multiple values in an array.
-func newMultiString(name, usage string) *multiString {
-	var f multiString
-	flag.Var(&f, name, usage)
-	return &f
-}
+	outputFile := flags.String("o", "-", "Where to write the output. (default stdout)")
+	sources := newMultiString(flags, "rtrace", "Projects or metadata files to trace back from. (required; multiple allowed)")
+	stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
 
-// 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() {
-	flag.Parse()
+	flags.Parse(expandedArgs)
 
 	// Must specify at least one root target.
-	if flag.NArg() == 0 {
-		flag.Usage()
+	if flags.NArg() == 0 {
+		flags.Usage()
 		os.Exit(2)
 	}
 
 	if len(*sources) == 0 {
-		flag.Usage()
+		flags.Usage()
 		fmt.Fprintf(os.Stderr, "\nMust specify at least 1 --rtrace source.\n")
 		os.Exit(2)
 	}
 
-	ctx := &context{
-		sources:         *sources,
-		stripPrefix:     *stripPrefix,
+	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)
+		}
 	}
-	_, err := traceRestricted(ctx, os.Stdout, os.Stderr, compliance.FS, flag.Args()...)
+
+	var ofile io.Writer
+	ofile = os.Stdout
+	var obuf *bytes.Buffer
+	if *outputFile != "-" {
+		obuf = &bytes.Buffer{}
+		ofile = obuf
+	}
+
+	ctx := &context{
+		sources:     *sources,
+		stripPrefix: *stripPrefix,
+	}
+	_, err := traceRestricted(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...)
 	if err != nil {
 		if err == failNoneRequested {
-			flag.Usage()
+			flags.Usage()
 		}
 		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
 		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 from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
+			os.Exit(1)
+		}
+	}
 	os.Exit(0)
 }
 
diff --git a/tools/compliance/cmd/shippedlibs/shippedlibs.go b/tools/compliance/cmd/shippedlibs/shippedlibs.go
index 9d25dd3..add6dd6 100644
--- a/tools/compliance/cmd/shippedlibs/shippedlibs.go
+++ b/tools/compliance/cmd/shippedlibs/shippedlibs.go
@@ -39,9 +39,6 @@
 	rootFS fs.FS
 }
 
-func init() {
-}
-
 func main() {
 	var expandedArgs []string
 	for _, arg := range os.Args[1:] {
diff --git a/tools/compliance/cmd/textnotice/textnotice.go b/tools/compliance/cmd/textnotice/textnotice.go
index cfa0859..9beaf58 100644
--- a/tools/compliance/cmd/textnotice/textnotice.go
+++ b/tools/compliance/cmd/textnotice/textnotice.go
@@ -25,18 +25,13 @@
 	"path/filepath"
 	"strings"
 
+	"android/soong/response"
 	"android/soong/tools/compliance"
 
 	"github.com/google/blueprint/deptools"
 )
 
 var (
-	outputFile  = flag.String("o", "-", "Where to write the NOTICE text file. (default stdout)")
-	depsFile    = flag.String("d", "", "Where to write the deps file")
-	product     = flag.String("product", "", "The name of the product for which the notice is generated.")
-	stripPrefix = newMultiString("strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
-	title       = flag.String("title", "", "The title of the notice file.")
-
 	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
 	failNoLicenses    = fmt.Errorf("No licenses found")
 )
@@ -67,22 +62,10 @@
 	return installPath
 }
 
-func init() {
-	flag.Usage = func() {
-		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
-
-Outputs a text NOTICE file.
-
-Options:
-`, filepath.Base(os.Args[0]))
-		flag.PrintDefaults()
-	}
-}
-
 // newMultiString creates a flag that allows multiple values in an array.
-func newMultiString(name, usage string) *multiString {
+func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
 	var f multiString
-	flag.Var(&f, name, usage)
+	flags.Var(&f, name, usage)
 	return &f
 }
 
@@ -93,16 +76,55 @@
 func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
 
 func main() {
-	flag.Parse()
+	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 text NOTICE file.
+
+Options:
+`, filepath.Base(os.Args[0]))
+		flags.PrintDefaults()
+	}
+
+	outputFile := flags.String("o", "-", "Where to write the NOTICE text 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)")
+	title := flags.String("title", "", "The title of the notice file.")
+
+	flags.Parse(expandedArgs)
 
 	// Must specify at least one root target.
-	if flag.NArg() == 0 {
-		flag.Usage()
+	if flags.NArg() == 0 {
+		flags.Usage()
 		os.Exit(2)
 	}
 
 	if len(*outputFile) == 0 {
-		flag.Usage()
+		flags.Usage()
 		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
 		os.Exit(2)
 	} else {
@@ -139,10 +161,10 @@
 
 	ctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, *title, &deps}
 
-	err := textNotice(ctx, flag.Args()...)
+	err := textNotice(ctx, flags.Args()...)
 	if err != nil {
 		if err == failNoneRequested {
-			flag.Usage()
+			flags.Usage()
 		}
 		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
 		os.Exit(1)
diff --git a/tools/compliance/cmd/xmlnotice/xmlnotice.go b/tools/compliance/cmd/xmlnotice/xmlnotice.go
index 84859d7..2097b7c 100644
--- a/tools/compliance/cmd/xmlnotice/xmlnotice.go
+++ b/tools/compliance/cmd/xmlnotice/xmlnotice.go
@@ -26,18 +26,13 @@
 	"path/filepath"
 	"strings"
 
+	"android/soong/response"
 	"android/soong/tools/compliance"
 
 	"github.com/google/blueprint/deptools"
 )
 
 var (
-	outputFile  = flag.String("o", "-", "Where to write the NOTICE xml or xml.gz file. (default stdout)")
-	depsFile    = flag.String("d", "", "Where to write the deps file")
-	product     = flag.String("product", "", "The name of the product for which the notice is generated.")
-	stripPrefix = newMultiString("strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
-	title       = flag.String("title", "", "The title of the notice file.")
-
 	failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
 	failNoLicenses    = fmt.Errorf("No licenses found")
 )
@@ -68,23 +63,10 @@
 	return installPath
 }
 
-func init() {
-	flag.Usage = func() {
-		fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
-
-Outputs an xml NOTICE.xml or gzipped NOTICE.xml.gz file if the -o filename ends
-with ".gz".
-
-Options:
-`, filepath.Base(os.Args[0]))
-		flag.PrintDefaults()
-	}
-}
-
 // newMultiString creates a flag that allows multiple values in an array.
-func newMultiString(name, usage string) *multiString {
+func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
 	var f multiString
-	flag.Var(&f, name, usage)
+	flags.Var(&f, name, usage)
 	return &f
 }
 
@@ -95,16 +77,56 @@
 func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
 
 func main() {
-	flag.Parse()
+	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 xml NOTICE.xml or gzipped NOTICE.xml.gz file if the -o filename ends
+with ".gz".
+
+Options:
+`, filepath.Base(os.Args[0]))
+		flags.PrintDefaults()
+	}
+
+	outputFile := flags.String("o", "-", "Where to write the NOTICE xml or xml.gz 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)")
+	title := flags.String("title", "", "The title of the notice file.")
+
+	flags.Parse(expandedArgs)
 
 	// Must specify at least one root target.
-	if flag.NArg() == 0 {
-		flag.Usage()
+	if flags.NArg() == 0 {
+		flags.Usage()
 		os.Exit(2)
 	}
 
 	if len(*outputFile) == 0 {
-		flag.Usage()
+		flags.Usage()
 		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
 		os.Exit(2)
 	} else {
@@ -141,10 +163,10 @@
 
 	ctx := &context{ofile, os.Stderr, compliance.FS, *product, *stripPrefix, *title, &deps}
 
-	err := xmlNotice(ctx, flag.Args()...)
+	err := xmlNotice(ctx, flags.Args()...)
 	if err != nil {
 		if err == failNoneRequested {
-			flag.Usage()
+			flags.Usage()
 		}
 		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
 		os.Exit(1)
diff --git a/tools/releasetools/merge/OWNERS b/tools/releasetools/merge/OWNERS
index 9012e3a..0eddee2 100644
--- a/tools/releasetools/merge/OWNERS
+++ b/tools/releasetools/merge/OWNERS
@@ -1,3 +1,4 @@
-danielnorman@google.com
+deyaoren@google.com
+haamed@google.com
 jgalmes@google.com
 rseymour@google.com