Override descriptors of the same partition.

When using multiple --include_descriptors_from_image, if there are
multiple descriptors for the same partition, only include the last
one, this is useful if we want to update one descriptor in an existing
vbmeta.img.

Bug: 76386656
Test: avbtool info_image --image $OUT/vbmeta_dev.img
Test: libavb_host_unittest
Change-Id: I7cfeef79013355ca4858ccf99798b76049674bd7
diff --git a/avbtool b/avbtool
index 14914ce..7be7027 100755
--- a/avbtool
+++ b/avbtool
@@ -2577,6 +2577,7 @@
 
     # Add descriptors from other images.
     if include_descriptors_from_image:
+      descriptors_dict = dict()
       for image in include_descriptors_from_image:
         image_handler = ImageHandler(image.name)
         (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
@@ -2585,7 +2586,18 @@
         h.bump_required_libavb_version_minor(
             image_vbmeta_header.required_libavb_version_minor)
         for desc in image_descriptors:
-          encoded_descriptors.extend(desc.encode())
+          # The --include_descriptors_from_image option is used in some setups
+          # with images A and B where both A and B contain a descriptor
+          # for a partition with the same name. Since it's not meaningful
+          # to include both descriptors, only include the last seen descriptor.
+          # See bug 76386656 for details.
+          if hasattr(desc, 'partition_name'):
+            key = type(desc).__name__ + '_' + desc.partition_name
+            descriptors_dict[key] = desc.encode()
+          else:
+            encoded_descriptors.extend(desc.encode())
+      for key in sorted(descriptors_dict.keys()):
+        encoded_descriptors.extend(descriptors_dict[key])
 
     # Load public key metadata blob, if requested.
     pkmd_blob = []
diff --git a/test/avb_slot_verify_unittest.cc b/test/avb_slot_verify_unittest.cc
index 9584439..823257e 100644
--- a/test/avb_slot_verify_unittest.cc
+++ b/test/avb_slot_verify_unittest.cc
@@ -499,6 +499,10 @@
       "      Flags:                 0\n"
       "      Kernel Cmdline:        'cmdline in vbmeta "
       "$(ANDROID_BOOT_PARTUUID)'\n"
+      "    Kernel Cmdline descriptor:\n"
+      "      Flags:                 0\n"
+      "      Kernel Cmdline:        'cmdline in hash footer "
+      "$(ANDROID_SYSTEM_PARTUUID)'\n"
       "    Hash descriptor:\n"
       "      Image Size:            5242880 bytes\n"
       "      Hash Algorithm:        sha256\n"
@@ -506,11 +510,7 @@
       "      Salt:                  deadbeef\n"
       "      Digest:                "
       "184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
-      "      Flags:                 0\n"
-      "    Kernel Cmdline descriptor:\n"
-      "      Flags:                 0\n"
-      "      Kernel Cmdline:        'cmdline in hash footer "
-      "$(ANDROID_SYSTEM_PARTUUID)'\n",
+      "      Flags:                 0\n",
       InfoImage(vbmeta_image_path_));
 
   EXPECT_COMMAND(0,
@@ -567,7 +567,7 @@
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1472 "
       "androidboot.vbmeta.digest="
-      "34cdb59b955aa35d4da97701f304fabf7392eecca8c50ff1a0b7b6e1c9aaa1b8 "
+      "99e84e34697a77414f0d7dd7896e98ac4da2d26bdd3756ef59ec79918de2adbe "
       "androidboot.vbmeta.invalidate_on_error=yes "
       "androidboot.veritymode=enforcing",
       std::string(slot_data->cmdline));
@@ -1286,20 +1286,20 @@
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
-      "      Image Size:            5242880 bytes\n"
-      "      Hash Algorithm:        sha256\n"
-      "      Partition Name:        foo\n"
-      "      Salt:                  deadbeef\n"
-      "      Digest:                "
-      "184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
-      "      Flags:                 0\n"
-      "    Hash descriptor:\n"
       "      Image Size:            10485760 bytes\n"
       "      Hash Algorithm:        sha256\n"
       "      Partition Name:        bar\n"
       "      Salt:                  deadbeef\n"
       "      Digest:                "
       "baea4bbd261d0edf4d1fe5e6e5a36976c291eeba66b6a46fa81dba691327a727\n"
