Proper split of BootControl and DynamicPartitionControl.

All dynamic/static partitions stuff are moved to
DynamicPartitionControlAndroid.

After this patch:
(1) BootControl remains a simple shim over the boot control HAL.
    (BootControl still have two calls that is a delegate
    to DynamicPartitionControl, which will be cleaned up
    in follow up CLs.)
(2) DynamicPartitionControlInterface API is minimized. All
    libdm and other Android specific details are hidden from the
    API surface now.

Also move tests from boot_control_unittest to
dynamic_partition_control_unittest because functionalities are moved.

Test: update_engine_unittests

Change-Id: I6ed902197569f9f0ef40e02703634e9078a4b060
diff --git a/Android.bp b/Android.bp
index d6f1090..84d4a7a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -677,7 +677,6 @@
     test_suites: ["device-tests"],
 
     srcs: [
-        "boot_control_android_unittest.cc",
         "certificate_checker_unittest.cc",
         "common/action_pipe_unittest.cc",
         "common/action_processor_unittest.cc",
diff --git a/boot_control_android.cc b/boot_control_android.cc
index b1d775e..05e9637 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -24,8 +24,6 @@
 #include <base/logging.h>
 #include <bootloader_message/bootloader_message.h>
 #include <brillo/message_loops/message_loop.h>
-#include <fs_mgr_overlayfs.h>
-#include <libdm/dm.h>
 
 #include "update_engine/common/utils.h"
 #include "update_engine/dynamic_partition_control_android.h"
@@ -88,131 +86,12 @@
   return module_->getCurrentSlot();
 }
 
-bool BootControlAndroid::GetSuffix(Slot slot, string* suffix) const {
-  auto store_suffix_cb = [&suffix](hidl_string cb_suffix) {
-    *suffix = cb_suffix.c_str();
-  };
-  Return<void> ret = module_->getSuffix(slot, store_suffix_cb);
-
-  if (!ret.isOk()) {
-    LOG(ERROR) << "boot_control impl returned no suffix for slot "
-               << SlotName(slot);
-    return false;
-  }
-  return true;
-}
-
-bool BootControlAndroid::IsSuperBlockDevice(
-    const base::FilePath& device_dir,
-    Slot slot,
-    const string& partition_name_suffix) const {
-  string source_device =
-      device_dir.Append(dynamic_control_->GetSuperPartitionName(slot)).value();
-  auto source_metadata =
-      dynamic_control_->LoadMetadataBuilder(source_device, slot);
-  return source_metadata->HasBlockDevice(partition_name_suffix);
-}
-
-BootControlAndroid::DynamicPartitionDeviceStatus
-BootControlAndroid::GetDynamicPartitionDevice(
-    const base::FilePath& device_dir,
-    const string& partition_name_suffix,
-    Slot slot,
-    string* device) const {
-  string super_device =
-      device_dir.Append(dynamic_control_->GetSuperPartitionName(slot)).value();
-
-  auto builder = dynamic_control_->LoadMetadataBuilder(super_device, slot);
-
-  if (builder == nullptr) {
-    LOG(ERROR) << "No metadata in slot "
-               << BootControlInterface::SlotName(slot);
-    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.";
-
-    if (IsSuperBlockDevice(device_dir, current_slot, partition_name_suffix)) {
-      LOG(ERROR) << "The static partition " << partition_name_suffix
-                 << " is a block device for current metadata ("
-                 << dynamic_control_->GetSuperPartitionName(current_slot)
-                 << ", slot " << BootControlInterface::SlotName(current_slot)
-                 << "). It cannot be used as a logical partition.";
-      return DynamicPartitionDeviceStatus::ERROR;
-    }
-
-    return DynamicPartitionDeviceStatus::TRY_STATIC;
-  }
-
-  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;
-    }
-  }
-
-  bool force_writable = slot != current_slot;
-  if (dynamic_control_->MapPartitionOnDeviceMapper(
-          super_device, partition_name_suffix, slot, force_writable, device)) {
-    return DynamicPartitionDeviceStatus::SUCCESS;
-  }
-  return DynamicPartitionDeviceStatus::ERROR;
-}
 
 bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
                                             Slot slot,
                                             string* device) const {
-  string suffix;
-  if (!GetSuffix(slot, &suffix)) {
-    return false;
-  }
-  const string partition_name_suffix = partition_name + suffix;
-
-  string device_dir_str;
-  if (!dynamic_control_->GetDeviceDir(&device_dir_str)) {
-    return false;
-  }
-  base::FilePath device_dir(device_dir_str);
-
-  // 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_->GetDynamicPartitionsFeatureFlag().IsEnabled() &&
-      (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);
-  if (!dynamic_control_->DeviceExists(path.value())) {
-    LOG(ERROR) << "Device file " << path.value() << " does not exist.";
-    return false;
-  }
-
-  *device = path.value();
-  return true;
+  return dynamic_control_->GetPartitionDevice(
+      partition_name, slot, GetCurrentSlot(), device);
 }
 
 bool BootControlAndroid::IsSlotBootable(Slot slot) const {
@@ -283,31 +162,8 @@
     Slot target_slot,
     const DeltaArchiveManifest& manifest,
     bool update_metadata) {
-  if (fs_mgr_overlayfs_is_setup()) {
-    // Non DAP devices can use overlayfs as well.
-    LOG(WARNING)
-        << "overlayfs overrides are active and can interfere with our "
-           "resources.\n"
-        << "run adb enable-verity to deactivate if required and try again.";
-  }
-  if (!dynamic_control_->GetDynamicPartitionsFeatureFlag().IsEnabled()) {
-    return true;
-  }
 
   auto source_slot = GetCurrentSlot();
-  if (target_slot == source_slot) {
-    LOG(ERROR) << "Cannot call PreparePartitionsForUpdate on current slot.";
-    return false;
-  }
-
-  // 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_ = !manifest.dynamic_partition_metadata().groups().empty();
-  if (!is_target_dynamic_) {
-    return true;
-  }
-
   return dynamic_control_->PreparePartitionsForUpdate(
       source_slot, target_slot, manifest, update_metadata);
 }
