Improve the logic for mapping target dynamic partitions.

While applying a retrofit update on a dynamic-partitions-enabled build,
it should always use static target partitions. Otherwise reading them
from the updated partition metadata would end up using mismatching info.

This CL improves the logic in detecting and handling such a case.
- It identifies a retrofit or regular update based on the absence of
  DynamicPartitionMetadata field.
- Upon seeing that, it skips updating partition metadata for the target
  slot, and looks up target partitions as static partitions.
- Source partitions will always be loaded according to the actual state.

This CL also removes the re-mapping of the target partitions from
InitPartitionMetadata(). It only needs to unmap those partitions, since
they may become inconsistent with the updated metadata. However, it's
unnecessary to re-map them, which will be done later as part of
GetDynamicPartitionDevice().

Also updated tests to reflect this.

Bug: 120775936
Test: update_engine_unittests
Test: Apply an update with dynamic partitions; abort and resume.
Test: Apply a retrofit update; abort and resume the update.
Change-Id: Ic07bd98847e91a003101266e426c4d23666810f2
diff --git a/boot_control_android.cc b/boot_control_android.cc
index 7d02ee2..c42c8d7 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -122,10 +122,6 @@
     const string& partition_name_suffix,
     Slot slot,
     string* device) const {
-  if (!dynamic_control_->IsDynamicPartitionsEnabled()) {
-    return DynamicPartitionDeviceStatus::TRY_STATIC;
-  }
-
   string super_device =
       device_dir.Append(fs_mgr_get_super_partition_name(slot)).value();
 
@@ -203,15 +199,22 @@
   }
   base::FilePath device_dir(device_dir_str);
 
-  switch (GetDynamicPartitionDevice(
-      device_dir, partition_name_suffix, slot, device)) {
-    case DynamicPartitionDeviceStatus::SUCCESS:
-      return true;
-    case DynamicPartitionDeviceStatus::TRY_STATIC:
-      break;
-    case DynamicPartitionDeviceStatus::ERROR:  // fallthrough
-    default:
-      return false;
+  // When looking up target partition devices, treat them as static if the
+  // current payload doesn't encode them as dynamic partitions. This may happen
+  // when applying a retrofit update on top of a dynamic-partitions-enabled
+  // build.
+  if (dynamic_control_->IsDynamicPartitionsEnabled() &&
+      (slot == GetCurrentSlot() || is_target_dynamic_)) {
+    switch (GetDynamicPartitionDevice(
+        device_dir, partition_name_suffix, slot, device)) {
+      case DynamicPartitionDeviceStatus::SUCCESS:
+        return true;
+      case DynamicPartitionDeviceStatus::TRY_STATIC:
+        break;
+      case DynamicPartitionDeviceStatus::ERROR:  // fallthrough
+      default:
+        return false;
+    }
   }
 
   base::FilePath path = device_dir.Append(partition_name_suffix);
@@ -289,14 +292,19 @@
 
 namespace {
 
-bool InitPartitionMetadataInternal(
-    DynamicPartitionControlInterface* dynamic_control,
-    const string& source_device,
-    const string& target_device,
-    Slot source_slot,
-    Slot target_slot,
-    const string& target_suffix,
-    const PartitionMetadata& partition_metadata) {
+bool UpdatePartitionMetadata(DynamicPartitionControlInterface* dynamic_control,
+                             Slot source_slot,
+                             Slot target_slot,
+                             const string& target_suffix,
+                             const PartitionMetadata& partition_metadata) {
+  string device_dir_str;
+  if (!dynamic_control->GetDeviceDir(&device_dir_str)) {
+    return false;
+  }
+  base::FilePath device_dir(device_dir_str);
+  auto source_device =
+      device_dir.Append(fs_mgr_get_super_partition_name(source_slot)).value();
+
   auto builder = dynamic_control->LoadMetadataBuilder(
       source_device, source_slot, target_slot);
   if (builder == nullptr) {
@@ -330,7 +338,7 @@
   if (total_size > allocatable_space) {
     LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix
                << " (" << total_size << ") has exceeded " << expr
-               << "allocatable space for dynamic partitions "
+               << " allocatable space for dynamic partitions "
                << allocatable_space << ".";
     return false;
   }
@@ -364,34 +372,21 @@
     }
   }
 
+  auto target_device =
+      device_dir.Append(fs_mgr_get_super_partition_name(target_slot)).value();
   return dynamic_control->StoreMetadata(
       target_device, builder.get(), target_slot);
 }
 
-// Unmap all partitions, and remap partitions as writable.
-bool Remap(DynamicPartitionControlInterface* dynamic_control,
-           const string& target_device,
-           Slot target_slot,
-           const string& target_suffix,
-           const PartitionMetadata& partition_metadata) {
+bool UnmapTargetPartitions(DynamicPartitionControlInterface* dynamic_control,
+                           const string& target_suffix,
+                           const PartitionMetadata& partition_metadata) {
   for (const auto& group : partition_metadata.groups) {
     for (const auto& partition : group.partitions) {
       if (!dynamic_control->UnmapPartitionOnDeviceMapper(
               partition.name + target_suffix, true /* wait */)) {
         return false;
       }
-      if (partition.size == 0) {
-        continue;
-      }
-      string map_path;
-      if (!dynamic_control->MapPartitionOnDeviceMapper(
-              target_device,
-              partition.name + target_suffix,
-              target_slot,
-              true /* force writable */,
-              &map_path)) {
-        return false;
-      }
     }
   }
   return true;
@@ -405,42 +400,37 @@
     return true;
   }
 
-  string device_dir_str;
-  if (!dynamic_control_->GetDeviceDir(&device_dir_str)) {
-    return false;
-  }
-  base::FilePath device_dir(device_dir_str);
-  string target_device =
-      device_dir.Append(fs_mgr_get_super_partition_name(target_slot)).value();
-
-  Slot current_slot = GetCurrentSlot();
-  if (target_slot == current_slot) {
+  auto source_slot = GetCurrentSlot();
+  if (target_slot == source_slot) {
     LOG(ERROR) << "Cannot call InitPartitionMetadata on current slot.";
     return false;
   }
-  string source_device =
-      device_dir.Append(fs_mgr_get_super_partition_name(current_slot)).value();
 
   string target_suffix;
   if (!GetSuffix(target_slot, &target_suffix)) {
     return false;
   }
 
-  if (!InitPartitionMetadataInternal(dynamic_control_.get(),
-                                     source_device,
-                                     target_device,
-                                     current_slot,
-                                     target_slot,
-                                     target_suffix,
-                                     partition_metadata)) {
+  // Although the current build supports dynamic partitions, the given payload
+  // doesn't use it for target partitions. This could happen when applying a
+  // retrofit update. Skip updating the partition metadata for the target slot.
+  is_target_dynamic_ = !partition_metadata.groups.empty();
+  if (!is_target_dynamic_) {
+    return true;
+  }
+
+  // Unmap all the target dynamic partitions because they would become
+  // inconsistent with the new metadata.
+  if (!UnmapTargetPartitions(
+          dynamic_control_.get(), target_suffix, partition_metadata)) {
     return false;
   }
 
-  if (!Remap(dynamic_control_.get(),
-             target_device,
-             target_slot,
-             target_suffix,
-             partition_metadata)) {
+  if (!UpdatePartitionMetadata(dynamic_control_.get(),
+                               source_slot,
+                               target_slot,
+                               target_suffix,
+                               partition_metadata)) {
     return false;
   }