[REFACTOR] DynamicPartitionControl: minimize API
am: 012508efa3

Change-Id: Ida2cbb9a32a132b136ff868f7c3b38f588f5ef90
diff --git a/Android.bp b/Android.bp
index 1d146a5..29e6659 100644
--- a/Android.bp
+++ b/Android.bp
@@ -214,6 +214,7 @@
     srcs: [
         "boot_control_android.cc",
         "dynamic_partition_control_android.cc",
+        "dynamic_partition_utils.cc",
     ],
 }
 
diff --git a/boot_control_android.cc b/boot_control_android.cc
index 8ab73be..b820ded 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -22,7 +22,6 @@
 
 #include <base/bind.h>
 #include <base/logging.h>
-#include <base/strings/string_util.h>
 #include <bootloader_message/bootloader_message.h>
 #include <brillo/message_loops/message_loop.h>
 #include <fs_mgr.h>
@@ -34,7 +33,6 @@
 using std::string;
 
 using android::dm::DmDeviceState;
-using android::fs_mgr::Partition;
 using android::hardware::hidl_string;
 using android::hardware::Return;
 using android::hardware::boot::V1_0::BoolResult;
@@ -112,8 +110,8 @@
     const string& partition_name_suffix) const {
   string source_device =
       device_dir.Append(fs_mgr_get_super_partition_name(slot)).value();
-  auto source_metadata = dynamic_control_->LoadMetadataBuilder(
-      source_device, slot, BootControlInterface::kInvalidSlot);
+  auto source_metadata =
+      dynamic_control_->LoadMetadataBuilder(source_device, slot);
   return source_metadata->HasBlockDevice(partition_name_suffix);
 }
 
@@ -126,8 +124,7 @@
   string super_device =
       device_dir.Append(fs_mgr_get_super_partition_name(slot)).value();
 
-  auto builder = dynamic_control_->LoadMetadataBuilder(
-      super_device, slot, BootControlInterface::kInvalidSlot);
+  auto builder = dynamic_control_->LoadMetadataBuilder(super_device, slot);
 
   if (builder == nullptr) {
     LOG(ERROR) << "No metadata in slot "
@@ -280,110 +277,6 @@
          brillo::MessageLoop::kTaskIdNull;
 }
 
-namespace {
-
-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) {
-    // TODO(elsk): allow reconstructing metadata from partition_metadata
-    // in recovery sideload.
-    LOG(ERROR) << "No metadata at "
-               << BootControlInterface::SlotName(source_slot);
-    return false;
-  }
-
-  std::vector<string> groups = builder->ListGroups();
-  for (const auto& group_name : groups) {
-    if (base::EndsWith(
-            group_name, target_suffix, base::CompareCase::SENSITIVE)) {
-      LOG(INFO) << "Removing group " << group_name;
-      builder->RemoveGroupAndPartitions(group_name);
-    }
-  }
-
-  uint64_t total_size = 0;
-  for (const auto& group : partition_metadata.groups) {
-    total_size += group.size;
-  }
-
-  string expr;
-  uint64_t allocatable_space = builder->AllocatableSpace();
-  if (!dynamic_control->IsDynamicPartitionsRetrofit()) {
-    allocatable_space /= 2;
-    expr = "half of ";
-  }
-  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 << ".";
-    return false;
-  }
-
-  for (const auto& group : partition_metadata.groups) {
-    auto group_name_suffix = group.name + target_suffix;
-    if (!builder->AddGroup(group_name_suffix, group.size)) {
-      LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size "
-                 << group.size;
-      return false;
-    }
-    LOG(INFO) << "Added group " << group_name_suffix << " with size "
-              << group.size;
-
-    for (const auto& partition : group.partitions) {
-      auto partition_name_suffix = partition.name + target_suffix;
-      Partition* p = builder->AddPartition(
-          partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY);
-      if (!p) {
-        LOG(ERROR) << "Cannot add partition " << partition_name_suffix
-                   << " to group " << group_name_suffix;
-        return false;
-      }
-      if (!builder->ResizePartition(p, partition.size)) {
-        LOG(ERROR) << "Cannot resize partition " << partition_name_suffix
-                   << " to size " << partition.size << ". Not enough space?";
-        return false;
-      }
-      LOG(INFO) << "Added partition " << partition_name_suffix << " to group "
-                << group_name_suffix << " with size " << partition.size;
-    }
-  }
-
-  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);
-}
-
-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)) {
-        return false;
-      }
-    }
-  }
-  return true;
-}
-
-}  // namespace
-
 bool BootControlAndroid::InitPartitionMetadata(
     Slot target_slot,
     const PartitionMetadata& partition_metadata,
@@ -417,23 +310,8 @@
     return true;
   }
 