+      "      Flags:                 0\n"
+      "    Hash descriptor:\n"
+      "      Image Size:            5242880 bytes\n"
+      "      Hash Algorithm:        sha256\n"
+      "      Partition Name:        foo\n"
+      "      Salt:                  deadbeef\n"
+      "      Digest:                "
+      "184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
       "      Flags:                 0\n",
       InfoImage(vbmeta_image_path_));
 
@@ -1330,13 +1330,13 @@
   // The 'foo' and 'bar' image data should match what is generated
   // above with GenerateImage().
   EXPECT_EQ(size_t(2), slot_data->num_loaded_partitions);
-  EXPECT_EQ("foo", std::string(slot_data->loaded_partitions[0].partition_name));
-  EXPECT_EQ(foo_image_size, slot_data->loaded_partitions[0].data_size);
+  EXPECT_EQ("bar", std::string(slot_data->loaded_partitions[0].partition_name));
+  EXPECT_EQ(bar_image_size, slot_data->loaded_partitions[0].data_size);
   for (size_t n = 0; n < slot_data->loaded_partitions[0].data_size; n++) {
     EXPECT_EQ(slot_data->loaded_partitions[0].data[n], uint8_t(n));
   }
-  EXPECT_EQ("bar", std::string(slot_data->loaded_partitions[1].partition_name));
-  EXPECT_EQ(bar_image_size, slot_data->loaded_partitions[1].data_size);
+  EXPECT_EQ("foo", std::string(slot_data->loaded_partitions[1].partition_name));
+  EXPECT_EQ(foo_image_size, slot_data->loaded_partitions[1].data_size);
   for (size_t n = 0; n < slot_data->loaded_partitions[1].data_size; n++) {
     EXPECT_EQ(slot_data->loaded_partitions[1].data[n], uint8_t(n));
   }
@@ -1399,20 +1399,20 @@
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
-      "      Image Size:            5242880 bytes\n"
-      "      Hash Algorithm:        sha256\n"
-      "      Partition Name:        foo\n"
-      "      Salt:                  deadbeef\n"
-      "      Digest:                "
-      "184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
-      "      Flags:                 0\n"
-      "    Hash descriptor:\n"
       "      Image Size:            10485760 bytes\n"
       "      Hash Algorithm:        sha256\n"
       "      Partition Name:        bar\n"
       "      Salt:                  deadbeef\n"
       "      Digest:                "
       "baea4bbd261d0edf4d1fe5e6e5a36976c291eeba66b6a46fa81dba691327a727\n"
+      "      Flags:                 0\n"
+      "    Hash descriptor:\n"
+      "      Image Size:            5242880 bytes\n"
+      "      Hash Algorithm:        sha256\n"
+      "      Partition Name:        foo\n"
+      "      Salt:                  deadbeef\n"
+      "      Digest:                "
+      "184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
       "      Flags:                 0\n",
       InfoImage(vbmeta_image_path_));
 
@@ -2071,21 +2071,6 @@
       "'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'\n"
       "    Hashtree descriptor:\n"
       "      Version of dm-verity:  1\n"
-      "      Image Size:            16777216 bytes\n"
-      "      Tree Offset:           16777216\n"
-      "      Tree Size:             135168 bytes\n"
-      "      Data Block Size:       4096 bytes\n"
-      "      Hash Block Size:       4096 bytes\n"
-      "      FEC num roots:         0\n"
-      "      FEC offset:            0\n"
-      "      FEC size:              0 bytes\n"
-      "      Hash Algorithm:        sha1\n"
-      "      Partition Name:        system\n"
-      "      Salt:                  d00df00d\n"
-      "      Root Digest:           c9ffc3bfae5000269a55a56621547fd1fcf819df\n"
-      "      Flags:                 0\n"
-      "    Hashtree descriptor:\n"
-      "      Version of dm-verity:  1\n"
       "      Image Size:            8388608 bytes\n"
       "      Tree Offset:           8388608\n"
       "      Tree Size:             69632 bytes\n"
@@ -2098,6 +2083,21 @@
       "      Partition Name:        foobar\n"
       "      Salt:                  d00df00d\n"
       "      Root Digest:           d52d93c988d336a79abe1c05240ae9a79a9b7d61\n"