diff --git a/boot_control_android.h b/boot_control_android.h
index 65543ca..c81f86d 100644
--- a/boot_control_android.h
+++ b/boot_control_android.h
@@ -22,10 +22,10 @@
 #include <string>
 
 #include <android/hardware/boot/1.0/IBootControl.h>
-#include <base/files/file_util.h>
 #include <liblp/builder.h>
 
 #include "update_engine/common/boot_control.h"
+#include "update_engine/dynamic_partition_control_android.h"
 #include "update_engine/dynamic_partition_control_interface.h"
 
 namespace chromeos_update_engine {
@@ -58,35 +58,10 @@
 
  private:
   ::android::sp<::android::hardware::boot::V1_0::IBootControl> module_;
-  std::unique_ptr<DynamicPartitionControlInterface> dynamic_control_;
+  std::unique_ptr<DynamicPartitionControlAndroid> dynamic_control_;
 
   friend class BootControlAndroidTest;
 
-  // Wrapper method of IBootControl::getSuffix().
-  bool GetSuffix(Slot slot, std::string* out) const;
-
-  enum class DynamicPartitionDeviceStatus {
-    SUCCESS,
-    ERROR,
-    TRY_STATIC,
-  };
-
-  DynamicPartitionDeviceStatus GetDynamicPartitionDevice(
-      const base::FilePath& device_dir,
-      const std::string& partition_name_suffix,
-      Slot slot,
-      std::string* device) const;
-
-  // Return true if |partition_name_suffix| is a block device of
-  // super partition metadata slot |slot|.
-  bool IsSuperBlockDevice(const base::FilePath& device_dir,
-                          Slot slot,
-                          const std::string& partition_name_suffix) const;
-
-  // Whether the target partitions should be loaded as dynamic partitions. Set
-  // by PreparePartitionsForUpdate() per each update.
-  bool is_target_dynamic_{false};
-
   DISALLOW_COPY_AND_ASSIGN(BootControlAndroid);
 };
 
