Merge "Generate the recovery text images at build time"
diff --git a/core/soong_app_prebuilt.mk b/core/soong_app_prebuilt.mk
index 837920f..1440806 100644
--- a/core/soong_app_prebuilt.mk
+++ b/core/soong_app_prebuilt.mk
@@ -67,8 +67,16 @@
 endif
 
 ifdef LOCAL_DEX_PREOPT
+ifdef LOCAL_STRIP_DEX
+  # If we have stripped the final APK, use the original jar generated by soong.
   $(built_odex): $(LOCAL_SOONG_DEX_JAR)
 	$(call dexpreopt-one-file,$<,$@)
+else
+  # If we haven't stripped, use the final APK for dexpreopt, as this may affect how the dex
+  # code is compiled.
+  $(built_odex): $(LOCAL_PREBUILT_MODULE_FILE)
+	$(call dexpreopt-one-file,$<,$@)
+endif  # LOCAL_STRIP_DEX
   $(eval $(call dexpreopt-copy-jar,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE),$(LOCAL_STRIP_DEX)))
 else
   $(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_BUILT_MODULE)))
diff --git a/envsetup.sh b/envsetup.sh
index 7e94748..a1d6e39 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -4,28 +4,31 @@
 Run "m help" for help with the build system itself.
 
 Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
