Merge "API coverage Copy generated API list during building cc_library to cc_apis/"
diff --git a/core/Makefile b/core/Makefile
index 24302d4..f6411ec 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -4377,6 +4377,7 @@
   verity_signer \
   verity_verifier \
   zipalign \
+  zucchini \
 
 # Additional tools to unpack and repack the apex file.
 INTERNAL_OTATOOLS_MODULES += \
diff --git a/core/config.mk b/core/config.mk
index acdf15e..94235d9 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -259,7 +259,7 @@
 
 define add_soong_config_namespace
 $(eval SOONG_CONFIG_NAMESPACES += $1) \
-$(eval SOONG_CONFIG_$1 :=)
+$(eval SOONG_CONFIG_$(strip $1) :=)
 endef
 
 # The add_soong_config_var function adds a a list of soong config variables to
@@ -268,8 +268,8 @@
 # $1 is the namespace. $2 is the list of variables.
 # Ex: $(call add_soong_config_var,acme,COOL_FEATURE_A COOL_FEATURE_B)
 define add_soong_config_var
-$(eval SOONG_CONFIG_$1 += $2) \
-$(foreach v,$2,$(eval SOONG_CONFIG_$1_$v := $($v)))
+$(eval SOONG_CONFIG_$(strip $1) += $2) \
+$(foreach v,$(strip $2),$(eval SOONG_CONFIG_$(strip $1)_$v := $($v)))
 endef
 
 # The add_soong_config_var_value function defines a make variable and also adds
diff --git a/core/definitions.mk b/core/definitions.mk
index 0fd023a..981c6cb 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2329,6 +2329,7 @@
 define add-jar-resources-to-package
   rm -rf $(3)
   mkdir -p $(3)
+  zipinfo -1 $(2) > /dev/null
   unzip -qo $(2) -d $(3) $$(zipinfo -1 $(2) | grep -v -E "\.class$$")
   $(JAR) uf $(1) $(call jar-args-sorted-files-in-directory,$(3))
 endef
diff --git a/core/envsetup.rbc b/core/envsetup.rbc
index 451623b..69c4989 100644
--- a/core/envsetup.rbc
+++ b/core/envsetup.rbc
@@ -188,9 +188,18 @@
     if g["HOST_BUILD_TYPE"] not in ["release", "debug"]:
         fail("HOST_BUILD_TYPE must be either release or debug, not '%s'" % g["HOST_BUILD_TYPE"])
 
+    g.update([
+	    ("TARGET_COPY_OUT_VENDOR", "||VENDOR-PATH-PH||"),
+    	("TARGET_COPY_OUT_PRODUCT", "||PRODUCT-PATH-PH||"),
+	    ("TARGET_COPY_OUT_PRODUCT_SERVICES", "||PRODUCT-PATH-PH||"),
+	    ("TARGET_COPY_OUT_SYSTEM_EXT", "||SYSTEM_EXT-PATH-PH||"),
+	    ("TARGET_COPY_OUT_ODM", "||ODM-PATH-PH||"),
+	    ("TARGET_COPY_OUT_VENDOR_DLKM", "||VENDOR_DLKM-PATH-PH||"),
+	    ("TARGET_COPY_OUT_ODM_DLKM", "||ODM_DLKM-PATH-PH||"),
+        ])
+
     # TODO(asmundak): there is more stuff in envsetup.mk lines 249-292, but
     # it does not seem to affect product configuration. Revisit this.