-  string target_suffix;
-  if (!GetSuffix(target_slot, &target_suffix)) {
-    return false;
-  }
-
-  // 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;
-  }
-
-  return UpdatePartitionMetadata(dynamic_control_.get(),
-                                 source_slot,
-                                 target_slot,
-                                 target_suffix,
-                                 partition_metadata);
+  return dynamic_control_->PreparePartitionsForUpdate(
+      source_slot, target_slot, partition_metadata);
 }
 
 }  // namespace chromeos_update_engine
diff --git a/boot_control_android_unittest.cc b/boot_control_android_unittest.cc
index 94e195f..dfcb6fb 100644
--- a/boot_control_android_unittest.cc
+++ b/boot_control_android_unittest.cc
@@ -262,7 +262,7 @@
     // Fake init bootctl_
     bootctl_.module_ = new NiceMock<MockBootControlHal>();
     bootctl_.dynamic_control_ =
-        std::make_unique<NiceMock<MockDynamicPartitionControl>>();
+        std::make_unique<NiceMock<MockDynamicPartitionControlAndroid>>();
 
     ON_CALL(module(), getNumberSlots()).WillByDefault(Invoke([] {
       return kMaxNumSlots;
@@ -297,8 +297,8 @@
   }
 
   // Return the mocked DynamicPartitionControlInterface.
-  NiceMock<MockDynamicPartitionControl>& dynamicControl() {
-    return static_cast<NiceMock<MockDynamicPartitionControl>&>(
+  NiceMock<MockDynamicPartitionControlAndroid>& dynamicControl() {
+    return static_cast<NiceMock<MockDynamicPartitionControlAndroid>&>(
         *bootctl_.dynamic_control_);
   }
 
diff --git a/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc
index b5b22df..5a172b0 100644
--- a/dynamic_partition_control_android.cc
+++ b/dynamic_partition_control_android.cc
@@ -19,16 +19,20 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <base/files/file_util.h>
 #include <base/logging.h>
+#include <base/strings/string_util.h>
 #include <bootloader_message/bootloader_message.h>
+#include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 
 #include "update_engine/common/boot_control_interface.h"
 #include "update_engine/common/utils.h"
+#include "update_engine/dynamic_partition_utils.h"
 
 using android::base::GetBoolProperty;
 using android::base::Join;
@@ -37,10 +41,14 @@
 using android::fs_mgr::CreateLogicalPartition;
 using android::fs_mgr::DestroyLogicalPartition;
 using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::Partition;
 using android::fs_mgr::PartitionOpener;
+using android::fs_mgr::SlotSuffixForSlotNumber;
 
 namespace chromeos_update_engine {
 
+using PartitionMetadata = BootControlInterface::PartitionMetadata;
+
 constexpr char kUseDynamicPartitions[] = "ro.boot.dynamic_partitions";
 constexpr char kRetrfoitDynamicPartitions[] =
     "ro.boot.dynamic_partitions_retrofit";
@@ -172,6 +180,13 @@
 
 std::unique_ptr<MetadataBuilder>
 DynamicPartitionControlAndroid::LoadMetadataBuilder(
+    const std::string& super_device, uint32_t source_slot) {
+  return LoadMetadataBuilder(
+      super_device, source_slot, BootControlInterface::kInvalidSlot);
+}
+
+std::unique_ptr<MetadataBuilder>
+DynamicPartitionControlAndroid::LoadMetadataBuilder(
     const std::string& super_device,
     uint32_t source_slot,
     uint32_t target_slot) {
@@ -257,4 +272,104 @@
   *out = base::FilePath(misc_device).DirName().value();
   return true;
 }
+
+bool DynamicPartitionControlAndroid::PreparePartitionsForUpdate(
+    uint32_t source_slot,
+    uint32_t target_slot,
+    const PartitionMetadata& partition_metadata) {
+  const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
+
+  // Unmap all the target dynamic partitions because they would become
+  // inconsistent with the new metadata.
+  for (const auto& group : partition_metadata.groups) {
+    for (const auto& partition : group.partitions) {
+      if (!UnmapPartitionOnDeviceMapper(partition.name + target_suffix)) {
+        return false;
+      }
+    }
+  }
+
+  std::string device_dir_str;
+  if (!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 = LoadMetadataBuilder(source_device, source_slot, target_slot);
+  if (builder == nullptr) {
+    LOG(ERROR) << "No metadata at "
+               << BootControlInterface::SlotName(source_slot);
+    return false;
+  }
+
+  if (!UpdatePartitionMetadata(
+          builder.get(), target_slot, partition_metadata)) {
+    return false;
+  }
+
+  auto target_device =
+      device_dir.Append(fs_mgr_get_super_partition_name(target_slot)).value();
+  return StoreMetadata(target_device, builder.get(), target_slot);
+}
+
+bool DynamicPartitionControlAndroid::UpdatePartitionMetadata(
+    MetadataBuilder* builder,
+    uint32_t target_slot,
+    const PartitionMetadata& partition_metadata) {
+  const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
+  DeleteGroupsWithSuffix(builder, target_suffix);
+
+  uint64_t total_size = 0;
+  for (const auto& group : partition_metadata.groups) {
+    total_size += group.size;
+  }
+
+  std::string expr;
+  uint64_t allocatable_space = builder->AllocatableSpace();
+  if (!IsDynamicPartitionsRetrofit()) {
+    allocatable_space /= 2;
+    expr = "half of ";
+  }
+  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 << ".";
+    return false;
+  }
+
+  for (const auto& group : partition_metadata.groups) {
+    auto group_name_suffix = group.name + target_suffix;
+    if (!builder->AddGroup(group_name_suffix, group.size)) {
+      LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size "
+                 << group.size;
+      return false;
+    }
+    LOG(INFO) << "Added group " << group_name_suffix << " with size "
+              << group.size;
+
+    for (const auto& partition : group.partitions) {
+      auto partition_name_suffix = partition.name + target_suffix;
+      Partition* p = builder->AddPartition(
+          partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY);
+      if (!p) {
+        LOG(ERROR) << "Cannot add partition " << partition_name_suffix
+                   << " to group " << group_name_suffix;
+        return false;
+      }
+      if (!builder->ResizePartition(p, partition.size)) {
+        LOG(ERROR) << "Cannot resize partition " << partition_name_suffix
+                   << " to size " << partition.size << ". Not enough space?";
+        return false;
+      }
+      LOG(INFO) << "Added partition " << partition_name_suffix << " to group "
+                << group_name_suffix << " with size " << partition.size;
+    }
+  }
+
+  return true;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/dynamic_partition_control_android.h b/dynamic_partition_control_android.h
index 334f9bd..e0859ed 100644
--- a/dynamic_partition_control_android.h
+++ b/dynamic_partition_control_android.h
@@ -36,21 +36,48 @@
                                   uint32_t slot,
                                   bool force_writable,
                                   std::string* path) override;
-  bool UnmapPartitionOnDeviceMapper(
-      const std::string& target_partition_name) 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 BootControlInterface::PartitionMetadata&
+                                      partition_metadata) override;
+  bool GetDeviceDir(std::string* path) override;
+
+ protected:
+  // These functions are exposed for testing.
+
+  // Unmap logical partition on device mapper. This is the reverse operation
+  // of MapPartitionOnDeviceMapper.
+  // Returns true if unmapped successfully.
+  virtual bool UnmapPartitionOnDeviceMapper(
+      const std::string& target_partition_name);
+
+  // Retrieve metadata from |super_device| at slot |source_slot|.
+  //
+  // If |target_slot| != kInvalidSlot, before returning the metadata, this
+  // function modifies the metadata so that during updates, the metadata can be
+  // written to |target_slot|. In particular, on retrofit devices, the returned
+  // metadata automatically includes block devices at |target_slot|.
+  //
+  // If |target_slot| == kInvalidSlot, this function returns metadata at
+  // |source_slot| without modifying it. This is the same as
+  // LoadMetadataBuilder(const std::string&, uint32_t).
+  virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
       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;
-  bool GetDeviceDir(std::string* path) override;
+      uint32_t target_slot);
+
+  // Write metadata |builder| to |super_device| at slot |target_slot|.
+  virtual bool StoreMetadata(const std::string& super_device,
+                             android::fs_mgr::MetadataBuilder* builder,
+                             uint32_t target_slot);
 
  private:
   std::set<std::string> mapped_devices_;
@@ -62,6 +89,13 @@
                             bool force_writable,
                             std::string* path);
 
+  // Update |builder| according to |partition_metadata|, assuming the device
+  // does not have Virtual A/B.
+  bool UpdatePartitionMetadata(
+      android::fs_mgr::MetadataBuilder* builder,
+      uint32_t target_slot,
+      const BootControlInterface::PartitionMetadata& partition_metadata);
+
   DISALLOW_COPY_AND_ASSIGN(DynamicPartitionControlAndroid);
 };
 
diff --git a/dynamic_partition_control_interface.h b/dynamic_partition_control_interface.h
index d4590f7..12e62e0 100644
--- a/dynamic_partition_control_interface.h
+++ b/dynamic_partition_control_interface.h
@@ -26,6 +26,8 @@
 #include <libdm/dm.h>
 #include <liblp/builder.h>
 
+#include "update_engine/common/boot_control_interface.h"
+
 namespace chromeos_update_engine {
 
 class DynamicPartitionControlInterface {
@@ -52,13 +54,6 @@
       bool force_writable,
       std::string* path) = 0;
 
-  // Unmap logical partition on device mapper. This is the reverse operation
-  // of MapPartitionOnDeviceMapper.
-  // If |wait| is set, wait until the device is unmapped.
-  // Returns true if unmapped successfully.
-  virtual bool UnmapPartitionOnDeviceMapper(
-      const std::string& target_partition_name) = 0;
-
   // Do necessary cleanups before destroying the object.
   virtual void Cleanup() = 0;
 
@@ -77,17 +72,16 @@
                                      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,
-      uint32_t target_slot) = 0;
+      const std::string& super_device, uint32_t source_slot) = 0;
 
