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/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']