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/boot_control_android.cc b/boot_control_android.cc
index 8909cd9..1fab85f 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -135,11 +135,11 @@
     return DynamicPartitionDeviceStatus::ERROR;
   }
 
+  Slot current_slot = GetCurrentSlot();
   if (builder->FindPartition(partition_name_suffix) == nullptr) {
     LOG(INFO) << partition_name_suffix
               << " is not in super partition metadata.";
 
-    Slot current_slot = GetCurrentSlot();
     if (IsSuperBlockDevice(device_dir, current_slot, partition_name_suffix)) {
       LOG(ERROR) << "The static partition " << partition_name_suffix
                  << " is a block device for current metadata ("
@@ -152,36 +152,28 @@
     return DynamicPartitionDeviceStatus::TRY_STATIC;
   }
 
-  DmDeviceState state = dynamic_control_->GetState(partition_name_suffix);
-
-  // Device is mapped in the previous GetPartitionDevice() call. Just return
-  // the path.
-  if (state == DmDeviceState::ACTIVE) {
-    if (dynamic_control_->GetDmDevicePathByName(partition_name_suffix,
-                                                device)) {
-      LOG(INFO) << partition_name_suffix
-                << " is mapped on device mapper: " << *device;
-      return DynamicPartitionDeviceStatus::SUCCESS;
+  if (slot == current_slot) {
+    if (dynamic_control_->GetState(partition_name_suffix) !=
+        DmDeviceState::ACTIVE) {
+      LOG(WARNING) << partition_name_suffix << " is at current slot but it is "
+                   << "not mapped. Now try to map it.";
+    } else {
+      if (dynamic_control_->GetDmDevicePathByName(partition_name_suffix,
+                                                  device)) {
+        LOG(INFO) << partition_name_suffix
+                  << " is mapped on device mapper: " << *device;
+        return DynamicPartitionDeviceStatus::SUCCESS;
+      }
+      LOG(ERROR) << partition_name_suffix << "is mapped but path is unknown.";
+      return DynamicPartitionDeviceStatus::ERROR;
     }
-    LOG(ERROR) << partition_name_suffix << " is mapped but path is unknown.";
-    return DynamicPartitionDeviceStatus::ERROR;
   }
 
-  if (state == DmDeviceState::INVALID) {
-    bool force_writable = slot != GetCurrentSlot();
-    if (dynamic_control_->MapPartitionOnDeviceMapper(super_device,
-                                                     partition_name_suffix,
-                                                     slot,
-                                                     force_writable,
-                                                     device)) {
-      return DynamicPartitionDeviceStatus::SUCCESS;
-    }
-    return DynamicPartitionDeviceStatus::ERROR;
+  bool force_writable = slot != current_slot;
+  if (dynamic_control_->MapPartitionOnDeviceMapper(
+          super_device, partition_name_suffix, slot, force_writable, device)) {
+    return DynamicPartitionDeviceStatus::SUCCESS;
   }
-
-  LOG(ERROR) << partition_name_suffix
-             << " is mapped on device mapper but state is unknown: "
-             << static_cast<std::underlying_type_t<DmDeviceState>>(state);
   return DynamicPartitionDeviceStatus::ERROR;
 }
 
diff --git a/boot_control_android_unittest.cc b/boot_control_android_unittest.cc
index bb9903e..b2885a3 100644
--- a/boot_control_android_unittest.cc
+++ b/boot_control_android_unittest.cc
@@ -593,10 +593,19 @@
   EXPECT_EQ(GetDmDevice(S("system")), system_device);
 
   EXPECT_CALL(dynamicControl(), GetState(T("system")))
-      .Times(1)
+      .Times(AnyNumber())
       .WillOnce(Return(DmDeviceState::ACTIVE));
+  EXPECT_CALL(dynamicControl(),
+              MapPartitionOnDeviceMapper(
+                  GetSuperDevice(target()), T("system"), target(), _, _))
+      .Times(AnyNumber())
+      .WillRepeatedly(
+          Invoke([](const auto&, const auto& name, auto, auto, auto* device) {
+            *device = "/fake/remapped/" + name;
+            return true;
+          }));
   EXPECT_TRUE(bootctl_.GetPartitionDevice("system", target(), &system_device));
-  EXPECT_EQ(GetDmDevice(T("system")), system_device);
+  EXPECT_EQ("/fake/remapped/" + T("system"), system_device);
 
   // Static partition "bar".
   EXPECT_CALL(dynamicControl(), GetState(S("bar"))).Times(0);
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) !=
diff --git a/dynamic_partition_control_android.h b/dynamic_partition_control_android.h
index 0ccab4e..1233b64 100644
--- a/dynamic_partition_control_android.h
+++ b/dynamic_partition_control_android.h
@@ -56,6 +56,11 @@
   std::set<std::string> mapped_devices_;
 
   void CleanupInternal(bool wait);
+  bool MapPartitionInternal(const std::string& super_device,
+                            const std::string& target_partition_name,
+                            uint32_t slot,
+                            bool force_writable,
+                            std::string* path);
 
   DISALLOW_COPY_AND_ASSIGN(DynamicPartitionControlAndroid);
 };