-
     g["ART_APEX_JARS"] = [
         "com.android.art:core-oj",
         "com.android.art:core-libart",
diff --git a/core/main.mk b/core/main.mk
index 1d00936..e1cfead 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -120,9 +120,6 @@
 ifndef SKIP_BOOT_JARS_CHECK
 SKIP_BOOT_JARS_CHECK := true
 endif
-# Mainline modules prebuilts do not support coverage. Build them from source.
-# See b/159241638
-MODULE_BUILD_FROM_SOURCE := true
 endif
 
 ifdef TARGET_ARCH_SUITE
@@ -1242,14 +1239,43 @@
 # Name resolution for LOCAL_REQUIRED_MODULES:
 #   See the select-bitness-of-required-modules definition.
 # $(1): product makefile
+
+# TODO(asmundak):
+# `product-installed-files` and `host-installed-files` macros below used to
+# call `get-product-var` directly to obtain per-file configuration variable
+# values (the value of variable FOO is fetched from PRODUCT.<product-makefile>.FOO).
+# Starlark-based configuration does not maintain per-file variable variable
+# values. To work around this problem, we utilize the fact that
+# `product-installed-files` and `host-installed-files` are called only in
+# two places:
+# 1. For the top-level product makefile (in this file). In this case
+#    $(call get-product-var <product>, FOO) is the same as $(FOO) as the
+#    product configuration has been run already. Therefore we define
+#    _product-var macro to pick the values directly from product config
+#    variables when using Starlark-based configuration.
+# 2. To check the path requirements (in artifact_path_requirements.mk).
+#    Starlark-based configuration does not perform this check at the moment.
+# In the longer run most of the logic of this file will be moved to the
+# Starlark.
+
+ifndef RBC_PRODUCT_CONFIG
+define _product-var
+  $(call get-product-var,$(1),$(2))
+endef
+else
+define _product-var
+  $(call $(2))
+endef
+endif
+
 define product-installed-files
   $(eval _pif_modules := \
-    $(call get-product-var,$(1),PRODUCT_PACKAGES) \
-    $(if $(filter eng,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ENG)) \
-    $(if $(filter debug,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \
-    $(if $(filter tests,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_TESTS)) \
-    $(if $(filter asan,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \
-    $(if $(filter java_coverage,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \
+    $(call _product-var,$(1),PRODUCT_PACKAGES) \
+    $(if $(filter eng,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_ENG)) \
+    $(if $(filter debug,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \
+    $(if $(filter tests,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_TESTS)) \
+    $(if $(filter asan,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \
+    $(if $(filter java_coverage,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \
     $(call auto-included-modules) \
   ) \
   $(eval ### Filter out the overridden packages and executables before doing expansion) \
@@ -1260,13 +1286,13 @@
   $(call expand-required-modules,_pif_modules,$(_pif_modules),$(_pif_overrides)) \
   $(filter-out $(HOST_OUT_ROOT)/%,$(call module-installed-files, $(_pif_modules))) \
   $(call resolve-product-relative-paths,\
-    $(foreach cf,$(call get-product-var,$(1),PRODUCT_COPY_FILES),$(call word-colon,2,$(cf))))
+    $(foreach cf,$(call _product-var,$(1),PRODUCT_COPY_FILES),$(call word-colon,2,$(cf))))
 endef
 
 # Similar to product-installed-files above, but handles PRODUCT_HOST_PACKAGES instead
 # This does support the :32 / :64 syntax, but does not support module overrides.
 define host-installed-files
-  $(eval _hif_modules := $(call get-product-var,$(1),PRODUCT_HOST_PACKAGES)) \
+  $(eval _hif_modules := $(call _product-var,$(1),PRODUCT_HOST_PACKAGES)) \
   $(eval ### Split host vs host cross modules) \
   $(eval _hcif_modules := $(filter host_cross_%,$(_hif_modules))) \
   $(eval _hif_modules := $(filter-out host_cross_%,$(_hif_modules))) \
@@ -1351,7 +1377,7 @@
 
   # Verify the artifact path requirements made by included products.
   is_asan := $(if $(filter address,$(SANITIZE_TARGET)),true)
-  ifneq (true,$(or $(is_asan),$(DISABLE_ARTIFACT_PATH_REQUIREMENTS)))
+  ifeq (,$(or $(is_asan),$(DISABLE_ARTIFACT_PATH_REQUIREMENTS),$(RBC_PRODUCT_CONFIG)))
     include $(BUILD_SYSTEM)/artifact_path_requirements.mk
   endif
 else
diff --git a/core/product-graph.mk b/core/product-graph.mk
index 968d01b..de4e581 100644
--- a/core/product-graph.mk
+++ b/core/product-graph.mk
@@ -81,6 +81,7 @@
 $(products_graph): PRIVATE_PRODUCTS_FILTER := $(products_list)
 
 $(products_graph): $(this_makefile)
+ifeq (,$(RBC_PRODUCT_CONFIG)$(RBC_NO_PRODUCT_GRAPH))
 	@echo Product graph DOT: $@ for $(PRIVATE_PRODUCTS_FILTER)
 	$(hide) echo 'digraph {' > $@.in
 	$(hide) echo 'graph [ ratio=.5 ];' >> $@.in
@@ -89,6 +90,10 @@
 	$(foreach p,$(PRIVATE_PRODUCTS),$(call emit-product-node-props,$(p),$@.in))
 	$(hide) echo '}' >> $@.in
 	$(hide) build/make/tools/filter-product-graph.py $(PRIVATE_PRODUCTS_FILTER) < $@.in > $@
+else
+	@echo RBC_PRODUCT_CONFIG and RBC_NO_PRODUCT_GRAPH should be unset to generate product graph
+	false
+endif
 
 # Evaluates to the name of the product file
 # $(1) product file
@@ -143,6 +148,7 @@
 	$(hide) cat $$< | build/make/tools/product_debug.py > $$@
 endef
 
+ifeq (,$(RBC_PRODUCT_CONFIG)$(RBC_NO_PRODUCT_GRAPH))
 product_debug_files:=
 $(foreach p,$(all_products), \
 			$(eval $(call transform-product-debug, $(p))) \
@@ -154,3 +160,8 @@
 	@echo Product graph .dot file: $(products_graph)
 	@echo Command to convert to pdf: dot -Tpdf -Nshape=box -o $(OUT_DIR)/products.pdf $(products_graph)
 	@echo Command to convert to svg: dot -Tsvg -Nshape=box -o $(OUT_DIR)/products.svg $(products_graph)
+else
+.PHONY: product-graph
+	@echo RBC_PRODUCT_CONFIG and RBC_NO_PRODUCT_GRAPH should be unset to generate product graph
+	false
+endif
\ No newline at end of file
diff --git a/core/product.mk b/core/product.mk
index f6347e8..9aaf5eb 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -372,11 +372,6 @@
 _product_list_vars += PRODUCT_PACKAGE_NAME_OVERRIDES
 _product_list_vars += PRODUCT_CERTIFICATE_OVERRIDES
 
-# A list of <overridden-apex>:<override-apex> pairs that specifies APEX module
-# overrides to be applied to the APEX names in the boot jar variables
-# (PRODUCT_BOOT_JARS, PRODUCT_APEX_BOOT_JARS etc).
-_product_list_vars += PRODUCT_BOOT_JAR_MODULE_OVERRIDES
-
 # Controls for whether different partitions are built for the current product.
 _product_single_value_vars += PRODUCT_BUILD_SYSTEM_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_SYSTEM_OTHER_IMAGE
diff --git a/core/product_config.mk b/core/product_config.mk
index 99ff8aa..54fbb7d 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -87,6 +87,19 @@
 $(foreach f,$(1),$(f):$(2)/$(notdir $(f)))
 endef
 
+#
+# Convert the list of file names to the list of PRODUCT_COPY_FILES items
+# $(1): from pattern
+# $(2): to pattern
+# $(3): file names
+# E.g., calling product-copy-files-by-pattern with
+#   (from/%, to/%, a b)
+# returns
+#   from/a:to/a from/b:to/b
+define product-copy-files-by-pattern
+$(join $(patsubst %,$(1),$(3)),$(patsubst %,:$(2),$(3)))
+endef
+
 # ---------------------------------------------------------------
 # Check for obsolete PRODUCT- and APP- goals
 ifeq ($(CALLED_FROM_SETUP),true)
@@ -172,11 +185,24 @@
 ifneq (1,$(words $(current_product_makefile)))
 $(error Product "$(TARGET_PRODUCT)" ambiguous: matches $(current_product_makefile))
 endif
+
+ifndef RBC_PRODUCT_CONFIG
 $(call import-products, $(current_product_makefile))
+else
+  rbcscript=build/soong/scripts/rbc-run
+  rc := $(shell $(rbcscript) $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT) >$(OUT_DIR)/rbctemp.mk || echo $$?)
+  ifneq (,$(rc))
+    $(error product configuration converter failed: $(rc))
+  endif
+  include $(OUT_DIR)/rbctemp.mk
+  PRODUCTS += $(current_product_makefile)
+endif
 endif  # Import all or just the current product makefile
 
+ifndef RBC_PRODUCT_CONFIG
 # Quick check
 $(check-all-products)
+endif
 
 ifeq ($(SKIP_ARTIFACT_PATH_REQUIREMENT_PRODUCTS_CHECK),)
 # Import all the products that have made artifact path requirements, so that we can verify
@@ -196,6 +222,7 @@
 $(dump-products)
 endif
 
+ifndef RBC_PRODUCT_CONFIG
 # Convert a short name like "sooner" into the path to the product
 # file defining that product.
 #
@@ -208,6 +235,9 @@
 ############################################################################
 # Strip and assign the PRODUCT_ variables.
 $(call strip-product-vars)
+else
+INTERNAL_PRODUCT := $(current_product_makefile)
+endif
 
 current_product_makefile :=
 all_product_makefiles :=
@@ -268,19 +298,6 @@
 # All APEX jars come after /system and /system_ext jars, so adding core-icu4j at the end of the list
 PRODUCT_BOOT_JARS += com.android.i18n:core-icu4j
 
-# Replaces references to overridden boot jar modules in a boot jars variable.
-# $(1): Name of a boot jars variable with <apex>:<jar> pairs.
-define replace-boot-jar-module-overrides
-  $(foreach pair,$(PRODUCT_BOOT_JAR_MODULE_OVERRIDES),\
-    $(eval _rbjmo_from := $(call word-colon,1,$(pair)))\
-    $(eval _rbjmo_to := $(call word-colon,2,$(pair)))\
-    $(eval $(1) := $(patsubst $(_rbjmo_from):%,$(_rbjmo_to):%,$($(1)))))
-endef
-
-$(call replace-boot-jar-module-overrides,PRODUCT_BOOT_JARS)
-$(call replace-boot-jar-module-overrides,PRODUCT_APEX_BOOT_JARS)
-$(call replace-boot-jar-module-overrides,ART_APEX_JARS)
-
 # The extra system server jars must be appended at the end after common system server jars.
 PRODUCT_SYSTEM_SERVER_JARS += $(PRODUCT_SYSTEM_SERVER_JARS_EXTRA)
 
diff --git a/core/product_config.rbc b/core/product_config.rbc
index d9a2a72..ef0d0c9 100644
--- a/core/product_config.rbc
+++ b/core/product_config.rbc
@@ -79,6 +79,10 @@
             if attr == _soong_config_namespaces_key:
                 __print_attr("SOONG_CONFIG_NAMESPACES", val.keys())
                 for nsname, nsvars in sorted(val.items()):
+                    # Define SOONG_CONFIG_<ns> for Make, othewise
+                    # it cannot be added to .KATI_READONLY list
+                    if _options.format == "make":
+                        print("SOONG_CONFIG_" + nsname, ":=")
                     for var, val in sorted(nsvars.items()):
                         __print_attr("SOONG_CONFIG_%s_%s" % (nsname, var), val)
             elif attr not in _globals_base:
@@ -369,9 +373,14 @@
         if type(val) == "list":
             val.append(_indirect(pcm_name))
 
+def __base(path):
+    """Returns basename."""
+    return path.rsplit("/",1)[-1]
+
+
 def _copy_files(l, outdir):
     """Generate <item>:<outdir>/item for each item."""
-    return ["%s:%s/%s" % (item, outdir, item) for item in __words(l)]
+    return ["%s:%s/%s" % (path, outdir, __base(path)) for path in __words(l)]
 
 def _copy_if_exists(path_pair):
     """If from file exists, returns [from:to] pair."""
@@ -392,7 +401,8 @@
 
 def _find_and_copy(pattern, from_dir, to_dir):
     """Return a copy list for the files matching the pattern."""
-    return ["%s/%s:%s/%s" % (from_dir, f, to_dir, f) for f in rblf_wildcard(pattern, from_dir)]
+    return ["%s/%s:%s/%s" % (
+        from_dir, f, to_dir, f) for f in rblf_find_files(from_dir, pattern, only_files=1)]
 
 def _filter_out(pattern, text):
     """Return all the words from `text' that do not match any word in `pattern'.
@@ -459,6 +469,28 @@
     print(message)
 
 
+def __mkparse_pattern(pattern):
+    """Parses Make's patsubst pattern."""
+    in_escape = False
+    res = []
+    acc = ""
+    for c in pattern.elems():
+        if in_escape:
+            in_escape = False
+            acc += c
+        elif c == '\\':
+            in_escape = True
+        elif c == '%' and not res:
+            res.append(acc)
+            acc = ''
+        else:
+            acc += c
+    if in_escape:
+        acc += '\\'
+    res.append(acc)
+    return res
+
+
 def __mkpatsubst_word(parsed_pattern,parsed_subst, word):
     (before, after) = parsed_pattern
     if not word.startswith(before):
@@ -476,16 +508,14 @@
     Tokenizes `s` (unless it is already a list), and then performs a simple
     wildcard substitution (in other words, `foo%bar` pattern is equivalent to
     the regular expression `^foo(.*)bar$, and the first `%` in replacement is
-    $1 in regex terms). Escaping % is not supported
+    $1 in regex terms).
     """
-    if pattern.find("\\") >= 0:
-        fail("'\\' in pattern is not allowed")
-    parsed_pattern = pattern.split("%", 1)
+    parsed_pattern = __mkparse_pattern(pattern)
     words = s if type(s) == "list" else _mkstrip(s).split(" ")
     if len(parsed_pattern) == 1:
         out_words = [ replacement if x == pattern else x for x in words]
     else:
-        parsed_replacement = replacement.split("%", 1)
+        parsed_replacement = __mkparse_pattern(replacement)
         out_words = [__mkpatsubst_word(parsed_pattern, parsed_replacement, x) for x in words]
     return out_words if type(s) == "list" else " ".join(out_words)
 
@@ -518,6 +548,22 @@
     return s.replace(old, new)
 
 
+def _product_copy_files_by_pattern(src, dest, s):
+    """Creates a copy list.
+
+    For each item in a given list, create <from>:<to> pair, where <from> and
+    <to> are the results of applying Make-style patsubst of <src> and <dest>
+    respectively. E.g. the result of calling this function with
+    ("foo/%", "bar/%", ["a", "b"])  will be
+    ["foo/a:bar/a", "foo/b:bar/b"].
+    """
+    parsed_src = __mkparse_pattern(src)
+    parsed_dest = __mkparse_pattern(dest)
+    parsed_percent = ["", ""]
+    words = s if type(s) == "list" else _mkstrip(s).split(" ")
+    return [ __mkpatsubst_word(parsed_percent, parsed_src, x) + ":" + __mkpatsubst_word(parsed_percent, parsed_dest, x) for x in words]
+
+
 def __get_options():
     """Returns struct containing runtime global settings."""
     settings = dict(
@@ -573,6 +619,7 @@
     mksubst = _mksubst,
     printvars = _printvars,
     product_configuration = _product_configuration,
+    product_copy_files_by_pattern = _product_copy_files_by_pattern,
     require_artifacts_in_path = _require_artifacts_in_path,
     require_artifacts_in_path_relaxed = _require_artifacts_in_path_relaxed,
     setdefault = _setdefault,
diff --git a/core/tasks/tools/build_custom_image.mk b/core/tasks/tools/build_custom_image.mk
index 4721591..8b766ae 100644
--- a/core/tasks/tools/build_custom_image.mk
+++ b/core/tasks/tools/build_custom_image.mk
@@ -57,12 +57,13 @@
 my_kernel_module_copy_files :=
 my_custom_image_modules_var := BOARD_$(strip $(call to-upper,$(my_custom_image_name)))_KERNEL_MODULES
 ifdef $(my_custom_image_modules_var)
-  my_kernel_module_copy_files += $(call build-image-kernel-modules,$(my_custom_image_modules_var),$(my_staging_dir),$(my_custom_image_name)/,$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)))
+  my_kernel_module_copy_files += $(call build-image-kernel-modules,$($(my_custom_image_modules_var)),$(my_staging_dir),$(CUSTOM_IMAGE_MOUNT_POINT),$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)),$($(my_custom_image_modules_var)),modules.load,,$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)_stripped))
+  my_copy_pairs += $(my_kernel_module_copy_files)
 endif
 
 # Collect CUSTOM_IMAGE_COPY_FILES.
 my_image_copy_files :=
-$(foreach f,$(CUSTOM_IMAGE_COPY_FILES) $(my_kernel_module_copy_files),\
+$(foreach f,$(CUSTOM_IMAGE_COPY_FILES),\
   $(eval pair := $(subst :,$(space),$(f)))\
   $(eval src := $(word 1,$(pair)))\
   $(eval my_image_copy_files += $(src))\
diff --git a/envsetup.sh b/envsetup.sh
index 77b2247..6085f7a 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -1661,12 +1661,19 @@
     if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then
         color_failed=$'\E'"[0;31m"
         color_success=$'\E'"[0;32m"
+        color_warning=$'\E'"[0;33m"
         color_reset=$'\E'"[00m"
     else
         color_failed=""
         color_success=""
         color_reset=""
     fi
+
+    if [[ "x${USE_RBE}" == "x" && $mins -gt 15 && "${ANDROID_BUILD_ENVIRONMENT_CONFIG}" == "googler" ]]; then
+        echo
+        echo "${color_warning}Start using RBE (http://go/build-fast) to get faster builds!${color_reset}"
+    fi
+
     echo
     if [ $ret -eq 0 ] ; then
         echo -n "${color_success}#### build completed successfully "
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index de3d63d..d121484 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -369,6 +369,7 @@
     profcollectd \
     profcollectctl \
     remount \
+    servicedispatcher \
     showmap \
     sqlite3 \
     ss \
diff --git a/target/product/emulator_vendor.mk b/target/product/emulator_vendor.mk
index 4d46358..e6db0f8 100644
--- a/target/product/emulator_vendor.mk
+++ b/target/product/emulator_vendor.mk
@@ -37,7 +37,7 @@
 
 #watchdog tiggers reboot because location service is not
 #responding, disble it for now.
-#still keep it on internal master as it is still working
+#still keep it on internal main (master) as it is still working
 #once it is fixed in aosp, remove this block of comment.
 #PRODUCT_VENDOR_PROPERTIES += \
 #config.disable_location=true
diff --git a/target/product/virtual_ab_ota/compression_with_xor.mk b/target/product/virtual_ab_ota/compression_with_xor.mk
new file mode 100644
index 0000000..7d92532
--- /dev/null
+++ b/target/product/virtual_ab_ota/compression_with_xor.mk
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2021 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.
+#
+
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/compression.mk)
+
+
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled=true
diff --git a/tests/device.rbc b/tests/device.rbc
index b57dbf9..feefcf7 100644
--- a/tests/device.rbc
+++ b/tests/device.rbc
@@ -45,9 +45,11 @@
   cfg["PRODUCT_COPY_FILES"] += ["device_from:device_to"]
   _include1_init(g, handle)
   cfg["PRODUCT_PACKAGES"] += ["dev_after"]
-  cfg["PRODUCT_COPY_FILES"] += (rblf.find_and_copy("audio_platform_info*.xml", "device/google/redfin/audio", "||VENDOR-PATH-PH||/etc") +
+  cfg["PRODUCT_COPY_FILES"] += (rblf.find_and_copy("audio_platform_info*.xml", "device/google/redfin", "||VENDOR-PATH-PH||/etc") +
       ["xyz:/etc/xyz"])
   cfg["PRODUCT_COPY_FILES"] += rblf.copy_files("x.xml y.xml", "/etc")
+  cfg["PRODUCT_COPY_FILES"] += rblf.copy_files(["from/sub/x", "from/sub/y"], "to")
+
   rblf.add_soong_config_namespace(g, "NS1")
   rblf.add_soong_config_var_value(g, "NS1", "v1", "abc")
   rblf.add_soong_config_var_value(g, "NS1", "v2", "def")
diff --git a/tests/run.rbc b/tests/run.rbc
index 15f6212..4d7166a 100644
--- a/tests/run.rbc
+++ b/tests/run.rbc
@@ -43,6 +43,9 @@
 assert_eq("azx b", rblf.mkpatsubst("az", "AZ", "azx  b"))
 assert_eq(["azx", "b"], rblf.mkpatsubst("az", "AZ", ["azx", "b"]))
 assert_eq("ABC", rblf.mkpatsubst("abc", "ABC", "abc"))
+assert_eq(["%/foo"], rblf.mkpatsubst("%", "\\%/%", ["foo"]))
+assert_eq(["foo/%"], rblf.mkpatsubst("%", "%/%", ["foo"]))
+assert_eq(["from/a:to/a", "from/b:to/b"], rblf.product_copy_files_by_pattern("from/%", "to/%", "a b"))
 
 globals, config = rblf.product_configuration("test/device", init)
 assert_eq(
@@ -50,10 +53,12 @@
       "PRODUCT_COPY_FILES": [
           "part_from:part_to",
           "device_from:device_to",
-          "device/google/redfin/audio/audio_platform_info_noextcodec_snd.xml:||VENDOR-PATH-PH||/etc/audio_platform_info_noextcodec_snd.xml",
+          "device/google/redfin/audio/audio_platform_info_noextcodec_snd.xml:||VENDOR-PATH-PH||/etc/audio/audio_platform_info_noextcodec_snd.xml",
           "xyz:/etc/xyz",
           "x.xml:/etc/x.xml",
           "y.xml:/etc/y.xml",
+          "from/sub/x:to/x",
+          "from/sub/y:to/y",
       ],
       "PRODUCT_HOST_PACKAGES": ["host"],
       "PRODUCT_PACKAGES": [
diff --git a/tools/rbcrun/README.md b/tools/rbcrun/README.md
index fb58c89..ecf8a24 100644
--- a/tools/rbcrun/README.md
+++ b/tools/rbcrun/README.md
@@ -68,6 +68,11 @@
 
 Returns `True`  if *file* exists
 
+#### rblf_find_files(*top*, *file-pattern*, only_files = 0)
+
+Returns all the paths under *top* whose basename matches *pattern* (which is a shell's glob pattern). If *only_files* is
+not zero, only the paths to the regular files are returned. The returned paths are relative to *top*.
+
 #### rblf_wildcard(*glob*, *top* = None)
 
 Expands *glob*. If *top* is supplied, expands "*top*/*glob*", then removes
diff --git a/tools/rbcrun/host.go b/tools/rbcrun/host.go
index 1e43334..7f4f72d 100644
--- a/tools/rbcrun/host.go
+++ b/tools/rbcrun/host.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"io/fs"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -118,7 +119,7 @@
 	if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &path); err != nil {
 		return starlark.None, err
 	}
-	if stat, err := os.Stat(path); err != nil || stat.IsDir() {
+	if _, err := os.Stat(path); err != nil {
 		return starlark.False, nil
 	}
 	return starlark.True, nil
@@ -170,6 +171,46 @@
 	return makeStringList(files), nil
 }
 
+// find(top, pattern, only_files = 0) returns all the paths under 'top'
+// whose basename matches 'pattern' (which is a shell's glob pattern).
+// If 'only_files' is non-zero, only the paths to the regular files are
+// returned. The returned paths are relative to 'top'.
+func find(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple,
+	kwargs []starlark.Tuple) (starlark.Value, error) {
+	var top, pattern string
+	var onlyFiles int
+	if err := starlark.UnpackArgs(b.Name(), args, kwargs,
+		"top", &top, "pattern", &pattern, "only_files?", &onlyFiles); err != nil {
+		return starlark.None, err
+	}
+	top = filepath.Clean(top)
+	pattern = filepath.Clean(pattern)
+	// Go's filepath.Walk is slow, consider using OS's find
+	var res []string
+	err := filepath.WalkDir(top, func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			if d != nil && d.IsDir() {
+				return fs.SkipDir
+			} else {
+				return nil
+			}
+		}
+		relPath := strings.TrimPrefix(path, top)
+		if len(relPath) > 0 && relPath[0] == os.PathSeparator {
+			relPath = relPath[1:]
+		}
+		// Do not return top-level dir
+		if len(relPath) == 0 {
+			return nil
+		}
+		if matched, err := filepath.Match(pattern, d.Name()); err == nil && matched && (onlyFiles == 0 || d.Type().IsRegular()) {
+			res = append(res, relPath)
+		}
+		return nil
+	})
+	return makeStringList(res), err
+}
+
 // shell(command) runs OS shell with given command and returns back
 // its output the same way as Make's $(shell ) function. The end-of-lines
 // ("\n" or "\r\n") are replaced with " " in the result, and the trailing
@@ -226,6 +267,8 @@
 		"rblf_env": structFromEnv(os.Environ()),
 		// To convert makefile's $(wildcard foo)
 		"rblf_file_exists": starlark.NewBuiltin("rblf_file_exists", fileExists),
+		// To convert find-copy-subdir and product-copy-files-by pattern
+		"rblf_find_files": starlark.NewBuiltin("rblf_find_files", find),
 		// To convert makefile's $(filter ...)/$(filter-out)
 		"rblf_regex": starlark.NewBuiltin("rblf_regex", regexMatch),
 		// To convert makefile's $(shell cmd)
diff --git a/tools/rbcrun/testdata/file_ops.star b/tools/rbcrun/testdata/file_ops.star
index e1f1ac2..50e39bf 100644
--- a/tools/rbcrun/testdata/file_ops.star
+++ b/tools/rbcrun/testdata/file_ops.star
@@ -4,15 +4,22 @@
 
 def test():
     myname = "file_ops.star"
+    assert.true(rblf_file_exists("."), "./ exists ")
     assert.true(rblf_file_exists(myname), "the file %s does exist" % myname)
     assert.true(not rblf_file_exists("no_such_file"), "the file no_such_file does not exist")
     files = rblf_wildcard("*.star")
     assert.true(myname in files, "expected %s in  %s" % (myname, files))
-    # RBCDATADIR is set by the caller to the path where this file resides
     files = rblf_wildcard("*.star", rblf_env.TEST_DATA_DIR)
     assert.true(myname in files, "expected %s in %s" % (myname, files))
     files = rblf_wildcard("*.xxx")
     assert.true(len(files) == 0, "expansion should be empty but contains %s" % files)
-
-
+    mydir = "testdata"
+    myrelname = "%s/%s" % (mydir, myname)
+    files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*")
+    assert.true(mydir in files and myrelname in files, "expected %s and %s in %s" % (mydir, myrelname, files))
+    files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*", only_files=1)
+    assert.true(mydir not in files, "did not expect %s in %s" % (mydir, files))
+    assert.true(myrelname in files, "expected %s  in %s" % (myrelname, files))
+    files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*.star")
+    assert.true(myrelname in files, "expected %s in %s" % (myrelname, files))
 test()
diff --git a/tools/releasetools/apex_utils.py b/tools/releasetools/apex_utils.py
index bd86acf..51ec434 100644
--- a/tools/releasetools/apex_utils.py
+++ b/tools/releasetools/apex_utils.py
@@ -340,6 +340,8 @@
     zip_items = apex_fd.namelist()
 
   payload_info = ParseApexPayloadInfo(avbtool, payload_file)
+  if no_hashtree is None:
+    no_hashtree = payload_info.get("Tree Size", 0) == 0
   SignApexPayload(
       avbtool,
       payload_file,
@@ -381,8 +383,8 @@
 
 
 def SignCompressedApex(avbtool, apex_file, payload_key, container_key,
-                         container_pw, apk_keys, codename_to_api_level_map,
-                         no_hashtree, signing_args=None):
+                       container_pw, apk_keys, codename_to_api_level_map,
+                       no_hashtree, signing_args=None):
   """Signs the current compressed APEX with the given payload/container keys.
 
   Args:
@@ -503,6 +505,7 @@
     raise ApexInfoError(
         'Failed to get type for {}:\n{}'.format(apex_file, e))
 
+
 def GetApexInfoFromTargetFiles(input_file, partition, compressed_only=True):
   """
   Get information about system APEX stored in the input_file zip
@@ -545,7 +548,7 @@
   for apex_filename in os.listdir(target_dir):
     apex_filepath = os.path.join(target_dir, apex_filename)
     if not os.path.isfile(apex_filepath) or \
-        not zipfile.is_zipfile(apex_filepath):
+            not zipfile.is_zipfile(apex_filepath):
       logger.info("Skipping %s because it's not a zipfile", apex_filepath)
       continue
     apex_info = ota_metadata_pb2.ApexInfo()
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index c708154..f30e382 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -449,6 +449,13 @@
     return vabc_enabled
 
   @property
+  def is_vabc_xor(self):
+    vendor_prop = self.info_dict.get("vendor.build.prop")
+    vabc_xor_enabled = vendor_prop and \
+        vendor_prop.GetProp("ro.virtual_ab.compression.xor.enabled") == "true"
+    return vabc_xor_enabled
+
+  @property
   def vendor_suppressed_vabc(self):
     vendor_prop = self.info_dict.get("vendor.build.prop")
     vabc_suppressed = vendor_prop and \
@@ -2937,7 +2944,7 @@
           th.join()
 
       if p.returncode != 0:
-        logger.warning("Failure running %s:\n%s\n", diff_program, "".join(err))
+        logger.warning("Failure running %s:\n%s\n", cmd, "".join(err))
         self.patch = None
         return None, None, None
       diff = ptemp.read()
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 1f3022b..366b51a 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -1091,6 +1091,9 @@
   if target_info.vendor_suppressed_vabc:
     logger.info("Vendor suppressed VABC. Disabling")
     OPTIONS.disable_vabc = True
+  if not target_info.is_vabc_xor or OPTIONS.disable_vabc:
+    logger.info("VABC XOR Not supported, disabling")
+    OPTIONS.enable_vabc_xor = False
   additional_args = []
 
   # Prepare custom images.
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 2859948..0842af9 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -428,7 +428,7 @@
   if is_compressed:
     uncompressed = tempfile.NamedTemporaryFile()
     with gzip.open(unsigned.name, "rb") as in_file, \
-         open(uncompressed.name, "wb") as out_file:
+            open(uncompressed.name, "wb") as out_file:
       shutil.copyfileobj(in_file, out_file)
 
     # Finally, close the "unsigned" file (which is gzip compressed), and then
@@ -468,7 +468,7 @@
     # Recompress the file after it has been signed.
     compressed = tempfile.NamedTemporaryFile()
     with open(signed.name, "rb") as in_file, \
-         gzip.open(compressed.name, "wb") as out_file:
+            gzip.open(compressed.name, "wb") as out_file:
       shutil.copyfileobj(in_file, out_file)
 
     data = compressed.read()
@@ -484,21 +484,21 @@
 
 def IsBuildPropFile(filename):
   return filename in (
-        "SYSTEM/etc/prop.default",
-        "BOOT/RAMDISK/prop.default",
-        "RECOVERY/RAMDISK/prop.default",
+      "SYSTEM/etc/prop.default",
+      "BOOT/RAMDISK/prop.default",
+      "RECOVERY/RAMDISK/prop.default",
 
-        "VENDOR_BOOT/RAMDISK/default.prop",
-        "VENDOR_BOOT/RAMDISK/prop.default",
+      "VENDOR_BOOT/RAMDISK/default.prop",
+      "VENDOR_BOOT/RAMDISK/prop.default",
 
-        # ROOT/default.prop is a legacy path, but may still exist for upgrading
-        # devices that don't support `property_overrides_split_enabled`.
-        "ROOT/default.prop",
+      # ROOT/default.prop is a legacy path, but may still exist for upgrading
+      # devices that don't support `property_overrides_split_enabled`.
+      "ROOT/default.prop",
 
-        # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
-        # as a symlink in the current code. So it's a no-op here. Keeping the
-        # path here for clarity.
-        "RECOVERY/RAMDISK/default.prop") or filename.endswith("build.prop")
+      # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
+      # as a symlink in the current code. So it's a no-op here. Keeping the
+      # path here for clarity.
+      "RECOVERY/RAMDISK/default.prop") or filename.endswith("build.prop")
 
 
 def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
@@ -561,7 +561,7 @@
 
       # We've asserted not having a case with only one of them PRESIGNED.
       if (payload_key not in common.SPECIAL_CERT_STRINGS and
-          container_key not in common.SPECIAL_CERT_STRINGS):
+              container_key not in common.SPECIAL_CERT_STRINGS):
         print("    signing: %-*s container (%s)" % (
             maxsize, name, container_key))
         print("           : %-*s payload   (%s)" % (
@@ -575,7 +575,7 @@
             key_passwords,
             apk_keys,
             codename_to_api_level_map,
-            no_hashtree=True,
+            no_hashtree=None,  # Let apex_util determine if hash tree is needed
             signing_args=OPTIONS.avb_extra_args.get('apex'))
         common.ZipWrite(output_tf_zip, signed_apex, filename)
 
@@ -658,7 +658,7 @@
     # Updates system_other.avbpubkey in /product/etc/.
     elif filename in (
         "PRODUCT/etc/security/avb/system_other.avbpubkey",
-        "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
+            "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
       # Only update system_other's public key, if the corresponding signing
       # key is specified via --avb_system_other_key.
       signing_key = OPTIONS.avb_keys.get("system_other")
@@ -671,7 +671,7 @@
     # Should NOT sign boot-debug.img.
     elif filename in (
         "BOOT/RAMDISK/force_debuggable",
-        "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
+            "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
       raise common.ExternalError("debuggable boot.img cannot be signed")
 
     # A non-APK file; copy it verbatim.
@@ -762,7 +762,8 @@
   # it's only checking entries with global seinfo at the moment (i.e. ignoring
   # the ones with inner packages). (Bug: 69479366)
   root = ElementTree.fromstring(data)
-  signatures = [signer.attrib['signature'] for signer in root.findall('signer')]
+  signatures = [signer.attrib['signature']
+                for signer in root.findall('signer')]
   assert len(signatures) == len(set(signatures)), \
       "Found duplicate entries after cert replacement: {}".format(data)
 
@@ -807,7 +808,7 @@
     if line and line[0] != '#' and "=" in line:
       key, value = line.split("=", 1)
       if (key.startswith("ro.") and
-          key.endswith((".build.fingerprint", ".build.thumbprint"))):
+              key.endswith((".build.fingerprint", ".build.thumbprint"))):
         pieces = value.split("/")
         pieces[-1] = EditTags(pieces[-1])
         value = "/".join(pieces)
@@ -1000,7 +1001,7 @@
     ReplaceAvbPartitionSigningKey(partition)
 
   for custom_partition in misc_info.get(
-      "avb_custom_images_partition_list", "").strip().split():
+          "avb_custom_images_partition_list", "").strip().split():
     ReplaceAvbPartitionSigningKey(custom_partition)
 
 
@@ -1065,7 +1066,7 @@
           devkeydir + "/shared":   d + "/shared",
           devkeydir + "/platform": d + "/platform",
           devkeydir + "/networkstack": d + "/networkstack",
-          })
+      })
     else:
       OPTIONS.key_map[s] = d
 
@@ -1168,8 +1169,8 @@
     if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
       container_key = 'PRESIGNED'
     elif CompareKeys(
-        container_cert, OPTIONS.public_key_suffix,
-        container_private_key, OPTIONS.private_key_suffix):
+            container_cert, OPTIONS.public_key_suffix,
+            container_private_key, OPTIONS.private_key_suffix):
       container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
     else:
       raise ValueError("Failed to parse container keys: \n{}".format(line))