DynamicPartitionControl: support retrofit devices

On retrofit devices:

* The retrofit update includes block devices at source
  slot (for example, system_a, vendor_a, product_a).
  This is done automatically because the retrofit update
  does not look different from regular updates in OTA
  client's perspective.

* The first update after the retrofit update includes
  the rest of the block devices (in the above example,
  system_b, vendor_b and product_b).

In order to do the second,

* use NewForUpdate() API from liblp to automatically
  include block devices at the target slot when the metadata
  is loaded.

* Use FlashPartitionTable() API to flash metadata to system_b
  directly without reading existing metadata from it.

Test: manual OTA on retrofit devices
Bug: 118506262
Change-Id: Ib2c15b8a1a04271320bfef408813723a5b2a7bd7
diff --git a/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc
index 38c6759..93a0fae 100644
--- a/dynamic_partition_control_android.cc
+++ b/dynamic_partition_control_android.cc
@@ -37,10 +37,13 @@
 using android::fs_mgr::CreateLogicalPartition;
 using android::fs_mgr::DestroyLogicalPartition;
 using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::PartitionOpener;
 
 namespace chromeos_update_engine {
 
-constexpr char kUseDynamicPartitions[] = "ro.boot.logical_partitions";
+constexpr char kUseDynamicPartitions[] = "ro.boot.dynamic_partitions";
+constexpr char kRetrfoitDynamicPartitions[] =
+    "ro.boot.dynamic_partitions_retrofit";
 constexpr uint64_t kMapTimeoutMillis = 1000;
 
 DynamicPartitionControlAndroid::~DynamicPartitionControlAndroid() {
@@ -51,6 +54,10 @@
   return GetBoolProperty(kUseDynamicPartitions, false);
 }
 
+static bool IsDynamicPartitionsRetrofit() {
+  return GetBoolProperty(kRetrfoitDynamicPartitions, false);
+}
+
 bool DynamicPartitionControlAndroid::MapPartitionOnDeviceMapper(
     const std::string& super_device,
     const std::string& target_partition_name,
@@ -122,8 +129,20 @@
 
 std::unique_ptr<MetadataBuilder>
 DynamicPartitionControlAndroid::LoadMetadataBuilder(
-    const std::string& super_device, uint32_t source_slot) {
-  auto builder = MetadataBuilder::New(super_device, source_slot);
+    const std::string& super_device,
+    uint32_t source_slot,
+    uint32_t target_slot) {
+  std::unique_ptr<MetadataBuilder> builder;
+
+  if (target_slot != BootControlInterface::kInvalidSlot &&
+      IsDynamicPartitionsRetrofit()) {
+    builder = MetadataBuilder::NewForUpdate(
+        PartitionOpener(), super_device, source_slot, target_slot);
+  } else {
+    builder =
+        MetadataBuilder::New(PartitionOpener(), super_device, source_slot);
+  }
+
   if (builder == nullptr) {
     LOG(WARNING) << "No metadata slot "
                  << BootControlInterface::SlotName(source_slot) << " in "
@@ -148,16 +167,24 @@
     return false;
   }
 
-  if (!UpdatePartitionTable(super_device, *metadata, target_slot)) {
-    LOG(ERROR) << "Cannot write metadata to slot "
-               << BootControlInterface::SlotName(target_slot) << " in "
-               << super_device;
-    return false;
+  if (IsDynamicPartitionsRetrofit()) {
+    if (!FlashPartitionTable(super_device, *metadata)) {
+      LOG(ERROR) << "Cannot write metadata to " << super_device;
+      return false;
+    }
+    LOG(INFO) << "Written metadata to " << super_device;
+  } else {
+    if (!UpdatePartitionTable(super_device, *metadata, target_slot)) {
+      LOG(ERROR) << "Cannot write metadata to slot "
+                 << BootControlInterface::SlotName(target_slot) << " in "
+                 << super_device;
+      return false;
+    }
+    LOG(INFO) << "Copied metadata to slot "
+              << BootControlInterface::SlotName(target_slot) << " in "
+              << super_device;
   }
 
-  LOG(INFO) << "Copied metadata to slot "
-            << BootControlInterface::SlotName(target_slot) << " in "
-            << super_device;
   return true;
 }