-- lunch:     lunch <product_name>-<build_variant>
-             Selects <product_name> as the product to build, and <build_variant> as the variant to
-             build, and stores those selections in the environment to be read by subsequent
-             invocations of 'm' etc.
-- tapas:     tapas [<App1> <App2> ...] [arm|x86|mips|arm64|x86_64|mips64] [eng|userdebug|user]
-- croot:     Changes directory to the top of the tree.
-- m:         Makes from the top of the tree.
-- mm:        Builds all of the modules in the current directory, but not their dependencies.
-- mmm:       Builds all of the modules in the supplied directories, but not their dependencies.
-             To limit the modules being built use the syntax: mmm dir/:target1,target2.
-- mma:       Builds all of the modules in the current directory, and their dependencies.
-- mmma:      Builds all of the modules in the supplied directories, and their dependencies.
-- provision: Flash device with all required partitions. Options will be passed on to fastboot.
-- cgrep:     Greps on all local C/C++ files.
-- ggrep:     Greps on all local Gradle files.
-- jgrep:     Greps on all local Java files.
-- resgrep:   Greps on all local res/*.xml files.
-- mangrep:   Greps on all local AndroidManifest.xml files.
-- mgrep:     Greps on all local Makefiles files.
-- sepgrep:   Greps on all local sepolicy files.
-- sgrep:     Greps on all local source files.
-- godir:     Go to the directory containing a file.
+- lunch:      lunch <product_name>-<build_variant>
+              Selects <product_name> as the product to build, and <build_variant> as the variant to
+              build, and stores those selections in the environment to be read by subsequent
+              invocations of 'm' etc.
+- tapas:      tapas [<App1> <App2> ...] [arm|x86|mips|arm64|x86_64|mips64] [eng|userdebug|user]
+- croot:      Changes directory to the top of the tree.
+- m:          Makes from the top of the tree.
+- mm:         Builds all of the modules in the current directory, but not their dependencies.
+- mmm:        Builds all of the modules in the supplied directories, but not their dependencies.
+              To limit the modules being built use the syntax: mmm dir/:target1,target2.
+- mma:        Builds all of the modules in the current directory, and their dependencies.
+- mmma:       Builds all of the modules in the supplied directories, and their dependencies.
+- provision:  Flash device with all required partitions. Options will be passed on to fastboot.
+- cgrep:      Greps on all local C/C++ files.
+- ggrep:      Greps on all local Gradle files.
+- jgrep:      Greps on all local Java files.
+- resgrep:    Greps on all local res/*.xml files.
+- mangrep:    Greps on all local AndroidManifest.xml files.
+- mgrep:      Greps on all local Makefiles files.
+- sepgrep:    Greps on all local sepolicy files.
+- sgrep:      Greps on all local source files.
+- godir:      Go to the directory containing a file.
+- allmod:     List all modules.
+- gomod:      Go to the directory containing a module.
+- refreshmod: Refresh list of modules for allmod/gomod.
 
 Environment options:
 - SANITIZE_HOST: Set to 'true' to use ASAN for all host modules. Note that
@@ -359,6 +362,9 @@
         complete -C "bit --tab" bit
     fi
     complete -F _lunch lunch
+
+    complete -F _complete-android-module-names gomod
+    complete -F _complete-android-module-names m
 }
 
 function choosetype()
@@ -1463,6 +1469,77 @@
     \cd $T/$pathname
 }
 
+# Update module-info.json in out.
+function refreshmod() {
+    if [ ! "$ANDROID_PRODUCT_OUT" ]; then
+        echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2
+        return 1
+    fi
+
+    echo "Refreshing modules (building module-info.json). Log at $ANDROID_PRODUCT_OUT/module-info.json.build.log." >&2
+
+    # for the output of the next command
+    mkdir -p $ANDROID_PRODUCT_OUT || return 1
+
+    # Note, can't use absolute path because of the way make works.
+    m out/target/product/$(get_build_var TARGET_DEVICE)/module-info.json \
+        > $ANDROID_PRODUCT_OUT/module-info.json.build.log 2>&1
+}
+
+# List all modules for the current device, as cached in module-info.json. If any build change is
+# made and it should be reflected in the output, you should run 'refreshmod' first.
+function allmod() {
+    if [ ! "$ANDROID_PRODUCT_OUT" ]; then
+        echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2
+        return 1
+    fi
+
+    if [ ! -f "$ANDROID_PRODUCT_OUT/module-info.json" ]; then
+        echo "Could not find module-info.json. It will only be built once, and it can be updated with 'refreshmod'" >&2
+        refreshmod || return 1
+    fi
+
+    python -c "import json; print '\n'.join(sorted(json.load(open('$ANDROID_PRODUCT_OUT/module-info.json')).keys()))"
+}
+
+# Go to a specific module in the android tree, as cached in module-info.json. If any build change
+# is made, and it should be reflected in the output, you should run 'refreshmod' first.
+function gomod() {
+    if [ ! "$ANDROID_PRODUCT_OUT" ]; then
+        echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2
+        return 1
+    fi
+
+    if [[ $# -ne 1 ]]; then
+        echo "usage: gomod <module>" >&2
+        return 1
+    fi
+
+    if [ ! -f "$ANDROID_PRODUCT_OUT/module-info.json" ]; then
+        echo "Could not find module-info.json. It will only be built once, and it can be updated with 'refreshmod'" >&2
+        refreshmod || return 1
+    fi
+
+    local relpath=$(python -c "import json, os
+module = '$1'
+module_info = json.load(open('$ANDROID_PRODUCT_OUT/module-info.json'))
+if module not in module_info:
+    exit(1)
+print module_info[module]['path'][0]" 2>/dev/null)
+
+    if [ -z "$relpath" ]; then
+        echo "Could not find module '$1' (try 'refreshmod' if there have been build changes?)." >&2
+        return 1
+    else
+        cd $ANDROID_BUILD_TOP/$relpath
+    fi
+}
+
+function _complete-android-module-names() {
+    local word=${COMP_WORDS[COMP_CWORD]}
+    COMPREPLY=( $(allmod | grep -E "^$word") )
+}
+
 # Print colored exit condition
 function pez {
     "$@"
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 2c53a6e..3d5c512 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -314,6 +314,8 @@
     adb_keys \
     apex.test.key \
     arping \
+    com.android.apex.test_package.key \
+    com.android.apex.test_package.preinstall.key \
     iotop \
     iw \
     logpersist.start \
diff --git a/tools/checkowners.py b/tools/checkowners.py
index 8568ccf..7f03968 100755
--- a/tools/checkowners.py
+++ b/tools/checkowners.py
@@ -56,7 +56,8 @@
   glob = '[a-zA-Z0-9_\\.\\-\\*\\?]+'
   globs = '(%s( *, *%s)*)' % (glob, glob)
   perfile = 'per-file +' + globs + ' *= *' + directive
-  pats = '(|%s|%s|%s)$' % (noparent, email, perfile)
+  include = 'include +([^ :]+ *: *)?[^ ]+'
+  pats = '(|%s|%s|%s|%s)$' % (noparent, email, perfile, include)
   patterns = re.compile(pats)
   address_pattern = re.compile('([^@ ]+@[^ @]+)')
   perfile_pattern = re.compile('per-file +.*=(.*)')
diff --git a/tools/fs_config/Android.mk b/tools/fs_config/Android.mk
index bf5e171..5ade258 100644
--- a/tools/fs_config/Android.mk
+++ b/tools/fs_config/Android.mk
@@ -104,11 +104,14 @@
 
 include $(BUILD_HOST_EXECUTABLE)
 fs_config_generate_bin := $(LOCAL_INSTALLED_MODULE)
-# List of all supported vendor, oem and odm Partitions
+# List of supported vendor, oem, odm, product and product_services Partitions
 fs_config_generate_extra_partition_list := $(strip \
   $(if $(BOARD_USES_VENDORIMAGE)$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),vendor) \
   $(if $(BOARD_USES_OEMIMAGE)$(BOARD_OEMIMAGE_FILE_SYSTEM_TYPE),oem) \
-  $(if $(BOARD_USES_ODMIMAGE)$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),odm))
+  $(if $(BOARD_USES_ODMIMAGE)$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),odm) \
+  $(if $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE),product) \
+  $(if $(BOARD_PRODUCT_SERVICESIMAGE_FILE_SYSTEM_TYPE),product_services) \
+)
 
 ##################################
 # Generate the <p>/etc/fs_config_dirs binary files for each partition.
@@ -286,6 +289,72 @@
 
 endif
 
+ifneq ($(filter product,$(fs_config_generate_extra_partition_list)),)
+##################################
+# Generate the product/etc/fs_config_dirs binary file for the target
+# Add fs_config_dirs or fs_config_dirs_product to PRODUCT_PACKAGES in
+# the device make file to enable
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_dirs_product
+LOCAL_MODULE_CLASS := ETC
+LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT)/etc
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
+	@mkdir -p $(dir $@)
+	$< -D -P product -o $@
+
+##################################
+# Generate the product/etc/fs_config_files binary file for the target
+# Add fs_config_files of fs_config_files_product to PRODUCT_PACKAGES in
+# the device make file to enable
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_files_product
+LOCAL_MODULE_CLASS := ETC
+LOCAL_INSTALLED_MODULE_STEM := fs_config_files
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT)/etc
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
+	@mkdir -p $(dir $@)
+	$< -F -P product -o $@
+
+endif
+
+ifneq ($(filter product_services,$(fs_config_generate_extra_partition_list)),)
+##################################
+# Generate the product_services/etc/fs_config_dirs binary file for the target
+# Add fs_config_dirs or fs_config_dirs_product_services to PRODUCT_PACKAGES in
+# the device make file to enable
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_dirs_product_services
+LOCAL_MODULE_CLASS := ETC
+LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES)/etc
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
+	@mkdir -p $(dir $@)
+	$< -D -P product_services -o $@
+
+##################################
+# Generate the product_services/etc/fs_config_files binary file for the target
+# Add fs_config_files of fs_config_files_product_services to PRODUCT_PACKAGES in
+# the device make file to enable
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_files_product_services
+LOCAL_MODULE_CLASS := ETC
+LOCAL_INSTALLED_MODULE_STEM := fs_config_files
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES)/etc
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
+	@mkdir -p $(dir $@)
+	$< -F -P product_services -o $@
+
+endif
+
 ##################################
 # Build the oemaid header library when fs config files are present.
 # Intentionally break build if you require generated AIDs
diff --git a/tools/fs_config/android_filesystem_config_test_data.h b/tools/fs_config/android_filesystem_config_test_data.h
index 07bc8e5..c65d406 100644
--- a/tools/fs_config/android_filesystem_config_test_data.h
+++ b/tools/fs_config/android_filesystem_config_test_data.h
@@ -26,6 +26,8 @@
     {00555, AID_ROOT, AID_SYSTEM, 0, "vendor/etc"},
     {00555, AID_ROOT, AID_SYSTEM, 0, "oem/etc"},
     {00555, AID_ROOT, AID_SYSTEM, 0, "odm/etc"},
+    {00555, AID_ROOT, AID_SYSTEM, 0, "product/etc"},
+    {00555, AID_ROOT, AID_SYSTEM, 0, "product_services/etc"},
     {00755, AID_SYSTEM, AID_ROOT, 0, "system/oem/etc"},
     {00755, AID_SYSTEM, AID_ROOT, 0, "system/odm/etc"},
     {00755, AID_SYSTEM, AID_ROOT, 0, "system/vendor/etc"},
@@ -41,16 +43,22 @@
     {00444, AID_ROOT, AID_SYSTEM, 0, "vendor/etc/fs_config_dirs"},
     {00444, AID_ROOT, AID_SYSTEM, 0, "oem/etc/fs_config_dirs"},
     {00444, AID_ROOT, AID_SYSTEM, 0, "odm/etc/fs_config_dirs"},
+    {00444, AID_ROOT, AID_SYSTEM, 0, "product/etc/fs_config_dirs"},
+    {00444, AID_ROOT, AID_SYSTEM, 0, "product_services/etc/fs_config_dirs"},
     {00444, AID_ROOT, AID_SYSTEM, 0, "system/etc/fs_config_files"},
     {00444, AID_ROOT, AID_SYSTEM, 0, "vendor/etc/fs_config_files"},
     {00444, AID_ROOT, AID_SYSTEM, 0, "oem/etc/fs_config_files"},
     {00444, AID_ROOT, AID_SYSTEM, 0, "odm/etc/fs_config_files"},
+    {00444, AID_ROOT, AID_SYSTEM, 0, "product/etc/fs_config_files"},
+    {00444, AID_ROOT, AID_SYSTEM, 0, "product_services/etc/fs_config_files"},
     {00644, AID_SYSTEM, AID_ROOT, 0, "system/vendor/etc/fs_config_dirs"},
     {00644, AID_SYSTEM, AID_ROOT, 0, "system/oem/etc/fs_config_dirs"},
     {00644, AID_SYSTEM, AID_ROOT, 0, "system/odm/etc/fs_config_dirs"},
     {00644, AID_SYSTEM, AID_ROOT, 0, "system/vendor/etc/fs_config_files"},
     {00644, AID_SYSTEM, AID_ROOT, 0, "system/oem/etc/fs_config_files"},
     {00644, AID_SYSTEM, AID_ROOT, 0, "system/odm/etc/fs_config_files"},
+    {00644, AID_SYSTEM, AID_ROOT, 0, "system/product/etc/fs_config_files"},
+    {00644, AID_SYSTEM, AID_ROOT, 0, "system/product_services/etc/fs_config_files"},
     {00644, AID_SYSTEM, AID_ROOT, 0, "etc/fs_config_files"},
     {00666, AID_ROOT, AID_SYSTEM, 0, "data/misc/oem"},
 };
diff --git a/tools/fs_config/fs_config_generate.c b/tools/fs_config/fs_config_generate.c
index cb7ff9d..0f0603b 100644
--- a/tools/fs_config/fs_config_generate.c
+++ b/tools/fs_config/fs_config_generate.c
@@ -47,11 +47,15 @@
     {0000, AID_ROOT, AID_ROOT, 0, "vendor/etc/fs_config_dirs"},
     {0000, AID_ROOT, AID_ROOT, 0, "oem/etc/fs_config_dirs"},
     {0000, AID_ROOT, AID_ROOT, 0, "odm/etc/fs_config_dirs"},
+    {0000, AID_ROOT, AID_ROOT, 0, "product/etc/fs_config_dirs"},
+    {0000, AID_ROOT, AID_ROOT, 0, "product_services/etc/fs_config_dirs"},
 #endif
     {0000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_files"},
     {0000, AID_ROOT, AID_ROOT, 0, "vendor/etc/fs_config_files"},
     {0000, AID_ROOT, AID_ROOT, 0, "oem/etc/fs_config_files"},
     {0000, AID_ROOT, AID_ROOT, 0, "odm/etc/fs_config_files"},
+    {0000, AID_ROOT, AID_ROOT, 0, "product/etc/fs_config_files"},
+    {0000, AID_ROOT, AID_ROOT, 0, "product_services/etc/fs_config_files"},
 };
 #endif
 
diff --git a/tools/fs_config/fs_config_generator.py b/tools/fs_config/fs_config_generator.py
index cd534ec..f7e3eb2 100755
--- a/tools/fs_config/fs_config_generator.py
+++ b/tools/fs_config/fs_config_generator.py
@@ -1108,9 +1108,14 @@
 
     _INCLUDE = '#include <private/android_filesystem_config.h>'
 
+    # Note that the android_id name field is of type 'const char[]' instead of
+    # 'const char*'.  While this seems less straightforward as we need to
+    # calculate the max length of all names, this allows the entire android_ids
+    # table to be placed in .rodata section instead of .data.rel.ro section,
+    # resulting in less memory pressure.
     _STRUCT_FS_CONFIG = textwrap.dedent("""
                          struct android_id_info {
-                             const char *name;
+                             const char name[%d];
                              unsigned aid;
                          };""")
 
@@ -1132,12 +1137,13 @@
     def __call__(self, args):
 
         hdr = AIDHeaderParser(args['hdrfile'])
+        max_name_length = max(len(aid.friendly) + 1 for aid in hdr.aids)
 
         print AIDArrayGen._GENERATED
         print
         print AIDArrayGen._INCLUDE
         print
-        print AIDArrayGen._STRUCT_FS_CONFIG
+        print AIDArrayGen._STRUCT_FS_CONFIG % max_name_length
         print
         print AIDArrayGen._OPEN_ID_ARRAY
 
diff --git a/tools/fs_config/fs_config_test.cpp b/tools/fs_config/fs_config_test.cpp
index f95a4ca..916c615 100644
--- a/tools/fs_config/fs_config_test.cpp
+++ b/tools/fs_config/fs_config_test.cpp
@@ -23,7 +23,6 @@
 #include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/strings.h>
-#include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 #include <private/android_filesystem_config.h>
 #include <private/fs_config.h>
@@ -31,12 +30,12 @@
 #include "android_filesystem_config_test_data.h"
 
 // must run test in the test directory
-const static char fs_config_generate_command[] = "./fs_config_generate_test";
+static const std::string fs_config_generate_command = "./fs_config_generate_test";
 
-static std::string popenToString(std::string command) {
+static std::string popenToString(const std::string command) {
   std::string ret;
 
-  FILE* fp = popen(command.c_str(), "r");
+  auto fp = popen(command.c_str(), "r");
   if (fp) {
     if (!android::base::ReadFdToString(fileno(fp), &ret)) ret = "";
     pclose(fp);
@@ -46,15 +45,14 @@
 
 static void confirm(std::string&& data, const fs_path_config* config,
                     ssize_t num_config) {
-  const struct fs_path_config_from_file* pc =
-      reinterpret_cast<const fs_path_config_from_file*>(data.c_str());
-  size_t len = data.size();
+  auto pc = reinterpret_cast<const fs_path_config_from_file*>(data.c_str());
+  auto len = data.size();
 
   ASSERT_TRUE(config != NULL);
   ASSERT_LT(0, num_config);
 
   while (len > 0) {
-    uint16_t host_len = pc->len;
+    auto host_len = pc->len;
     if (host_len > len) break;
 
     EXPECT_EQ(config->mode, pc->mode);
@@ -76,148 +74,114 @@
 /* See local android_filesystem_config.h for test data */
 
 TEST(fs_conf_test, dirs) {
-  confirm(popenToString(
-              android::base::StringPrintf("%s -D", fs_config_generate_command)),
+  confirm(popenToString(fs_config_generate_command + " -D"),
           android_device_dirs, arraysize(android_device_dirs));
 }
 
 TEST(fs_conf_test, files) {
-  confirm(popenToString(
-              android::base::StringPrintf("%s -F", fs_config_generate_command)),
+  confirm(popenToString(fs_config_generate_command + " -F"),
           android_device_files, arraysize(android_device_files));
 }
 
-static const char vendor_str[] = "vendor/";
-static const char vendor_alt_str[] = "system/vendor/";
-static const char oem_str[] = "oem/";
-static const char oem_alt_str[] = "system/oem/";
-static const char odm_str[] = "odm/";
-static const char odm_alt_str[] = "system/odm/";
+static bool is_system(const char* prefix) {
+  return !android::base::StartsWith(prefix, "vendor/") &&
+         !android::base::StartsWith(prefix, "system/vendor/") &&
+         !android::base::StartsWith(prefix, "oem/") &&
+         !android::base::StartsWith(prefix, "system/oem/") &&
+         !android::base::StartsWith(prefix, "odm/") &&
+         !android::base::StartsWith(prefix, "system/odm/") &&
+         !android::base::StartsWith(prefix, "product/") &&
+         !android::base::StartsWith(prefix, "system/product/") &&
+         !android::base::StartsWith(prefix, "product_services/") &&
+         !android::base::StartsWith(prefix, "system/product_services/");
+}
 
 TEST(fs_conf_test, system_dirs) {
   std::vector<fs_path_config> dirs;
-  const fs_path_config* config = android_device_dirs;
-  for (size_t num = arraysize(android_device_dirs); num; --num) {
-    if (!android::base::StartsWith(config->prefix, vendor_str) &&
-        !android::base::StartsWith(config->prefix, vendor_alt_str) &&
-        !android::base::StartsWith(config->prefix, oem_str) &&
-        !android::base::StartsWith(config->prefix, oem_alt_str) &&
-        !android::base::StartsWith(config->prefix, odm_str) &&
-        !android::base::StartsWith(config->prefix, odm_alt_str)) {
+  auto config = android_device_dirs;
+  for (auto num = arraysize(android_device_dirs); num; --num) {
+    if (is_system(config->prefix)) {
       dirs.emplace_back(*config);
     }
     ++config;
   }
-  confirm(popenToString(android::base::StringPrintf(
-              "%s -D -P -vendor,-oem,-odm", fs_config_generate_command)),
+  confirm(popenToString(fs_config_generate_command + " -D -P -vendor,-oem,-odm,-product,-product_services"),
+          &dirs[0], dirs.size());
+}
+
+static void fs_conf_test_dirs(const std::string& partition_name) {
+  std::vector<fs_path_config> dirs;
+  auto config = android_device_dirs;
+  const auto str = partition_name + "/";
+  const auto alt_str = "system/" + partition_name + "/";
+  for (auto num = arraysize(android_device_dirs); num; --num) {
+    if (android::base::StartsWith(config->prefix, str) ||
+        android::base::StartsWith(config->prefix, alt_str)) {
+      dirs.emplace_back(*config);
+    }
+    ++config;
+  }
+  confirm(popenToString(fs_config_generate_command + " -D -P " + partition_name),
           &dirs[0], dirs.size());
 }
 
 TEST(fs_conf_test, vendor_dirs) {
-  std::vector<fs_path_config> dirs;
-  const fs_path_config* config = android_device_dirs;
-  for (size_t num = arraysize(android_device_dirs); num; --num) {
-    if (android::base::StartsWith(config->prefix, vendor_str) ||
-        android::base::StartsWith(config->prefix, vendor_alt_str)) {
-      dirs.emplace_back(*config);
-    }
-    ++config;
-  }
-  confirm(popenToString(android::base::StringPrintf(
-              "%s -D -P vendor", fs_config_generate_command)),
-          &dirs[0], dirs.size());
+  fs_conf_test_dirs("vendor");
 }
 
 TEST(fs_conf_test, oem_dirs) {
-  std::vector<fs_path_config> dirs;
-  const fs_path_config* config = android_device_dirs;
-  for (size_t num = arraysize(android_device_dirs); num; --num) {
-    if (android::base::StartsWith(config->prefix, oem_str) ||
-        android::base::StartsWith(config->prefix, oem_alt_str)) {
-      dirs.emplace_back(*config);
-    }
-    ++config;
-  }
-  confirm(popenToString(android::base::StringPrintf(
-              "%s -D -P oem", fs_config_generate_command)),
-          &dirs[0], dirs.size());
+  fs_conf_test_dirs("oem");
 }
 
 TEST(fs_conf_test, odm_dirs) {
-  std::vector<fs_path_config> dirs;
-  const fs_path_config* config = android_device_dirs;
-  for (size_t num = arraysize(android_device_dirs); num; --num) {
-    if (android::base::StartsWith(config->prefix, odm_str) ||
-        android::base::StartsWith(config->prefix, odm_alt_str)) {
-      dirs.emplace_back(*config);
-    }
-    ++config;
-  }
-  confirm(popenToString(android::base::StringPrintf(
-              "%s -D -P odm", fs_config_generate_command)),
-          &dirs[0], dirs.size());
+  fs_conf_test_dirs("odm");
 }
 
 TEST(fs_conf_test, system_files) {
   std::vector<fs_path_config> files;
-  const fs_path_config* config = android_device_files;
-  for (size_t num = arraysize(android_device_files); num; --num) {
-    if (!android::base::StartsWith(config->prefix, vendor_str) &&
-        !android::base::StartsWith(config->prefix, vendor_alt_str) &&
-        !android::base::StartsWith(config->prefix, oem_str) &&
-        !android::base::StartsWith(config->prefix, oem_alt_str) &&
-        !android::base::StartsWith(config->prefix, odm_str) &&
-        !android::base::StartsWith(config->prefix, odm_alt_str)) {
+  auto config = android_device_files;
+  for (auto num = arraysize(android_device_files); num; --num) {
+    if (is_system(config->prefix)) {
       files.emplace_back(*config);
     }
     ++config;
   }
-  confirm(popenToString(android::base::StringPrintf(
-              "%s -F -P -vendor,-oem,-odm", fs_config_generate_command)),
+  confirm(popenToString(fs_config_generate_command + " -F -P -vendor,-oem,-odm,-product,-product_services"),
+          &files[0], files.size());
+}
+
+static void fs_conf_test_files(const std::string& partition_name) {
+  std::vector<fs_path_config> files;
+  auto config = android_device_files;
+  const auto str = partition_name + "/";
+  const auto alt_str = "system/" + partition_name + "/";
+  for (auto num = arraysize(android_device_files); num; --num) {
+    if (android::base::StartsWith(config->prefix, str) ||
+        android::base::StartsWith(config->prefix, alt_str)) {
+      files.emplace_back(*config);
+    }
+    ++config;
+  }
+  confirm(popenToString(fs_config_generate_command + " -F -P " + partition_name),
           &files[0], files.size());
 }
 
 TEST(fs_conf_test, vendor_files) {
-  std::vector<fs_path_config> files;
-  const fs_path_config* config = android_device_files;
-  for (size_t num = arraysize(android_device_files); num; --num) {
-    if (android::base::StartsWith(config->prefix, vendor_str) ||
-        android::base::StartsWith(config->prefix, vendor_alt_str)) {
-      files.emplace_back(*config);
-    }
-    ++config;
-  }
-  confirm(popenToString(android::base::StringPrintf(
-              "%s -F -P vendor", fs_config_generate_command)),
-          &files[0], files.size());
+  fs_conf_test_files("vendor");
 }
 
 TEST(fs_conf_test, oem_files) {
-  std::vector<fs_path_config> files;
-  const fs_path_config* config = android_device_files;
-  for (size_t num = arraysize(android_device_files); num; --num) {
-    if (android::base::StartsWith(config->prefix, oem_str) ||
-        android::base::StartsWith(config->prefix, oem_alt_str)) {
-      files.emplace_back(*config);
-    }
-    ++config;
-  }
-  confirm(popenToString(android::base::StringPrintf(
-              "%s -F -P oem", fs_config_generate_command)),
-          &files[0], files.size());
+  fs_conf_test_files("oem");
 }
 
 TEST(fs_conf_test, odm_files) {
-  std::vector<fs_path_config> files;
-  const fs_path_config* config = android_device_files;
-  for (size_t num = arraysize(android_device_files); num; --num) {
-    if (android::base::StartsWith(config->prefix, odm_str) ||
-        android::base::StartsWith(config->prefix, odm_alt_str)) {
-      files.emplace_back(*config);
-    }
-    ++config;
-  }
-  confirm(popenToString(android::base::StringPrintf(
-              "%s -F -P odm", fs_config_generate_command)),
-          &files[0], files.size());
+  fs_conf_test_files("odm");
+}
+
+TEST(fs_conf_test, product_files) {
+  fs_conf_test_files("product");
+}
+
+TEST(fs_conf_test, product_services_files) {
+  fs_conf_test_files("product_services");
 }
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 669d87b..1872a58 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -657,12 +657,13 @@
   """Create split super_*.img and store it in output_zip."""
 
   outdir = os.path.join(OPTIONS.input_tmp, "OTA")
-  build_super_image.BuildSuperImage(OPTIONS.input_tmp, outdir)
+  built = build_super_image.BuildSuperImage(OPTIONS.input_tmp, outdir)
 
-  for dev in OPTIONS.info_dict['super_block_devices'].strip().split():
-    img = OutputFile(output_zip, OPTIONS.input_tmp, "OTA",
-                     "super_" + dev + ".img")
-    img.Write()
+  if built:
+    for dev in OPTIONS.info_dict['super_block_devices'].strip().split():
+      img = OutputFile(output_zip, OPTIONS.input_tmp, "OTA",
+                       "super_" + dev + ".img")
+      img.Write()
 
 
 def ReplaceUpdatedFiles(zip_filename, files_list):
diff --git a/tools/releasetools/build_super_image.py b/tools/releasetools/build_super_image.py
index 6efd3f4..e8730ae 100755
--- a/tools/releasetools/build_super_image.py
+++ b/tools/releasetools/build_super_image.py
@@ -138,14 +138,25 @@
   else:
     logger.info("Done writing image %s", output)
 
+  return True
+
 
 def BuildSuperImageFromExtractedTargetFiles(inp, out):
   info_dict = common.LoadInfoDict(inp)
   partition_list = shlex.split(
       info_dict.get("dynamic_partition_list", "").strip())
+  missing_images = []
   for partition in partition_list:
-    info_dict["{}_image".format(partition)] = os.path.join(
-        inp, "IMAGES", "{}.img".format(partition))
+    image_path = os.path.join(inp, "IMAGES", "{}.img".format(partition))
+    if not os.path.isfile(image_path):
+      missing_images.append(image_path)
+    else:
+      info_dict["{}_image".format(partition)] = image_path
+  if missing_images:
+    logger.warning("Skip building super image because the following "
+                   "images are missing from target files:\n%s",
+                   "\n".join(missing_images))
+    return False
   return BuildSuperImageFromDict(info_dict, out)
 
 
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index fe63458..4c452ad 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -827,10 +827,10 @@
     ranges = image.file_map[entry]
 
     # If a RangeSet has been tagged as using shared blocks while loading the
-    # image, its block list must be already incomplete due to that reason. Don't
-    # give it 'incomplete' tag to avoid messing up the imgdiff stats.
+    # image, check the original block list to determine its completeness. Note
+    # that the 'incomplete' flag would be tagged to the original RangeSet only.
     if ranges.extra.get('uses_shared_blocks'):
-      continue
+      ranges = ranges.extra['uses_shared_blocks']
 
     if RoundUpTo4K(info.file_size) > ranges.size() * 4096:
       ranges.extra['incomplete'] = True
diff --git a/tools/releasetools/sparse_img.py b/tools/releasetools/sparse_img.py
index 5ebb1f0..7919ba4 100644
--- a/tools/releasetools/sparse_img.py
+++ b/tools/releasetools/sparse_img.py
@@ -248,15 +248,21 @@
         ranges = rangelib.RangeSet.parse(ranges)
 
         if allow_shared_blocks:
-          # Find the shared blocks that have been claimed by others.
+          # Find the shared blocks that have been claimed by others. If so, tag
+          # the entry so that we can skip applying imgdiff on this file.
           shared_blocks = ranges.subtract(remaining)
           if shared_blocks:
-            ranges = ranges.subtract(shared_blocks)
-            if not ranges:
+            non_shared = ranges.subtract(shared_blocks)
+            if not non_shared:
               continue
 
-            # Tag the entry so that we can skip applying imgdiff on this file.
-            ranges.extra['uses_shared_blocks'] = True
+            # There shouldn't anything in the extra dict yet.
+            assert not ranges.extra, "Non-empty RangeSet.extra"
+
+            # Put the non-shared RangeSet as the value in the block map, which
+            # has a copy of the original RangeSet.
+            non_shared.extra['uses_shared_blocks'] = ranges
+            ranges = non_shared
 
         out[fn] = ranges
         assert ranges.size() == ranges.intersect(remaining).size()
diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py
index ae8253d..eeb802b 100755
--- a/tools/releasetools/validate_target_files.py
+++ b/tools/releasetools/validate_target_files.py
@@ -89,13 +89,20 @@
         logging.warning('Skipping %s that has incomplete block list', entry)
         continue
 
+      # Use the original RangeSet if applicable, which includes the shared
+      # blocks. And this needs to happen before checking the monotonicity flag.
+      if ranges.extra.get('uses_shared_blocks'):
+        file_ranges = ranges.extra['uses_shared_blocks']
+      else:
+        file_ranges = ranges
+
       # TODO(b/79951650): Handle files with non-monotonic ranges.
-      if not ranges.monotonic:
+      if not file_ranges.monotonic:
         logging.warning(
-            'Skipping %s that has non-monotonic ranges: %s', entry, ranges)
+            'Skipping %s that has non-monotonic ranges: %s', entry, file_ranges)
         continue
 
-      blocks_sha1 = image.RangeSha1(ranges)
+      blocks_sha1 = image.RangeSha1(file_ranges)
 
       # The filename under unpacked directory, such as SYSTEM/bin/sh.
       unpacked_name = os.path.join(
@@ -104,7 +111,7 @@
       file_sha1 = unpacked_file.sha1
       assert blocks_sha1 == file_sha1, \
           'file: %s, range: %s, blocks_sha1: %s, file_sha1: %s' % (
-              entry, ranges, blocks_sha1, file_sha1)
+              entry, file_ranges, blocks_sha1, file_sha1)
 
   logging.info('Validating file consistency.')
 
@@ -311,9 +318,9 @@
   if info_dict.get("avb_enable") == "true":
     logging.info('Verifying Verified Boot 2.0 (AVB) images...')
 
-    # Temporarily disable the verification for AVB-signed images, due to the
-    # dependency on PyCrypto in `avbtool verify_image` (Bug: 119624011).
-    logging.info('Temporarily disabled due to b/119624011')
+    # TODO(b/120517892): Temporarily disable the verification for AVB-signed
+    # images. Needing supporting changes in caller to pass in the desired keys.
+    logging.info('Temporarily disabled due to b/120517892')
 
 
 def main():