-  // Write metadata |builder| to |super_device| at slot |target_slot|.
-  virtual bool StoreMetadata(const std::string& super_device,
-                             android::fs_mgr::MetadataBuilder* builder,
-                             uint32_t target_slot) = 0;
+  // Prepare all partitions for an update specified in |partition_metadata|.
+  // This is needed before calling MapPartitionOnDeviceMapper(), otherwise the
+  // device would be mapped in an inconsistent way.
+  virtual bool PreparePartitionsForUpdate(
+      uint32_t source_slot,
+      uint32_t target_slot,
+      const BootControlInterface::PartitionMetadata& partition_metadata) = 0;
 
   // Return a possible location for devices listed by name.
   virtual bool GetDeviceDir(std::string* path) = 0;
diff --git a/dynamic_partition_utils.cc b/dynamic_partition_utils.cc
new file mode 100644
index 0000000..f9bd886
--- /dev/null
+++ b/dynamic_partition_utils.cc
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2019 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/dynamic_partition_utils.h"
+
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+
+using android::fs_mgr::MetadataBuilder;
+
+namespace chromeos_update_engine {
+
+void DeleteGroupsWithSuffix(MetadataBuilder* builder,
+                            const std::string& suffix) {
+  std::vector<std::string> groups = builder->ListGroups();
+  for (const auto& group_name : groups) {
+    if (base::EndsWith(group_name, suffix, base::CompareCase::SENSITIVE)) {
+      LOG(INFO) << "Removing group " << group_name;
+      builder->RemoveGroupAndPartitions(group_name);
+    }
+  }
+}
+
+}  // namespace chromeos_update_engine
diff --git a/dynamic_partition_utils.h b/dynamic_partition_utils.h
new file mode 100644
index 0000000..09fce00
--- /dev/null
+++ b/dynamic_partition_utils.h
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2019 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.
+//
+
+#ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
+#define UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
+
+#include <string>
+
+#include <liblp/builder.h>
+
+namespace chromeos_update_engine {
+
+// Delete all groups (and their partitions) in |builder| that have names
+// ending with |suffix|.
+void DeleteGroupsWithSuffix(android::fs_mgr::MetadataBuilder* builder,
+                            const std::string& suffix);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
diff --git a/mock_dynamic_partition_control.h b/mock_dynamic_partition_control.h
index cdfeecc..310e528 100644
--- a/mock_dynamic_partition_control.h
+++ b/mock_dynamic_partition_control.h
@@ -21,11 +21,14 @@
 
 #include <gmock/gmock.h>
 
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/dynamic_partition_control_android.h"
 #include "update_engine/dynamic_partition_control_interface.h"
 
 namespace chromeos_update_engine {
 
-class MockDynamicPartitionControl : public DynamicPartitionControlInterface {
+class MockDynamicPartitionControlAndroid
+    : public DynamicPartitionControlAndroid {
  public:
   MOCK_METHOD5(MapPartitionOnDeviceMapper,
                bool(const std::string&,