diff --git a/boot_control_android_unittest.cc b/boot_control_android_unittest.cc
deleted file mode 100644
index e44af15..0000000
--- a/boot_control_android_unittest.cc
+++ /dev/null
@@ -1,250 +0,0 @@
-//
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "update_engine/boot_control_android.h"
-
-#include <set>
-#include <vector>
-
-#include <base/logging.h>
-#include <base/strings/string_util.h>
-#include <fs_mgr.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <libdm/dm.h>
-
-#include "update_engine/dynamic_partition_test_utils.h"
-#include "update_engine/mock_boot_control_hal.h"
-#include "update_engine/mock_dynamic_partition_control.h"
-
-using android::dm::DmDeviceState;
-using android::hardware::Void;
-using std::string;
-using testing::_;
-using testing::AnyNumber;
-using testing::Invoke;
-using testing::NiceMock;
-using testing::Not;
-using testing::Return;
-
-namespace chromeos_update_engine {
-
-class BootControlAndroidTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    // Fake init bootctl_
-    bootctl_.module_ = new NiceMock<MockBootControlHal>();
-    bootctl_.dynamic_control_ =
-        std::make_unique<NiceMock<MockDynamicPartitionControl>>();
-
-    ON_CALL(module(), getNumberSlots()).WillByDefault(Invoke([] {
-      return kMaxNumSlots;
-    }));
-    ON_CALL(module(), getSuffix(_, _))
-        .WillByDefault(Invoke([](auto slot, auto cb) {
-          EXPECT_LE(slot, kMaxNumSlots);
-          cb(slot < kMaxNumSlots ? kSlotSuffixes[slot] : "");
-          return Void();
-        }));
-
-    ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
-        .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
-    ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
-        .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
-    ON_CALL(dynamicControl(), DeviceExists(_)).WillByDefault(Return(true));
-    ON_CALL(dynamicControl(), GetDeviceDir(_))
-        .WillByDefault(Invoke([](auto path) {
-          *path = kFakeDevicePath;
-          return true;
-        }));
-    ON_CALL(dynamicControl(), GetDmDevicePathByName(_, _))
-        .WillByDefault(Invoke([](auto partition_name_suffix, auto device) {
-          *device = GetDmDevice(partition_name_suffix);
-          return true;
-        }));
-
-    ON_CALL(dynamicControl(), GetSuperPartitionName(_))
-        .WillByDefault(Return(kFakeSuper));
-  }
-
-  std::string GetSuperDevice(uint32_t slot) {
-    return GetDevice(dynamicControl().GetSuperPartitionName(slot));
-  }
-
-  // Return the mocked HAL module.
-  NiceMock<MockBootControlHal>& module() {
-    return static_cast<NiceMock<MockBootControlHal>&>(*bootctl_.module_);
-  }
-
-  // Return the mocked DynamicPartitionControlInterface.
-  NiceMock<MockDynamicPartitionControl>& dynamicControl() {
-    return static_cast<NiceMock<MockDynamicPartitionControl>&>(
-        *bootctl_.dynamic_control_);
-  }
-
-  // Set the fake metadata to return when LoadMetadataBuilder is called on
-  // |slot|.
-  void SetMetadata(uint32_t slot, const PartitionSuffixSizes& sizes) {
-    EXPECT_CALL(dynamicControl(),
-                LoadMetadataBuilder(GetSuperDevice(slot), slot))
-        .Times(AnyNumber())
-        .WillRepeatedly(Invoke([sizes](auto, auto) {
-          return NewFakeMetadata(PartitionSuffixSizesToManifest(sizes));
-        }));
-  }
-
-  uint32_t source() { return slots_.source; }
-
-  uint32_t target() { return slots_.target; }
-
-  // Return partition names with suffix of source().
-  string S(const string& name) { return name + kSlotSuffixes[source()]; }
-
-  // Return partition names with suffix of target().
-  string T(const string& name) { return name + kSlotSuffixes[target()]; }
-
-  // Set source and target slots to use before testing.
-  void SetSlots(const TestParam& slots) {
-    slots_ = slots;
-
-    ON_CALL(module(), getCurrentSlot()).WillByDefault(Invoke([this] {
-      return source();
-    }));
-  }
-
-  bool PreparePartitionsForUpdate(uint32_t slot,
-                                  PartitionSizes partition_sizes,
-                                  bool update_metadata = true) {
-    auto m = PartitionSizesToManifest(partition_sizes);
-    return bootctl_.PreparePartitionsForUpdate(slot, m, update_metadata);
-  }
-
-  BootControlAndroid bootctl_;  // BootControlAndroid under test.
-  TestParam slots_;
-};
-
-class BootControlAndroidTestP
-    : public BootControlAndroidTest,
-      public ::testing::WithParamInterface<TestParam> {
- public:
-  void SetUp() override {
-    BootControlAndroidTest::SetUp();
-    SetSlots(GetParam());
-  }
-};
-
-// Test applying retrofit update on a build with dynamic partitions enabled.
-TEST_P(BootControlAndroidTestP,
-       ApplyRetrofitUpdateOnDynamicPartitionsEnabledBuild) {
-  SetMetadata(source(),
-              {{S("system"), 2_GiB},
-               {S("vendor"), 1_GiB},
-               {T("system"), 2_GiB},
-               {T("vendor"), 1_GiB}});
-
-  // Not calling through BootControlAndroidTest::PreparePartitionsForUpdate(),
-  // since we don't want any default group in the PartitionMetadata.
-  EXPECT_TRUE(bootctl_.PreparePartitionsForUpdate(target(), {}, true));
-
-  // Should use dynamic source partitions.
-  EXPECT_CALL(dynamicControl(), GetState(S("system")))
-      .Times(1)
-      .WillOnce(Return(DmDeviceState::ACTIVE));
-  string system_device;
-  EXPECT_TRUE(bootctl_.GetPartitionDevice("system", source(), &system_device));
-  EXPECT_EQ(GetDmDevice(S("system")), system_device);
-
-  // Should use static target partitions without querying dynamic control.
-  EXPECT_CALL(dynamicControl(), GetState(T("system"))).Times(0);
-  EXPECT_TRUE(bootctl_.GetPartitionDevice("system", target(), &system_device));
-  EXPECT_EQ(GetDevice(T("system")), system_device);
-
-  // Static partition "bar".
-  EXPECT_CALL(dynamicControl(), GetState(S("bar"))).Times(0);
-  std::string bar_device;
-  EXPECT_TRUE(bootctl_.GetPartitionDevice("bar", source(), &bar_device));
-  EXPECT_EQ(GetDevice(S("bar")), bar_device);
-
-  EXPECT_CALL(dynamicControl(), GetState(T("bar"))).Times(0);
-  EXPECT_TRUE(bootctl_.GetPartitionDevice("bar", target(), &bar_device));
-  EXPECT_EQ(GetDevice(T("bar")), bar_device);
-}
-
-TEST_P(BootControlAndroidTestP, GetPartitionDeviceWhenResumingUpdate) {
-  // Both of the two slots contain valid partition metadata, since this is
-  // resuming an update.
-  SetMetadata(source(),
-              {{S("system"), 2_GiB},
-               {S("vendor"), 1_GiB},
-               {T("system"), 2_GiB},
-               {T("vendor"), 1_GiB}});
-  SetMetadata(target(),
-              {{S("system"), 2_GiB},
-               {S("vendor"), 1_GiB},
-               {T("system"), 2_GiB},
-               {T("vendor"), 1_GiB}});
-
-  EXPECT_CALL(dynamicControl(), PreparePartitionsForUpdate(_, _, _, false))
-      .WillOnce(Return(true));
-
-  EXPECT_TRUE(PreparePartitionsForUpdate(
-      target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}, false));
-
-  // Dynamic partition "system".
-  EXPECT_CALL(dynamicControl(), GetState(S("system")))
-      .Times(1)
-      .WillOnce(Return(DmDeviceState::ACTIVE));
-  string system_device;
-  EXPECT_TRUE(bootctl_.GetPartitionDevice("system", source(), &system_device));
-  EXPECT_EQ(GetDmDevice(S("system")), system_device);
-
-  EXPECT_CALL(dynamicControl(), GetState(T("system")))
-      .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("/fake/remapped/" + T("system"), system_device);
-
-  // Static partition "bar".
-  EXPECT_CALL(dynamicControl(), GetState(S("bar"))).Times(0);
-  std::string bar_device;
-  EXPECT_TRUE(bootctl_.GetPartitionDevice("bar", source(), &bar_device));
-  EXPECT_EQ(GetDevice(S("bar")), bar_device);
-
-  EXPECT_CALL(dynamicControl(), GetState(T("bar"))).Times(0);
-  EXPECT_TRUE(bootctl_.GetPartitionDevice("bar", target(), &bar_device));
-  EXPECT_EQ(GetDevice(T("bar")), bar_device);
-}
-
-INSTANTIATE_TEST_CASE_P(BootControlAndroidTest,
-                        BootControlAndroidTestP,
-                        testing::Values(TestParam{0, 1}, TestParam{1, 0}));
-
-TEST_F(BootControlAndroidTest, ApplyingToCurrentSlot) {
-  SetSlots({1, 1});
-  EXPECT_FALSE(PreparePartitionsForUpdate(target(), {}))
-      << "Should not be able to apply to current slot.";
-}
-
-}  // namespace chromeos_update_engine
diff --git a/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc
index c641a6b..464cdf1 100644
--- a/dynamic_partition_control_android.cc
+++ b/dynamic_partition_control_android.cc
@@ -31,6 +31,8 @@
 #include <bootloader_message/bootloader_message.h>
 #include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
