Add modules partition.

Modules partition is a dynamic read-write partition.
- AVB is not enabled on the partition
- OTA is file-based; see follow up CL for details
- No build prop files; in particular, no build fingerprint
- No fs_config
- No notice files; notice files are included in individual APEXes

Test: build on CF
Bug: 163543381

Change-Id: Ie397b9ec61dfd1c158450d050196024604854d4d
diff --git a/core/Makefile b/core/Makefile
index 1f9bd14..69c35d1 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1409,6 +1409,20 @@
     $(hide) echo "odm_dlkm_selinux_fc=$(SELINUX_FC)" >> $(1)
     $(hide) echo "building_odm_dlkm_image=$(BUILDING_ODM_DLKM_IMAGE)" >> $(1)
 )
+$(if $(filter $(2),modules),\
+    $(if $(BOARD_MODULESIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "modules_fs_type=$(BOARD_MODULESIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
+    $(if $(BOARD_MODULESIMAGE_EXTFS_INODE_COUNT),$(hide) echo "modules_extfs_inode_count=$(BOARD_MODULESIMAGE_EXTFS_INODE_COUNT)" >> $(1))
+    $(if $(BOARD_MODULESIMAGE_EXTFS_RSV_PCT),$(hide) echo "modules_extfs_rsv_pct=$(BOARD_MODULESIMAGE_EXTFS_RSV_PCT)" >> $(1))
+    $(if $(BOARD_MODULESIMAGE_PARTITION_SIZE),$(hide) echo "modules_size=$(BOARD_MODULESIMAGE_PARTITION_SIZE)" >> $(1))
+    $(if $(BOARD_MODULESIMAGE_JOURNAL_SIZE),$(hide) echo "modules_journal_size=$(BOARD_MODULESIMAGE_JOURNAL_SIZE)" >> $(1))
+    $(if $(BOARD_MODULESIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "modules_squashfs_compressor=$(BOARD_MODULESIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
+    $(if $(BOARD_MODULESIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "modules_squashfs_compressor_opt=$(BOARD_MODULESIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
+    $(if $(BOARD_MODULESIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "modules_squashfs_block_size=$(BOARD_MODULESIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
+    $(if $(BOARD_MODULESIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "modules_squashfs_disable_4k_align=$(BOARD_MODULESIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
+    $(if $(BOARD_MODULESIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "modules_reserved_size=$(BOARD_MODULESIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
+    $(hide) echo "modules_selinux_fc=$(SELINUX_FC)" >> $(1)
+    $(hide) echo "building_modules_image=$(BUILDING_MODULES_IMAGE)" >> $(1)
+)
 $(if $(filter $(2),oem),\
     $(if $(BOARD_OEMIMAGE_PARTITION_SIZE),$(hide) echo "oem_size=$(BOARD_OEMIMAGE_PARTITION_SIZE)" >> $(1))
     $(if $(BOARD_OEMIMAGE_JOURNAL_SIZE),$(hide) echo "oem_journal_size=$(BOARD_OEMIMAGE_JOURNAL_SIZE)" >> $(1))
@@ -1544,6 +1558,9 @@
 ifdef BUILDING_ODM_DLKM_IMAGE
   PROP_DICTIONARY_IMAGES += odm_dlkm
 endif
+ifdef BUILDING_MODULES_IMAGE
+  PROP_DICTIONARY_IMAGES += modules
+endif
 define generate-userimage-prop-dictionary
   $(call generate-image-prop-dictionary,$(1),$(PROP_DICTIONARY_IMAGES),$(2))
 endef
@@ -3000,6 +3017,54 @@
 $(eval $(call copy-one-file,$(BOARD_PREBUILT_ODM_DLKMIMAGE),$(INSTALLED_ODM_DLKMIMAGE_TARGET)))
 endif
 
+# -----------------------------------------------------------------
+# modules partition image
+ifdef BUILDING_MODULES_IMAGE
+INTERNAL_MODULESIMAGE_FILES := \
+    $(filter $(TARGET_OUT_MODULES)/%,\
+      $(ALL_DEFAULT_INSTALLED_MODULES))
+
+INSTALLED_FILES_FILE_MODULES := $(PRODUCT_OUT)/installed-files-modules.txt
+INSTALLED_FILES_JSON_MODULES := $(INSTALLED_FILES_FILE_MODULES:.txt=.json)
+$(INSTALLED_FILES_FILE_MODULES): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_MODULES)
+$(INSTALLED_FILES_FILE_MODULES) : $(INTERNAL_MODULESIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)
+	@echo Installed file list: $@
+	@mkdir -p $(dir $@)
+	@rm -f $@
+	@mkdir -p $(TARGET_OUT_MODULES)
+	$(hide) $(FILESLIST) $(TARGET_OUT_MODULES) > $(@:.txt=.json)
+	$(hide) $(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
+
+modulesimage_intermediates := \
+    $(call intermediates-dir-for,PACKAGING,modules)
+BUILT_MODULESIMAGE_TARGET := $(PRODUCT_OUT)/modules.img
+define build-modulesimage-target
+  $(call pretty,"Target modules fs image: $(INSTALLED_MODULESIMAGE_TARGET)")
+  @mkdir -p $(TARGET_OUT_MODULES)
+  @mkdir -p $(modulesimage_intermediates) && rm -rf $(modulesimage_intermediates)/modules_image_info.txt
+  $(call generate-userimage-prop-dictionary, $(modulesimage_intermediates)/modules_image_info.txt, skip_fsck=true)
+  PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
+      $(BUILD_IMAGE) \
+          $(TARGET_OUT_MODULES) $(modulesimage_intermediates)/modules_image_info.txt \
+          $(INSTALLED_MODULESIMAGE_TARGET) $(TARGET_OUT)
+  $(call assert-max-image-size,$(INSTALLED_MODULESIMAGE_TARGET),$(BOARD_MODULESIMAGE_PARTITION_SIZE))
+endef
+
+# We just build this directly to the install location.
+INSTALLED_MODULESIMAGE_TARGET := $(BUILT_MODULESIMAGE_TARGET)
+$(INSTALLED_MODULESIMAGE_TARGET): \
+    $(INTERNAL_USERIMAGES_DEPS) \
+    $(INTERNAL_MODULESIMAGE_FILES) \
+    $(INSTALLED_FILES_FILE_MODULES)
+	$(build-modulesimage-target)
+
+.PHONY: modulesimage-nodeps mnod
+modulesimage-nodeps mnod: | $(INTERNAL_USERIMAGES_DEPS)
+	$(build-modulesimage-target)
+
+sync: $(INTERNAL_MODULESIMAGE_FILES)
+# BOARD_PREBUILT_MODULESIMAGE is not allowed.
+endif
 
 # -----------------------------------------------------------------
 # dtbo image
@@ -3499,7 +3564,10 @@
 # -----------------------------------------------------------------
 # Check VINTF of build
 
-# Note: vendor_dlkm and odm_dlkm does not have VINTF files.
+# Note: the following do not have VINTF files:
+# - vendor_dlkm
+# - odm_dlkm
+# - modules
 ifeq (,$(TARGET_BUILD_UNBUNDLED))
 
 intermediates := $(call intermediates-dir-for,PACKAGING,check_vintf_all)
@@ -4522,6 +4590,11 @@
 	$(hide) $(call package_files-copy-root, \
 	    $(TARGET_OUT_SYSTEM_OTHER),$(zip_root)/SYSTEM_OTHER)
 endif
+ifdef BUILDING_MODULES_IMAGE
+	@# Contents of the modules image
+	$(hide) $(call package_files-copy-root, \
+	    $(TARGET_OUT_MODULES),$(zip_root)/MODULES)
+endif
 	@# Extra contents of the OTA package
 	$(hide) mkdir -p $(zip_root)/OTA
 	$(hide) cp $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/
@@ -4570,6 +4643,7 @@
 	$(hide) cp $(PRODUCT_ODM_DLKM_BASE_FS_PATH) \
 	  $(zip_root)/META/$(notdir $(PRODUCT_ODM_DLKM_BASE_FS_PATH))
 endif
+# No PRODUCT_MODULES_BASE_FS_PATH for modules partition
 ifeq ($(TARGET_OTA_ALLOW_NON_AB),true)
 ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
 	$(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \
@@ -4664,6 +4738,9 @@
 ifdef BUILDING_ODM_DLKM_IMAGE
 	$(hide) $(call fs_config,$(zip_root)/ODM_DLKM,odm_dlkm/) > $(zip_root)/META/odm_dlkm_filesystem_config.txt
 endif
+ifdef BUILDING_MODULES_IMAGE
+	$(hide) $(call fs_config,$(zip_root)/MODULES,modules/) > $(zip_root)/META/modules_filesystem_config.txt
+endif
 	@# ROOT always contains the files for the root under normal boot.
 	$(hide) $(call fs_config,$(zip_root)/ROOT,) > $(zip_root)/META/root_filesystem_config.txt
 ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
@@ -5207,6 +5284,16 @@
 droidcore: $(INSTALLED_QEMU_ODM_DLKMIMAGE)
 endif
 
+ifdef INSTALLED_MODULESIMAGE_TARGET
+INSTALLED_QEMU_MODULESIMAGE := $(PRODUCT_OUT)/modules-qemu.img
+$(INSTALLED_QEMU_MODULESIMAGE): $(INSTALLED_MODULESIMAGE_TARGET) $(MK_QEMU_IMAGE_SH) $(SGDISK_HOST)
+	@echo Create modules-qemu.img
+	(export SGDISK=$(SGDISK_HOST); $(MK_QEMU_IMAGE_SH) $(INSTALLED_MODULESIMAGE_TARGET))
+
+modulesimage: $(INSTALLED_QEMU_MODULESIMAGE)
+droidcore: $(INSTALLED_QEMU_MODULESIMAGE)
+endif
+
 QEMU_VERIFIED_BOOT_PARAMS := $(PRODUCT_OUT)/VerifiedBootParams.textproto
 $(QEMU_VERIFIED_BOOT_PARAMS): $(INSTALLED_VBMETAIMAGE_TARGET) $(INSTALLED_SYSTEMIMAGE_TARGET) \
     $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) $(AVBTOOL)
diff --git a/core/board_config.mk b/core/board_config.mk
index 95d8af8..103e415 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -628,6 +628,31 @@
 .KATI_READONLY := BUILDING_ODM_DLKM_IMAGE
 
 ###########################################
+# Are we building modules image
+BOARD_USES_MODULESIMAGE :=
+ifdef BOARD_PREBUILT_MODULESIMAGE
+  $(error BOARD_PREBUILT_MODULESIMAGE must not be set. Prebuilt modules image is not allowed. Instead, install prebuilt APEXes.)
+endif
+ifdef BOARD_MODULESIMAGE_FILE_SYSTEM_TYPE
+  BOARD_USES_MODULESIMAGE := true
+endif
+
+BUILDING_MODULES_IMAGE :=
+ifeq ($(PRODUCT_BUILD_MODULES_IMAGE),)
+  ifdef BOARD_MODULESIMAGE_FILE_SYSTEM_TYPE
+    BUILDING_MODULES_IMAGE := true
+  endif
+else ifeq ($(PRODUCT_BUILD_MODULES_IMAGE),true)
+  BUILDING_MODULES_IMAGE := true
+  ifndef BOARD_MODULESIMAGE_FILE_SYSTEM_TYPE
+    $(error PRODUCT_BUILD_MODULES_IMAGE set to true, but BOARD_MODULESIMAGE_FILE_SYSTEM_TYPE not defined)
+  endif
+endif
+# BOARD_PREBUILT_MODULESIMAGE is not allowed.
+# The prebuilt for an individual module should be provided instead.
+.KATI_READONLY := BUILDING_MODULES_IMAGE
+
+###########################################
 # Ensure consistency among TARGET_RECOVERY_UPDATER_LIBS, AB_OTA_UPDATER, and PRODUCT_OTA_FORCE_NON_AB_PACKAGE.
 TARGET_RECOVERY_UPDATER_LIBS ?=
 AB_OTA_UPDATER ?=
diff --git a/core/config.mk b/core/config.mk
index 1bbb78c..ddbaa14 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -898,7 +898,7 @@
 )
 
 # BOARD_*_PARTITION_LIST: a list of the following tokens
-valid_super_partition_list := system vendor product system_ext odm vendor_dlkm odm_dlkm
+valid_super_partition_list := system vendor product system_ext odm vendor_dlkm odm_dlkm modules
 $(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)), \
     $(if $(filter-out $(valid_super_partition_list),$(BOARD_$(group)_PARTITION_LIST)), \
         $(error BOARD_$(group)_PARTITION_LIST contains invalid partition name \
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 76e7dd3..e8eb158 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -255,6 +255,7 @@
 TARGET_COPY_OUT_TEST_HARNESS_RAMDISK := test_harness_ramdisk
 TARGET_COPY_OUT_ROOT := root
 TARGET_COPY_OUT_RECOVERY := recovery
+TARGET_COPY_OUT_MODULES := modules
 # The directory used for optional partitions depend on the BoardConfig, so
 # they're defined to placeholder values here and swapped after reading the
 # BoardConfig, to be either the partition dir, or a subdir within 'system'.
@@ -887,6 +888,56 @@
   $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS \
   $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED
 
+# Unlike other partitions, modules partition should only contain APEXes at build time.
+TARGET_OUT_MODULES := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_MODULES)
+.KATI_READONLY := TARGET_OUT_MODULES
+
+TARGET_OUT_MODULES_EXECUTABLES :=
+TARGET_OUT_MODULES_OPTIONAL_EXECUTABLES :=
+TARGET_OUT_MODULES_SHARED_LIBRARIES :=
+TARGET_OUT_MODULES_RENDERSCRIPT_BITCODE :=
+TARGET_OUT_MODULES_JAVA_LIBRARIES :=
+TARGET_OUT_MODULES_APPS :=
+TARGET_OUT_MODULES_APPS_PRIVILEGED :=
+TARGET_OUT_MODULES_ETC :=
+.KATI_READONLY := \
+  TARGET_OUT_MODULES_EXECUTABLES \
+  TARGET_OUT_MODULES_OPTIONAL_EXECUTABLES \
+  TARGET_OUT_MODULES_SHARED_LIBRARIES \
+  TARGET_OUT_MODULES_RENDERSCRIPT_BITCODE \
+  TARGET_OUT_MODULES_JAVA_LIBRARIES \
+  TARGET_OUT_MODULES_APPS \
+  TARGET_OUT_MODULES_APPS_PRIVILEGED \
+  TARGET_OUT_MODULES_ETC
+
+$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_EXECUTABLES :=
+$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_SHARED_LIBRARIES :=
+$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_RENDERSCRIPT_BITCODE :=
+$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_APPS :=
+$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_APPS_PRIVILEGED :=
+.KATI_READONLY := \
+  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_EXECUTABLES \
+  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_SHARED_LIBRARIES \
+  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_RENDERSCRIPT_BITCODE \
+  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_APPS \
+  $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_APPS_PRIVILEGED
+
+$(KATI_obsolete_var \
+    TARGET_OUT_MODULES_EXECUTABLES \
+    TARGET_OUT_MODULES_OPTIONAL_EXECUTABLES \
+    TARGET_OUT_MODULES_SHARED_LIBRARIES \
+    TARGET_OUT_MODULES_RENDERSCRIPT_BITCODE \
+    TARGET_OUT_MODULES_JAVA_LIBRARIES \
+    TARGET_OUT_MODULES_APPS \
+    TARGET_OUT_MODULES_APPS_PRIVILEGED \
+    TARGET_OUT_MODULES_ETC \
+    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_EXECUTABLES \
+    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_SHARED_LIBRARIES \
+    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_RENDERSCRIPT_BITCODE \
+    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_APPS \
+    $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_MODULES_APPS_PRIVILEGED \
+    , modules partition should not contain any executables, libraries, or apps. It should only contain APEXes)
+
 TARGET_OUT_BREAKPAD := $(PRODUCT_OUT)/breakpad
 .KATI_READONLY := TARGET_OUT_BREAKPAD
 
diff --git a/core/main.mk b/core/main.mk
index 36071b8..97bc62c 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -1579,6 +1579,9 @@
 .PHONY: vbmetavendorimage
 vbmetavendorimage: $(INSTALLED_VBMETA_VENDORIMAGE_TARGET)
 
+.PHONY: modulesimage
+modulesimage: $(INSTALLED_MODULESIMAGE_TARGET)
+
 # Build files and then package it into the rom formats
 .PHONY: droidcore
 droidcore: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) \
diff --git a/help.sh b/help.sh
index 4af5154..fd22a78 100755
--- a/help.sh
+++ b/help.sh
@@ -48,6 +48,8 @@
                             Stands for "VendorDlkm, NO Dependencies"
     odnod                   Quickly rebuild the odm_dlkm image from built packages
                             Stands for "OdmDlkm, NO Dependencies"
+    mnod                    Quickly rebuild the modules image from built packages
+                            Stands for "Modules, NO Dependencies"
 
 
 So, for example, you could run:
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index a1f8e31..43d2e3a 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -296,6 +296,7 @@
       block_list=block_list)
   return img.name
 
+
 def AddOdmDlkm(output_zip):
   """Turn the contents of OdmDlkm into an odm_dlkm image and store it in output_zip."""
 
@@ -312,6 +313,22 @@
   return img.name
 
 
+def AddModules(output_zip):
+  """Turn the contents of Modules into an modules image and store it in output_zip."""
+
+  img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "modules.img")
+  if os.path.exists(img.name):
+    logger.info("modules.img already exists; no need to rebuild...")
+    return img.name
+
+  block_list = OutputFile(
+      output_zip, OPTIONS.input_tmp, "IMAGES", "modules.map")
+  CreateImage(
+      OPTIONS.input_tmp, OPTIONS.info_dict, "modules", img,
+      block_list=block_list)
+  return img.name
+
+
 def AddDtbo(output_zip):
   """Adds the DTBO image.
 
@@ -420,7 +437,9 @@
   # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and
   # build fingerprint).
   build_info = common.BuildInfo(info_dict)
-  uuid_seed = what + "-" + build_info.GetPartitionFingerprint(what)
+  uuid_seed = what
+  if what != "modules":
+    uuid_seed += "-" + build_info.GetPartitionFingerprint(what)
   image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))
   hash_seed = "hash_seed-" + uuid_seed
   image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed))
@@ -798,6 +817,12 @@
                   OPTIONS.info_dict.get("building_product_image") == "true") or
                  os.path.exists(
                      os.path.join(OPTIONS.input_tmp, "IMAGES", "product.img")))
+  has_modules = ((os.path.isdir(os.path.join(OPTIONS.input_tmp,
+                                              "MODULES")) and
+                   OPTIONS.info_dict.get("building_modules_image")
+                   == "true") or
+                  os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
+                                              "modules.img")))
   has_system_ext = (
       (os.path.isdir(os.path.join(OPTIONS.input_tmp, "SYSTEM_EXT")) and
        OPTIONS.info_dict.get("building_system_ext_image") == "true") or
@@ -927,6 +952,10 @@
     banner("odm_dlkm")
     partitions['odm_dlkm'] = AddOdmDlkm(output_zip)
 
+  if has_modules:
+    banner("modules")
+    partitions['modules'] = AddModules(output_zip)
+
   if has_system_other:
     banner("system_other")
     AddSystemOther(output_zip)
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 9cc072f..d0f0bd7 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -754,6 +754,22 @@
       d["extfs_rsv_pct"] = "0"
     copy_prop("odm_dlkm_reserved_size", "partition_reserved_size")
     copy_prop("odm_dlkm_selinux_fc", "selinux_fc")
+  elif mount_point == "modules":
+    # modules partition has no AVB.
+    copy_prop("modules_fs_type", "fs_type")
+    copy_prop("modules_size", "partition_size")
+    if not copy_prop("modules_journal_size", "journal_size"):
+      d["journal_size"] = "0"
+    # not setting ext4_share_dup_blocks because modules partition is writable.
+    copy_prop("modules_squashfs_compressor", "squashfs_compressor")
+    copy_prop("modules_squashfs_compressor_opt", "squashfs_compressor_opt")
+    copy_prop("modules_squashfs_block_size", "squashfs_block_size")
+    copy_prop("modules_squashfs_disable_4k_align", "squashfs_disable_4k_align")
+    copy_prop("modules_extfs_inode_count", "extfs_inode_count")
+    if not copy_prop("modules_extfs_rsv_pct", "extfs_rsv_pct"):
+      d["extfs_rsv_pct"] = "0"
+    copy_prop("modules_reserved_size", "partition_reserved_size")
+    copy_prop("modules_selinux_fc", "selinux_fc")
   elif mount_point == "oem":
     copy_prop("fs_type", "fs_type")
     copy_prop("oem_size", "partition_size")
@@ -806,6 +822,8 @@
     copy_prop("partition_size", "product_size")
   elif mount_point == "system_ext":
     copy_prop("partition_size", "system_ext_size")
+  elif mount_point == "modules":
+    copy_prop("partition_size", "modules_size")
   return d
 
 
@@ -851,6 +869,8 @@
       mount_point = "product"
     elif image_filename == "system_ext.img":
       mount_point = "system_ext"
+    elif image_filename == "modules.img":
+      mount_point = "modules"
     else:
       logger.error("Unknown image file name %s", image_filename)
       sys.exit(1)
diff --git a/tools/releasetools/check_target_files_vintf.py b/tools/releasetools/check_target_files_vintf.py
index 0edefac..5151567 100755
--- a/tools/releasetools/check_target_files_vintf.py
+++ b/tools/releasetools/check_target_files_vintf.py
@@ -46,7 +46,10 @@
     '/product': ('PRODUCT', 'SYSTEM/product'),
     '/odm': ('ODM', 'VENDOR/odm', 'SYSTEM/vendor/odm'),
     '/system_ext': ('SYSTEM_EXT', 'SYSTEM/system_ext'),
-    # vendor_dlkm and odm_dlkm does not have VINTF files.
+    # The following do not have VINTF files:
+    # - vendor_dlkm
+    # - odm_dlkm
+    # - modules
 }
 
 UNZIP_PATTERN = ['META/*', '*/build.prop']
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 2833397..f5aebdd 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -272,7 +272,7 @@
 # Images to be excluded from secondary payload. We essentially only keep
 # 'system_other' and bootloader partitions.
 SECONDARY_PAYLOAD_SKIPPED_IMAGES = [
-    'boot', 'dtbo', 'modem', 'odm', 'odm_dlkm', 'product', 'radio', 'recovery',
+    'boot', 'dtbo', 'modules', 'modem', 'odm', 'odm_dlkm', 'product', 'radio', 'recovery',
     'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor',
     'vendor_boot']