Fix resuming updates on DAP launch devices.

On devices that launch with dynamic partitions, the following
sequence may occur:

- update started (assume current_slot = A)
- super partition metadata slot B initialized
- device rebooted
- init maps system_b from extents in metadata slot A
- update is resumed, but system_b has wrong extents now

InitPartitionMetadata is not called when update is resumed, hence
system_b is not unmapped before GetDynamicPartitionDevice is called.

The new logic for GetDynamicPartitionDevice is as follows:

if (slot == current_slot && partition is mapped) {
    return GetDmDevicePathByName
}
return MapPartitionOnDeviceMapper

The new logic for MapPartitionOnDeviceMapper is as follows:

if (not mapped) return CreateLogicalPartition;
if (mapped) {
    if (mapped by update_engine) {
        return GetDmDevicePathByName;
    }
    DestroyLogicalPartition;
    return CreateLogicalPartition;
}

Test: start OTA, see partition metadata initialized, reboot
      and resume OTA
Test: update_engine_unittests

Fixes: 129292271
Change-Id: If0b541e9aa42a7f462c1061c67750cf360e42732
diff --git a/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc
index bd34ea9..40c2663 100644
--- a/dynamic_partition_control_android.cc
+++ b/dynamic_partition_control_android.cc
@@ -58,7 +58,7 @@
   return GetBoolProperty(kRetrfoitDynamicPartitions, false);
 }
 
-bool DynamicPartitionControlAndroid::MapPartitionOnDeviceMapper(
+bool DynamicPartitionControlAndroid::MapPartitionInternal(
     const std::string& super_device,
     const std::string& target_partition_name,
     uint32_t slot,
@@ -81,6 +81,51 @@
   return true;
 }
 
+bool DynamicPartitionControlAndroid::MapPartitionOnDeviceMapper(
+    const std::string& super_device,
+    const std::string& target_partition_name,
+    uint32_t slot,
+    bool force_writable,
+    std::string* path) {
+  DmDeviceState state = GetState(target_partition_name);
+  if (state == DmDeviceState::ACTIVE) {
+    if (mapped_devices_.find(target_partition_name) != mapped_devices_.end()) {
+      if (GetDmDevicePathByName(target_partition_name, path)) {
+        LOG(INFO) << target_partition_name
+                  << " is mapped on device mapper: " << *path;
+        return true;
+      }
+      LOG(ERROR) << target_partition_name << " is mapped but path is unknown.";
+      return false;
+    }
+    // If target_partition_name is not in mapped_devices_ but state is ACTIVE,
+    // the device might be mapped incorrectly before. Attempt to unmap it.
+    // Note that for source partitions, if GetState() == ACTIVE, callers (e.g.
+    // BootControlAndroid) should not call MapPartitionOnDeviceMapper, but
+    // should directly call GetDmDevicePathByName.
+    if (!UnmapPartitionOnDeviceMapper(target_partition_name, true /* wait */)) {
+      LOG(ERROR) << target_partition_name
+                 << " is mapped before the update, and it cannot be unmapped.";
+      return false;
+    }
+    state = GetState(target_partition_name);
+    if (state != DmDeviceState::INVALID) {
+      LOG(ERROR) << target_partition_name << " is unmapped but state is "
+                 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
+      return false;
+    }
+  }
+  if (state == DmDeviceState::INVALID) {
+    return MapPartitionInternal(
+        super_device, target_partition_name, slot, force_writable, path);
+  }
+
+  LOG(ERROR) << target_partition_name
+             << " is mapped on device mapper but state is unknown: "
+             << static_cast<std::underlying_type_t<DmDeviceState>>(state);
+  return false;
+}
+
 bool DynamicPartitionControlAndroid::UnmapPartitionOnDeviceMapper(
     const std::string& target_partition_name, bool wait) {
   if (DeviceMapper::Instance().GetState(target_partition_name) !=