+#include <fs_mgr_overlayfs.h>
+#include <libdm/dm.h>
 #include <libsnapshot/snapshot.h>
 
 #include "update_engine/common/boot_control_interface.h"
@@ -354,6 +356,31 @@
     uint32_t target_slot,
     const DeltaArchiveManifest& manifest,
     bool update) {
+  if (fs_mgr_overlayfs_is_setup()) {
+    // Non DAP devices can use overlayfs as well.
+    LOG(WARNING)
+        << "overlayfs overrides are active and can interfere with our "
+           "resources.\n"
+        << "run adb enable-verity to deactivate if required and try again.";
+  }
+
+  if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
+    return true;
+  }
+
+  if (target_slot == source_slot) {
+    LOG(ERROR) << "Cannot call PreparePartitionsForUpdate on current slot.";
+    return false;
+  }
+
+  // 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_ = !manifest.dynamic_partition_metadata().groups().empty();
+  if (!is_target_dynamic_) {
+    return true;
+  }
+
   target_supports_snapshot_ =
       manifest.dynamic_partition_metadata().snapshot_enabled();
 
@@ -532,4 +559,105 @@
   return true;
 }
 
+bool DynamicPartitionControlAndroid::GetPartitionDevice(
+    const std::string& partition_name,
+    uint32_t slot,
+    uint32_t current_slot,
+    std::string* device) {
+  const auto& partition_name_suffix =
+      partition_name + SlotSuffixForSlotNumber(slot);
+  std::string device_dir_str;
+  TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+  base::FilePath device_dir(device_dir_str);
+
+  // 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 (GetDynamicPartitionsFeatureFlag().IsEnabled() &&
+      (slot == current_slot || is_target_dynamic_)) {
+    switch (GetDynamicPartitionDevice(
+        device_dir, partition_name_suffix, slot, current_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);
+  if (!DeviceExists(path.value())) {
+    LOG(ERROR) << "Device file " << path.value() << " does not exist.";
+    return false;
+  }
+
+  *device = path.value();
+  return true;
+}
+
+bool DynamicPartitionControlAndroid::IsSuperBlockDevice(
+    const base::FilePath& device_dir,
+    uint32_t current_slot,
+    const std::string& partition_name_suffix) {
+  std::string source_device =
+      device_dir.Append(GetSuperPartitionName(current_slot)).value();
+  auto source_metadata = LoadMetadataBuilder(source_device, current_slot);
+  return source_metadata->HasBlockDevice(partition_name_suffix);
+}
+
+DynamicPartitionControlAndroid::DynamicPartitionDeviceStatus
+DynamicPartitionControlAndroid::GetDynamicPartitionDevice(
+    const base::FilePath& device_dir,
+    const std::string& partition_name_suffix,
+    uint32_t slot,
+    uint32_t current_slot,
+    std::string* device) {
+  std::string super_device =
+      device_dir.Append(GetSuperPartitionName(slot)).value();
+
+  auto builder = LoadMetadataBuilder(super_device, slot);
+  if (builder == nullptr) {
+    LOG(ERROR) << "No metadata in slot "
+               << BootControlInterface::SlotName(slot);
+    return DynamicPartitionDeviceStatus::ERROR;
+  }
+  if (builder->FindPartition(partition_name_suffix) == nullptr) {
+    LOG(INFO) << partition_name_suffix
+              << " is not in super partition metadata.";
+
+    if (IsSuperBlockDevice(device_dir, current_slot, partition_name_suffix)) {
+      LOG(ERROR) << "The static partition " << partition_name_suffix
+                 << " is a block device for current metadata."
+                 << "It cannot be used as a logical partition.";
+      return DynamicPartitionDeviceStatus::ERROR;
+    }
+
+    return DynamicPartitionDeviceStatus::TRY_STATIC;
+  }
+
+  if (slot == current_slot) {
+    if (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 (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;
+    }
+  }
+
+  bool force_writable = slot != current_slot;
+  if (MapPartitionOnDeviceMapper(
+          super_device, partition_name_suffix, slot, force_writable, device)) {
+    return DynamicPartitionDeviceStatus::SUCCESS;
+  }
+  return DynamicPartitionDeviceStatus::ERROR;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/dynamic_partition_control_android.h b/dynamic_partition_control_android.h
index 07ce281..af37398 100644
--- a/dynamic_partition_control_android.h
+++ b/dynamic_partition_control_android.h
@@ -23,6 +23,7 @@
 #include <set>
 #include <string>
 
+#include <base/files/file_util.h>
 #include <libsnapshot/auto_device.h>
 #include <libsnapshot/snapshot.h>
 
@@ -34,27 +35,24 @@
   ~DynamicPartitionControlAndroid();
   FeatureFlag GetDynamicPartitionsFeatureFlag() override;
   FeatureFlag GetVirtualAbFeatureFlag() override;
-  bool MapPartitionOnDeviceMapper(const std::string& super_device,
-                                  const std::string& target_partition_name,
-                                  uint32_t slot,
-                                  bool force_writable,
-                                  std::string* path) override;
   void Cleanup() override;
-  bool DeviceExists(const std::string& path) override;
-  android::dm::DmDeviceState GetState(const std::string& name) override;
-  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;
 
   bool PreparePartitionsForUpdate(uint32_t source_slot,
                                   uint32_t target_slot,
                                   const DeltaArchiveManifest& manifest,
                                   bool update) override;
-  bool GetDeviceDir(std::string* path) override;
-  std::string GetSuperPartitionName(uint32_t slot) override;
   bool FinishUpdate() override;
 
+  // Return the device for partition |partition_name| at slot |slot|.
+  // |current_slot| should be set to the current active slot.
+  // Note: this function is only used by BootControl*::GetPartitionDevice.
+  // Other callers should prefer BootControl*::GetPartitionDevice over
+  // BootControl*::GetDynamicPartitionControl()->GetPartitionDevice().
+  bool GetPartitionDevice(const std::string& partition_name,
+                          uint32_t slot,
+                          uint32_t current_slot,
+                          std::string* device);
+
  protected:
   // These functions are exposed for testing.
 
@@ -84,6 +82,45 @@
                              android::fs_mgr::MetadataBuilder* builder,
                              uint32_t target_slot);
 
+  // Map logical partition on device-mapper.
+  // |super_device| is the device path of the physical partition ("super").
+  // |target_partition_name| is the identifier used in metadata; for example,
+  // "vendor_a"
+  // |slot| is the selected slot to mount; for example, 0 for "_a".
+  // Returns true if mapped successfully; if so, |path| is set to the device
+  // path of the mapped logical partition.
+  virtual bool MapPartitionOnDeviceMapper(
+      const std::string& super_device,
+      const std::string& target_partition_name,
+      uint32_t slot,
+      bool force_writable,
+      std::string* path);
+
+  // Return true if a static partition exists at device path |path|.
+  virtual bool DeviceExists(const std::string& path);
+
+  // Returns the current state of the underlying device mapper device
+  // with given name.
+  // One of INVALID, SUSPENDED or ACTIVE.
+  virtual android::dm::DmDeviceState GetState(const std::string& name);
+
+  // Returns the path to the device mapper device node in '/dev' corresponding
+  // to 'name'. If the device does not exist, false is returned, and the path
+  // parameter is not set.
+  virtual bool GetDmDevicePathByName(const std::string& name,
+                                     std::string* path);
+
+  // Retrieve metadata from |super_device| at slot |source_slot|.
+  virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
+      const std::string& super_device, uint32_t source_slot);
+
+  // Return a possible location for devices listed by name.
+  virtual bool GetDeviceDir(std::string* path);
+
+  // Return the name of the super partition (which stores super partition
+  // metadata) for a given slot.
+  virtual std::string GetSuperPartitionName(uint32_t slot);
+
  private:
   friend class DynamicPartitionControlAndroidTest;
 
@@ -112,12 +149,38 @@
                                           uint32_t target_slot,
                                           const DeltaArchiveManifest& manifest);
 
