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/boot_control_android.cc b/boot_control_android.cc
index 4568fe4..1d13139 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -119,7 +119,8 @@
     const string& partition_name_suffix,
     Slot slot,
     string* device) {
-  auto builder = dynamic_control->LoadMetadataBuilder(super_device, slot);
+  auto builder = dynamic_control->LoadMetadataBuilder(
+      super_device, slot, BootControlInterface::kInvalidSlot);
 
   if (builder == nullptr) {
     if (!dynamic_control->IsDynamicPartitionsEnabled()) {
@@ -284,8 +285,8 @@
     Slot target_slot,
     const string& target_suffix,
     const PartitionMetadata& partition_metadata) {
-  auto builder =
-      dynamic_control->LoadMetadataBuilder(super_device, source_slot);
+  auto builder = dynamic_control->LoadMetadataBuilder(
+      super_device, source_slot, target_slot);
   if (builder == nullptr) {
     // TODO(elsk): allow reconstructing metadata from partition_metadata
     // in recovery sideload.
diff --git a/boot_control_android_unittest.cc b/boot_control_android_unittest.cc
index e98d2f4..6460ffe 100644
--- a/boot_control_android_unittest.cc
+++ b/boot_control_android_unittest.cc
@@ -298,10 +298,12 @@
   }
 
   void SetMetadata(uint32_t slot, const PartitionMetadata& metadata) {
-    EXPECT_CALL(dynamicControl(), LoadMetadataBuilder(GetSuperDevice(), slot))
+    EXPECT_CALL(dynamicControl(),
+                LoadMetadataBuilder(GetSuperDevice(), slot, _))
         .Times(AnyNumber())
-        .WillRepeatedly(Invoke(
-            [metadata](auto, auto) { return NewFakeMetadata(metadata); }));
+        .WillRepeatedly(Invoke([metadata](auto, auto, auto) {
+          return NewFakeMetadata(metadata);
+        }));
   }
 
   // Expect that MapPartitionOnDeviceMapper is called on target() metadata slot
@@ -394,7 +396,7 @@
         .Times(0);
     // Should not load metadata from target slot.
     EXPECT_CALL(dynamicControl(),
-                LoadMetadataBuilder(GetSuperDevice(), target()))
+                LoadMetadataBuilder(GetSuperDevice(), target(), _))
         .Times(0);
   }
 
@@ -512,8 +514,9 @@
 
 // Test corrupt source metadata case.
 TEST_P(BootControlAndroidTestP, CorruptedSourceMetadata) {
-  EXPECT_CALL(dynamicControl(), LoadMetadataBuilder(GetSuperDevice(), source()))
-      .WillOnce(Invoke([](auto, auto) { return nullptr; }));
+  EXPECT_CALL(dynamicControl(),
+              LoadMetadataBuilder(GetSuperDevice(), source(), _))
+      .WillOnce(Invoke([](auto, auto, auto) { return nullptr; }));
   EXPECT_FALSE(InitPartitionMetadata(target(), {{"system", 1_GiB}}))
       << "Should not be able to continue with corrupt source metadata";
 }
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;
 }
 
diff --git a/dynamic_partition_control_android.h b/dynamic_partition_control_android.h
index 91c9f87..52d4f5b 100644
--- a/dynamic_partition_control_android.h
+++ b/dynamic_partition_control_android.h
@@ -43,7 +43,9 @@
   bool GetDmDevicePathByName(const std::string& name,
                              std::string* path) override;
   std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
-      const std::string& super_device, uint32_t source_slot) override;
+      const std::string& super_device,
+      uint32_t source_slot,
+      uint32_t target_slot) override;
   bool StoreMetadata(const std::string& super_device,
                      android::fs_mgr::MetadataBuilder* builder,
                      uint32_t target_slot) override;
diff --git a/dynamic_partition_control_interface.h b/dynamic_partition_control_interface.h
index 95b01a1..b92d88a 100644
--- a/dynamic_partition_control_interface.h
+++ b/dynamic_partition_control_interface.h
@@ -74,8 +74,12 @@
                                      std::string* path) = 0;
 
   // Retrieve metadata from |super_device| at slot |source_slot|.
+  // On retrofit devices, if |target_slot| != kInvalidSlot, the returned
+  // metadata automatically includes block devices at |target_slot|.
   virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
-      const std::string& super_device, uint32_t source_slot) = 0;
+      const std::string& super_device,
+      uint32_t source_slot,
+      uint32_t target_slot) = 0;
 
   // Write metadata |builder| to |super_device| at slot |target_slot|.
   virtual bool StoreMetadata(const std::string& super_device,
diff --git a/mock_dynamic_partition_control.h b/mock_dynamic_partition_control.h
index 6b233b3..7b37581 100644
--- a/mock_dynamic_partition_control.h
+++ b/mock_dynamic_partition_control.h
@@ -38,9 +38,9 @@
   MOCK_METHOD1(DeviceExists, bool(const std::string&));
   MOCK_METHOD1(GetState, ::android::dm::DmDeviceState(const std::string&));
   MOCK_METHOD2(GetDmDevicePathByName, bool(const std::string&, std::string*));
-  MOCK_METHOD2(LoadMetadataBuilder,
+  MOCK_METHOD3(LoadMetadataBuilder,
                std::unique_ptr<::android::fs_mgr::MetadataBuilder>(
-                   const std::string&, uint32_t));
+                   const std::string&, uint32_t, uint32_t));
   MOCK_METHOD3(StoreMetadata,
                bool(const std::string&,
                     android::fs_mgr::MetadataBuilder*,