+      "      Flags:                 0\n"
+      "    Hashtree descriptor:\n"
+      "      Version of dm-verity:  1\n"
+      "      Image Size:            16777216 bytes\n"
+      "      Tree Offset:           16777216\n"
+      "      Tree Size:             135168 bytes\n"
+      "      Data Block Size:       4096 bytes\n"
+      "      Hash Block Size:       4096 bytes\n"
+      "      FEC num roots:         0\n"
+      "      FEC offset:            0\n"
+      "      FEC size:              0 bytes\n"
+      "      Hash Algorithm:        sha1\n"
+      "      Partition Name:        system\n"
+      "      Salt:                  d00df00d\n"
+      "      Root Digest:           c9ffc3bfae5000269a55a56621547fd1fcf819df\n"
       "      Flags:                 0\n",
       InfoImage(boot_path));
 
@@ -2130,7 +2130,7 @@
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=5312 "
       "androidboot.vbmeta.digest="
-      "6b06719a940e5d8fa53ffef91eb4f0517ff0dda9833b90be1c9624ab3a5261d2 "
+      "b297d90aa92a5d49725d1206ff1301b054c5a0214f1cb2fc12b809b317d943e4 "
       "androidboot.vbmeta.invalidate_on_error=yes "
       "androidboot.veritymode=enforcing",
       std::string(slot_data->cmdline));
@@ -2547,6 +2547,11 @@
                                  "Flags:                    0\n"
                                  "Release String:           ''\n"
                                  "Descriptors:\n"
+                                 "    Kernel Cmdline descriptor:\n"
+                                 "      Flags:                 0\n"
+                                 "      Kernel Cmdline:        "
+                                 "'androidboot.vbmeta.root_digest.factory=$("
+                                 "AVB_FACTORY_ROOT_DIGEST)'\n"
                                  "    Hashtree descriptor:\n"
                                  "      Version of dm-verity:  1\n"
                                  "      Image Size:            5242880 bytes\n"
@@ -2561,12 +2566,7 @@
                                  "      Partition Name:        factory\n"
                                  "      Salt:                  deadbeef\n"
                                  "      Root Digest:           \n"
-                                 "      Flags:                 %d\n"
-                                 "    Kernel Cmdline descriptor:\n"
-                                 "      Flags:                 0\n"
-                                 "      Kernel Cmdline:        "
-                                 "'androidboot.vbmeta.root_digest.factory=$("
-                                 "AVB_FACTORY_ROOT_DIGEST)'\n",
+                                 "      Flags:                 %d\n",
                                  expected_tree_size,
                                  expected_fec_offset,
                                  verity_hash_algorithm_.c_str(),
@@ -2746,7 +2746,7 @@
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=1408 "
       "androidboot.vbmeta.digest="
-      "bdeff592f85f34a6ae40919e311273a10027f3877daa9c8c1be8e685947abb3d "
+      "0bf73ed205a043d410277444a49cc5643c1046f53b3b942cdc2c16fea06acd7b "
       "androidboot.vbmeta.invalidate_on_error=yes "
       "androidboot.veritymode=enforcing",
       last_cmdline_);
@@ -2773,7 +2773,7 @@
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=1408 "
       "androidboot.vbmeta.digest="
-      "03d287a0a126ed3fce48d6d8907612559e1485d29e201ede5838d65c5cc4bec2 "
+      "7d64315450f035f4ff93560403c46de5a7e2a0ddfc84b95bd69f7ed5654aa687 "
       "androidboot.vbmeta.invalidate_on_error=yes "
       "androidboot.veritymode=enforcing",
       last_cmdline_);
@@ -2804,7 +2804,7 @@
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=1408 "
       "androidboot.vbmeta.digest="
-      "931b10c7c8e7ab270437a4481b7d8d5c9757a3df190b7df3b6f93bf0289b9911 "
+      "097d002a75d1e89557b662b6db3f1ebffb8419a02e79792a97b2c4fd1c8bedc4 "
       "androidboot.vbmeta.invalidate_on_error=yes "
       "androidboot.veritymode=enforcing",
       last_cmdline_);