+  enum class DynamicPartitionDeviceStatus {
+    SUCCESS,
+    ERROR,
+    TRY_STATIC,
+  };
+
+  // Return SUCCESS and path in |device| if partition is dynamic.
+  // Return ERROR if any error.
+  // Return TRY_STATIC if caller should resolve the partition as a static
+  // partition instead.
+  DynamicPartitionDeviceStatus GetDynamicPartitionDevice(
+      const base::FilePath& device_dir,
+      const std::string& partition_name_suffix,
+      uint32_t slot,
+      uint32_t current_slot,
+      std::string* device);
+
+  // Return true if |partition_name_suffix| is a block device of
+  // super partition metadata slot |slot|.
+  bool IsSuperBlockDevice(const base::FilePath& device_dir,
+                          uint32_t current_slot,
+                          const std::string& partition_name_suffix);
+
   std::set<std::string> mapped_devices_;
   const FeatureFlag dynamic_partitions_;
   const FeatureFlag virtual_ab_;
   std::unique_ptr<android::snapshot::SnapshotManager> snapshot_;
   std::unique_ptr<android::snapshot::AutoDevice> metadata_device_;
   bool target_supports_snapshot_ = false;
+  // Whether the target partitions should be loaded as dynamic partitions. Set
+  // by PreparePartitionsForUpdate() per each update.
+  bool is_target_dynamic_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(DynamicPartitionControlAndroid);
 };
diff --git a/dynamic_partition_control_android_unittest.cc b/dynamic_partition_control_android_unittest.cc
index e8ef1f9..10075ed 100644
--- a/dynamic_partition_control_android_unittest.cc
+++ b/dynamic_partition_control_android_unittest.cc
@@ -27,9 +27,11 @@
 #include "update_engine/dynamic_partition_test_utils.h"
 #include "update_engine/mock_dynamic_partition_control.h"
 
+using android::dm::DmDeviceState;
 using std::string;
 using testing::_;
 using testing::AnyNumber;
+using testing::AnyOf;
 using testing::Invoke;
 using testing::NiceMock;
 using testing::Not;
@@ -55,6 +57,12 @@
 
     ON_CALL(dynamicControl(), GetSuperPartitionName(_))
         .WillByDefault(Return(kFakeSuper));
+
+    ON_CALL(dynamicControl(), GetDmDevicePathByName(_, _))
+        .WillByDefault(Invoke([](auto partition_name_suffix, auto device) {
+          *device = GetDmDevice(partition_name_suffix);
+          return true;
+        }));
   }
 
   // Return the mocked DynamicPartitionControlInterface.
@@ -283,6 +291,122 @@
       << "Should not be able to grow over size of super / 2";
 }
 
+TEST_P(DynamicPartitionControlAndroidTestP,
+       ApplyRetrofitUpdateOnDynamicPartitionsEnabledBuild) {
+  ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
+      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::RETROFIT)));
+  // Static partition {system,bar}_{a,b} exists.
+  EXPECT_CALL(dynamicControl(),
+              DeviceExists(AnyOf(GetDevice(S("bar")),
+                                 GetDevice(T("bar")),
+                                 GetDevice(S("system")),
+                                 GetDevice(T("system")))))
+      .WillRepeatedly(Return(true));
+
+  SetMetadata(source(),
+              {{S("system"), 2_GiB},
+               {S("vendor"), 1_GiB},
+               {T("system"), 2_GiB},
+               {T("vendor"), 1_GiB}});
+
+  // Not calling through
+  // DynamicPartitionControlAndroidTest::PreparePartitionsForUpdate(), since we
+  // don't want any default group in the PartitionMetadata.
+  EXPECT_TRUE(dynamicControl().PreparePartitionsForUpdate(
+      source(), target(), {}, true));
+
+  // Should use dynamic source partitions.
+  EXPECT_CALL(dynamicControl(), GetState(S("system")))
+      .Times(1)
+      .WillOnce(Return(DmDeviceState::ACTIVE));
+  string system_device;
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "system", source(), source(), &system_device));
+  EXPECT_EQ(GetDmDevice(S("system")), system_device);
+
+  // Should use static target partitions without querying dynamic control.
+  EXPECT_CALL(dynamicControl(), GetState(T("system"))).Times(0);
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "system", target(), source(), &system_device));
+  EXPECT_EQ(GetDevice(T("system")), system_device);
+
+  // Static partition "bar".
+  EXPECT_CALL(dynamicControl(), GetState(S("bar"))).Times(0);
+  std::string bar_device;
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "bar", source(), source(), &bar_device));
+  EXPECT_EQ(GetDevice(S("bar")), bar_device);
+
+  EXPECT_CALL(dynamicControl(), GetState(T("bar"))).Times(0);
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "bar", target(), source(), &bar_device));
+  EXPECT_EQ(GetDevice(T("bar")), bar_device);
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP,
+       GetPartitionDeviceWhenResumingUpdate) {
+  // Static partition bar_{a,b} exists.
+  EXPECT_CALL(dynamicControl(),
+              DeviceExists(AnyOf(GetDevice(S("bar")), GetDevice(T("bar")))))
+      .WillRepeatedly(Return(true));
+
+  // Both of the two slots contain valid partition metadata, since this is
+  // resuming an update.
+  SetMetadata(source(),
+              {{S("system"), 2_GiB},
+               {S("vendor"), 1_GiB},
+               {T("system"), 2_GiB},
+               {T("vendor"), 1_GiB}});
+  SetMetadata(target(),
+              {{S("system"), 2_GiB},
+               {S("vendor"), 1_GiB},
+               {T("system"), 2_GiB},
+               {T("vendor"), 1_GiB}});
+
+  EXPECT_TRUE(dynamicControl().PreparePartitionsForUpdate(
+      source(),
+      target(),
+      PartitionSizesToManifest({{"system", 2_GiB}, {"vendor", 1_GiB}}),
+      false));
+
+  // Dynamic partition "system".
+  EXPECT_CALL(dynamicControl(), GetState(S("system")))
+      .Times(1)
+      .WillOnce(Return(DmDeviceState::ACTIVE));
+  string system_device;
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "system", source(), source(), &system_device));
+  EXPECT_EQ(GetDmDevice(S("system")), system_device);
+
+  EXPECT_CALL(dynamicControl(), GetState(T("system")))
+      .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(dynamicControl().GetPartitionDevice(
+      "system", target(), source(), &system_device));
+  EXPECT_EQ("/fake/remapped/" + T("system"), system_device);
+
+  // Static partition "bar".
+  EXPECT_CALL(dynamicControl(), GetState(S("bar"))).Times(0);
+  std::string bar_device;
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "bar", source(), source(), &bar_device));
+  EXPECT_EQ(GetDevice(S("bar")), bar_device);
+
+  EXPECT_CALL(dynamicControl(), GetState(T("bar"))).Times(0);
+  EXPECT_TRUE(dynamicControl().GetPartitionDevice(
+      "bar", target(), source(), &bar_device));
+  EXPECT_EQ(GetDevice(T("bar")), bar_device);
+}
+
 INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
                         DynamicPartitionControlAndroidTestP,
                         testing::Values(TestParam{0, 1}, TestParam{1, 0}));
@@ -486,4 +610,10 @@
                                           {"deleted", 64_MiB}}));
 }
 
+TEST_F(DynamicPartitionControlAndroidTest, ApplyingToCurrentSlot) {
+  SetSlots({1, 1});
+  EXPECT_FALSE(PreparePartitionsForUpdate({}))
+      << "Should not be able to apply to current slot.";
+}
+
 }  // namespace chromeos_update_engine
diff --git a/dynamic_partition_control_interface.h b/dynamic_partition_control_interface.h
index 9c18973..b5be2be 100644
--- a/dynamic_partition_control_interface.h
+++ b/dynamic_partition_control_interface.h
@@ -22,11 +22,6 @@
 #include <memory>
 #include <string>
 
-#include <base/files/file_util.h>
-#include <libdm/dm.h>
-#include <liblp/builder.h>
-
-#include "update_engine/common/boot_control_interface.h"
 #include "update_engine/update_metadata.pb.h"
 
 namespace chromeos_update_engine {
@@ -55,41 +50,9 @@
   // Return the feature flags of Virtual A/B on this device.
   virtual FeatureFlag GetVirtualAbFeatureFlag() = 0;
 
-  // Map logical partition on device-mapper.
-  // |super_device| is the device path of the physical partition ("super").
-  // |target_partition_name| is the identifier used in metadata; for example,
-  // "vendor_a"
-  // |slot| is the selected slot to mount; for example, 0 for "_a".
-  // Returns true if mapped successfully; if so, |path| is set to the device
-  // path of the mapped logical partition.
-  virtual bool MapPartitionOnDeviceMapper(
-      const std::string& super_device,
-      const std::string& target_partition_name,
-      uint32_t slot,
-      bool force_writable,
-      std::string* path) = 0;
-
   // Do necessary cleanups before destroying the object.
   virtual void Cleanup() = 0;
 
-  // Return true if a static partition exists at device path |path|.
-  virtual bool DeviceExists(const std::string& path) = 0;
-
-  // Returns the current state of the underlying device mapper device
-  // with given name.
-  // One of INVALID, SUSPENDED or ACTIVE.
-  virtual android::dm::DmDeviceState GetState(const std::string& name) = 0;
-
-  // Returns the path to the device mapper device node in '/dev' corresponding
-  // to 'name'. If the device does not exist, false is returned, and the path
-  // parameter is not set.
-  virtual bool GetDmDevicePathByName(const std::string& name,
-                                     std::string* path) = 0;
-
-  // Retrieve metadata from |super_device| at slot |source_slot|.
-  virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
-      const std::string& super_device, uint32_t source_slot) = 0;
-
   // Prepare all partitions for an update specified in |manifest|.
   // This is needed before calling MapPartitionOnDeviceMapper(), otherwise the
   // device would be mapped in an inconsistent way.
@@ -99,13 +62,6 @@
                                           const DeltaArchiveManifest& manifest,
                                           bool update) = 0;
 
-  // Return a possible location for devices listed by name.
-  virtual bool GetDeviceDir(std::string* path) = 0;
-
-  // Return the name of the super partition (which stores super partition
-  // metadata) for a given slot.
-  virtual std::string GetSuperPartitionName(uint32_t slot) = 0;
-
   virtual bool FinishUpdate() = 0;
 };
 
diff --git a/mock_dynamic_partition_control.h b/mock_dynamic_partition_control.h
index 8146e0f..67b3998 100644
--- a/mock_dynamic_partition_control.h
+++ b/mock_dynamic_partition_control.h
@@ -36,17 +36,9 @@
                     bool,
                     std::string*));
   MOCK_METHOD0(Cleanup, void());
-  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,
-               std::unique_ptr<::android::fs_mgr::MetadataBuilder>(
-                   const std::string&, uint32_t));
-  MOCK_METHOD1(GetDeviceDir, bool(std::string*));
   MOCK_METHOD0(GetDynamicPartitionsFeatureFlag, FeatureFlag());
   MOCK_METHOD4(PreparePartitionsForUpdate,
                bool(uint32_t, uint32_t, const DeltaArchiveManifest&, bool));
-  MOCK_METHOD1(GetSuperPartitionName, std::string(uint32_t));
   MOCK_METHOD0(GetVirtualAbFeatureFlag, FeatureFlag());
   MOCK_METHOD0(FinishUpdate, bool());
 };