update_engine resize dynamic partitions during OTA.

update_engine uses device mapper to resize dynamic partitions
before opening the devices to apply the update.

* DeltaPerformer calls BootControlInterface::InitPartitionMetadata
  when parsing the update manifest. The implementation for
  BootControlAndroid::InitPartitionMetadata does the following
  if sizes for dynamic partitions are incorrect (assuming updating
  from slot A to B):
  * Load metadata from metadata slot A
  * Delete all extents of partitions at slot B (with _b suffix)
  * Add extents for partitions at slot B
  * Write metadata to metadata slot B
  * Re-map all partitions at slot B using metadata slot B with
    force_writable = true
* BootControlAndroid::GetPartitionDevice() checks device-mapper
  before returning static partitions.
* PostinstallRunnerAction::Cleanup calls BootControlInterface::Cleanup
  which unmaps all partitions at slot B.

A partition "foo" is considered dynamic if foo_a exists as a dynamic
partition OR foo_b does NOT exist as a static partition.

Bug: 110717529

Test: manual ota
Test: update_engine_unittests --gtest_filter=*BootControlAndroid*
Change-Id: I50f410b486a874242663624801c3694151bdda18
diff --git a/boot_control_android_unittest.cc b/boot_control_android_unittest.cc
new file mode 100644
index 0000000..9744b42
--- /dev/null
+++ b/boot_control_android_unittest.cc
@@ -0,0 +1,670 @@
+//
+// 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 <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/mock_boot_control_hal.h"
+#include "update_engine/mock_dynamic_partition_control.h"
+
+using android::base::Join;
+using android::fs_mgr::MetadataBuilder;
+using android::hardware::Void;
+using testing::_;
+using testing::AnyNumber;
+using testing::Contains;
+using testing::Eq;
+using testing::Invoke;
+using testing::Key;
+using testing::MakeMatcher;
+using testing::Matcher;
+using testing::MatcherInterface;
+using testing::MatchResultListener;
+using testing::NiceMock;
+using testing::Return;
+
+namespace chromeos_update_engine {
+
+constexpr const uint32_t kMaxNumSlots = 2;
+constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"};
+constexpr const char* kFakeDevicePath = "/fake/dev/path/";
+constexpr const char* kFakeMappedPath = "/fake/mapped/path/";
+constexpr const uint32_t kFakeMetadataSize = 65536;
+constexpr const char* kZeroGuid = "00000000-0000-0000-0000-000000000000";
+
+// A map describing the size of each partition.
+using PartitionSizes = std::map<std::string, uint64_t>;
+
+// C++ standards do not allow uint64_t (aka unsigned long) to be the parameter
+// of user-defined literal operators.
+unsigned long long operator"" _MiB(unsigned long long x) {  // NOLINT
+  return x << 20;
+}
+unsigned long long operator"" _GiB(unsigned long long x) {  // NOLINT
+  return x << 30;
+}
+
+template <typename U, typename V>
+std::ostream& operator<<(std::ostream& os, const std::map<U, V>& param) {
+  os << "{";
+  bool first = true;
+  for (const auto& pair : param) {
+    if (!first)
+      os << ", ";
+    os << pair.first << ":" << pair.second;
+    first = false;
+  }
+  return os << "}";
+}
+
+inline std::string GetDevice(const std::string& name) {
+  return kFakeDevicePath + name;
+}
+inline std::string GetSuperDevice() {
+  return GetDevice(LP_METADATA_PARTITION_NAME);
+}
+
+struct TestParam {
+  uint32_t source;
+  uint32_t target;
+};
+std::ostream& operator<<(std::ostream& os, const TestParam& param) {
+  return os << "{source: " << param.source << ", target:" << param.target
+            << "}";
+}
+
+std::unique_ptr<MetadataBuilder> NewFakeMetadata(const PartitionSizes& sizes) {
+  auto builder = MetadataBuilder::New(10_GiB, kFakeMetadataSize, kMaxNumSlots);
+  EXPECT_NE(nullptr, builder);
+  if (builder == nullptr)
+    return nullptr;
+  for (const auto& pair : sizes) {
+    auto p = builder->AddPartition(pair.first, kZeroGuid, 0 /* attr */);
+    EXPECT_TRUE(p && builder->ResizePartition(p, pair.second));
+  }
+  return builder;
+}
+
+class MetadataMatcher : public MatcherInterface<MetadataBuilder*> {
+ public:
+  explicit MetadataMatcher(const PartitionSizes& partition_sizes)
+      : partition_sizes_(partition_sizes) {}
+  bool MatchAndExplain(MetadataBuilder* metadata,
+                       MatchResultListener* listener) const override {
+    bool success = true;
+    for (const auto& pair : partition_sizes_) {
+      auto p = metadata->FindPartition(pair.first);
+      if (p == nullptr) {
+        if (success)
+          *listener << "; ";
+        *listener << "No partition " << pair.first;
+        success = false;
+        continue;
+      }
+      if (p->size() != pair.second) {
+        if (success)
+          *listener << "; ";
+        *listener << "Partition " << pair.first << " has size " << p->size()
+                  << ", expected " << pair.second;
+        success = false;
+      }
+    }
+    return success;
+  }
+
+  void DescribeTo(std::ostream* os) const override {
+    *os << "expect: " << partition_sizes_;
+  }
+
+  void DescribeNegationTo(std::ostream* os) const override {
+    *os << "expect not: " << partition_sizes_;
+  }
+
+ private:
+  PartitionSizes partition_sizes_;
+};
+
+inline Matcher<MetadataBuilder*> MetadataMatches(
+    const PartitionSizes& partition_sizes) {
+  return MakeMatcher(new MetadataMatcher(partition_sizes));
+}
+
+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(), IsDynamicPartitionsEnabled())
+        .WillByDefault(Return(true));
+    ON_CALL(dynamicControl(), GetDeviceDir(_))
+        .WillByDefault(Invoke([](auto path) {
+          *path = kFakeDevicePath;
+          return true;
+        }));
+  }
+
+  // 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 PartitionSizes& sizes) {
+    EXPECT_CALL(dynamicControl(), LoadMetadataBuilder(GetSuperDevice(), slot))
+        .WillOnce(
+            Invoke([sizes](auto, auto) { return NewFakeMetadata(sizes); }));
+  }
+
+  // Expect that MapPartitionOnDeviceMapper is called on target() metadata slot
+  // with each partition in |partitions|.
+  void ExpectMap(const std::set<std::string>& partitions) {
+    // Error when MapPartitionOnDeviceMapper is called on unknown arguments.
+    ON_CALL(dynamicControl(), MapPartitionOnDeviceMapper(_, _, _, _))
+        .WillByDefault(Return(false));
+
+    for (const auto& partition : partitions) {
+      EXPECT_CALL(
+          dynamicControl(),
+          MapPartitionOnDeviceMapper(GetSuperDevice(), partition, target(), _))
+          .WillOnce(Invoke([this](auto, auto partition, auto, auto path) {
+            auto it = mapped_devices_.find(partition);
+            if (it != mapped_devices_.end()) {
+              *path = it->second;
+              return true;
+            }
+            mapped_devices_[partition] = *path = kFakeMappedPath + partition;
+            return true;
+          }));
+    }
+  }
+
+  // Expect that UnmapPartitionOnDeviceMapper is called on target() metadata
+  // slot with each partition in |partitions|.
+  void ExpectUnmap(const std::set<std::string>& partitions) {
+    // Error when UnmapPartitionOnDeviceMapper is called on unknown arguments.
+    ON_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_, _))
+        .WillByDefault(Return(false));
+
+    for (const auto& partition : partitions) {
+      EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(partition, _))
+          .WillOnce(Invoke([this](auto partition, auto) {
+            mapped_devices_.erase(partition);
+            return true;
+          }));
+    }
+  }
+
+  void ExpectRemap(const std::set<std::string>& partitions) {
+    ExpectUnmap(partitions);
+    ExpectMap(partitions);
+  }
+
+  void ExpectDevicesAreMapped(const std::set<std::string>& partitions) {
+    ASSERT_EQ(partitions.size(), mapped_devices_.size());
+    for (const auto& partition : partitions) {
+      EXPECT_THAT(mapped_devices_, Contains(Key(Eq(partition))))
+          << "Expect that " << partition << " is mapped, but it is not.";
+    }
+  }
+
+  uint32_t source() { return slots_.source; }
+
+  uint32_t target() { return slots_.target; }
+
+  // Return partition names with suffix of source().
+  std::string S(const std::string& name) {
+    return name + std::string(kSlotSuffixes[source()]);
+  }
+
+  // Return partition names with suffix of target().
+  std::string T(const std::string& name) {
+    return name + std::string(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();
+    }));
+    // Should not store metadata to source slot.
+    EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, source()))
+        .Times(0);
+  }
+
+  BootControlAndroid bootctl_;  // BootControlAndroid under test.
+  TestParam slots_;
+  // mapped devices through MapPartitionOnDeviceMapper.
+  std::map<std::string, std::string> mapped_devices_;
+};
+
+class BootControlAndroidTestP
+    : public BootControlAndroidTest,
+      public ::testing::WithParamInterface<TestParam> {
+ public:
+  void SetUp() override {
+    BootControlAndroidTest::SetUp();
+    SetSlots(GetParam());
+  }
+};
+
+// Test no resize if no dynamic partitions at all.
+TEST_P(BootControlAndroidTestP, NoResizeIfNoDynamicPartitions) {
+  SetMetadata(source(), {});
+  SetMetadata(target(), {});
+  // Should not need to resize and store metadata
+  EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, target()))
+      .Times(0);
+  EXPECT_CALL(dynamicControl(), DeviceExists(Eq(GetDevice("static_a"))))
+      .Times(AnyNumber())
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(dynamicControl(), DeviceExists(Eq(GetDevice("static_b"))))
+      .Times(AnyNumber())
+      .WillRepeatedly(Return(true));
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(target(), {{"static", 1_GiB}}));
+  ExpectDevicesAreMapped({});
+}
+
+// Test no resize if update manifest does not contain any dynamic partitions
+TEST_P(BootControlAndroidTestP, NoResizeIfEmptyMetadata) {
+  SetMetadata(source(),
+              {{S("system"), 4_GiB},
+               {S("vendor"), 100_MiB},
+               {T("system"), 3_GiB},
+               {T("vendor"), 150_MiB}});
+  SetMetadata(target(),
+              {{S("system"), 2_GiB},
+               {S("vendor"), 1_GiB},
+               {T("system"), 3_GiB},
+               {T("vendor"), 150_MiB}});
+  // Should not need to resize and store metadata
+  EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, target()))
+      .Times(0);
+  EXPECT_CALL(dynamicControl(), DeviceExists(Eq(GetDevice("static_a"))))
+      .Times(AnyNumber())
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(dynamicControl(), DeviceExists(Eq(GetDevice("static_b"))))
+      .Times(AnyNumber())
+      .WillRepeatedly(Return(true));
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(target(), {{"static", 1_GiB}}));
+  ExpectDevicesAreMapped({});
+}
+
+// Do not resize if manifest size matches size in target metadata. When resuming
+// from an update, do not redo the resize if not needed.
+TEST_P(BootControlAndroidTestP, NoResizeIfSizeMatchWhenResizing) {
+  SetMetadata(source(), {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}});
+  SetMetadata(target(),
+              {{S("system"), 2_GiB},
+               {S("vendor"), 1_GiB},
+               {T("system"), 3_GiB},
+               {T("vendor"), 1_GiB}});
+  // Should not need to resize and store metadata
+  EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, target()))
+      .Times(0);
+  ExpectRemap({T("system"), T("vendor")});
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
+      target(), {{"system", 3_GiB}, {"vendor", 1_GiB}}));
+  ExpectDevicesAreMapped({T("system"), T("vendor")});
+}
+
+// Do not resize if manifest size matches size in target metadata. When resuming
+// from an update, do not redo the resize if not needed.
+TEST_P(BootControlAndroidTestP, NoResizeIfSizeMatchWhenAdding) {
+  SetMetadata(source(), {{S("system"), 2_GiB}, {T("system"), 2_GiB}});
+  SetMetadata(
+      target(),
+      {{S("system"), 2_GiB}, {T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
+  // Should not need to resize and store metadata
+  EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, target()))
+      .Times(0);
+  ExpectRemap({T("system"), T("vendor")});
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
+      target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}));
+  ExpectDevicesAreMapped({T("system"), T("vendor")});
+}
+
+// Do not resize if manifest size matches size in target metadata. When resuming
+// from an update, do not redo the resize if not needed.
+TEST_P(BootControlAndroidTestP, NoResizeIfSizeMatchWhenDeleting) {
+  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"), 0}});
+  // Should not need to resize and store metadata
+  EXPECT_CALL(dynamicControl(), StoreMetadata(GetSuperDevice(), _, target()))
+      .Times(0);
+  ExpectUnmap({T("system"), T("vendor")});
+  ExpectMap({T("system")});
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
+      target(), {{"system", 2_GiB}, {"vendor", 0}}));
+  ExpectDevicesAreMapped({T("system")});
+}
+
+// Test resize case. Grow if target metadata contains a partition with a size
+// less than expected.
+TEST_P(BootControlAndroidTestP, NeedGrowIfSizeNotMatchWhenResizing) {
+  PartitionSizes initial{{S("system"), 2_GiB},
+                         {S("vendor"), 1_GiB},
+                         {T("system"), 2_GiB},
+                         {T("vendor"), 1_GiB}};
+  SetMetadata(source(), initial);
+  SetMetadata(target(), initial);
+  EXPECT_CALL(dynamicControl(),
+              StoreMetadata(GetSuperDevice(),
+                            MetadataMatches({{S("system"), 2_GiB},
+                                             {S("vendor"), 1_GiB},
+                                             {T("system"), 3_GiB},
+                                             {T("vendor"), 1_GiB}}),
+                            target()))
+      .WillOnce(Return(true));
+  ExpectRemap({T("system"), T("vendor")});
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
+      target(), {{"system", 3_GiB}, {"vendor", 1_GiB}}));
+  ExpectDevicesAreMapped({T("system"), T("vendor")});
+}
+
+// Test resize case. Shrink if target metadata contains a partition with a size
+// greater than expected.
+TEST_P(BootControlAndroidTestP, NeedShrinkIfSizeNotMatchWhenResizing) {
+  PartitionSizes initial{{S("system"), 2_GiB},
+                         {S("vendor"), 1_GiB},
+                         {T("system"), 2_GiB},
+                         {T("vendor"), 1_GiB}};
+  SetMetadata(source(), initial);
+  SetMetadata(target(), initial);
+  EXPECT_CALL(dynamicControl(),
+              StoreMetadata(GetSuperDevice(),
+                            MetadataMatches({{S("system"), 2_GiB},
+                                             {S("vendor"), 1_GiB},
+                                             {T("system"), 2_GiB},
+                                             {T("vendor"), 150_MiB}}),
+                            target()))
+      .WillOnce(Return(true));
+  ExpectRemap({T("system"), T("vendor")});
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
+      target(), {{"system", 2_GiB}, {"vendor", 150_MiB}}));
+  ExpectDevicesAreMapped({T("system"), T("vendor")});
+}
+
+// Test adding partitions on the first run.
+TEST_P(BootControlAndroidTestP, AddPartitionToEmptyMetadata) {
+  SetMetadata(source(), {});
+  SetMetadata(target(), {});
+  EXPECT_CALL(dynamicControl(),
+              StoreMetadata(
+                  GetSuperDevice(),
+                  MetadataMatches({{T("system"), 2_GiB}, {T("vendor"), 1_GiB}}),
+                  target()))
+      .WillOnce(Return(true));
+  ExpectRemap({T("system"), T("vendor")});
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
+      target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}));
+  ExpectDevicesAreMapped({T("system"), T("vendor")});
+}
+
+// Test subsequent add case.
+TEST_P(BootControlAndroidTestP, AddAdditionalPartition) {
+  SetMetadata(source(), {{S("system"), 2_GiB}, {T("system"), 2_GiB}});
+  SetMetadata(target(), {{S("system"), 2_GiB}, {T("system"), 2_GiB}});
+  EXPECT_CALL(dynamicControl(),
+              StoreMetadata(GetSuperDevice(),
+                            MetadataMatches({{S("system"), 2_GiB},
+                                             {T("system"), 2_GiB},
+                                             {T("vendor"), 1_GiB}}),
+                            target()))
+      .WillOnce(Return(true));
+  ExpectRemap({T("system"), T("vendor")});
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
+      target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}));
+  ExpectDevicesAreMapped({T("system"), T("vendor")});
+}
+
+// Test delete one partition.
+TEST_P(BootControlAndroidTestP, DeletePartition) {
+  PartitionSizes initial{{S("system"), 2_GiB},
+                         {S("vendor"), 1_GiB},
+                         {T("system"), 2_GiB},
+                         {T("vendor"), 1_GiB}};
+  SetMetadata(source(), initial);
+  SetMetadata(target(), initial);
+  EXPECT_CALL(dynamicControl(),
+              StoreMetadata(GetSuperDevice(),
+                            MetadataMatches({{S("system"), 2_GiB},
+                                             {S("vendor"), 1_GiB},
+                                             {T("system"), 2_GiB},
+                                             {T("vendor"), 0}}),
+                            target()))
+      .WillOnce(Return(true));
+  ExpectUnmap({T("system"), T("vendor")});
+  ExpectMap({T("system")});
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
+      target(), {{"system", 2_GiB}, {"vendor", 0}}));
+  ExpectDevicesAreMapped({T("system")});
+}
+
+// Test delete all partitions.
+TEST_P(BootControlAndroidTestP, DeleteAll) {
+  PartitionSizes initial{{S("system"), 2_GiB},
+                         {S("vendor"), 1_GiB},
+                         {T("system"), 2_GiB},
+                         {T("vendor"), 1_GiB}};
+  SetMetadata(source(), initial);
+  SetMetadata(target(), initial);
+  EXPECT_CALL(dynamicControl(),
+              StoreMetadata(GetSuperDevice(),
+                            MetadataMatches({{S("system"), 2_GiB},
+                                             {S("vendor"), 1_GiB},
+                                             {T("system"), 0},
+                                             {T("vendor"), 0}}),
+                            target()))
+      .WillOnce(Return(true));
+  ExpectUnmap({T("system"), T("vendor")});
+  ExpectMap({});
+
+  EXPECT_TRUE(
+      bootctl_.InitPartitionMetadata(target(), {{"system", 0}, {"vendor", 0}}));
+  ExpectDevicesAreMapped({});
+}
+
+// Test corrupt source metadata case. This shouldn't happen in practice,
+// because the device is already booted normally.
+TEST_P(BootControlAndroidTestP, CorruptedSourceMetadata) {
+  EXPECT_CALL(dynamicControl(), LoadMetadataBuilder(GetSuperDevice(), source()))
+      .WillOnce(Invoke([](auto, auto) { return nullptr; }));
+  EXPECT_FALSE(bootctl_.InitPartitionMetadata(target(), {}))
+      << "Should not be able to continue with corrupt source metadata";
+}
+
+// Test corrupt target metadata case. This may happen in practice.
+// BootControlAndroid should copy from source metadata and make necessary
+// modifications on it.
+TEST_P(BootControlAndroidTestP, CorruptedTargetMetadata) {
+  SetMetadata(source(),
+              {{S("system"), 2_GiB},
+               {S("vendor"), 1_GiB},
+               {T("system"), 0},
+               {T("vendor"), 0}});
+  EXPECT_CALL(dynamicControl(), LoadMetadataBuilder(GetSuperDevice(), target()))
+      .WillOnce(Invoke([](auto, auto) { return nullptr; }));
+  EXPECT_CALL(dynamicControl(),
+              StoreMetadata(GetSuperDevice(),
+                            MetadataMatches({{S("system"), 2_GiB},
+                                             {S("vendor"), 1_GiB},
+                                             {T("system"), 3_GiB},
+                                             {T("vendor"), 150_MiB}}),
+                            target()))
+      .WillOnce(Return(true));
+  ExpectRemap({T("system"), T("vendor")});
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(
+      target(), {{"system", 3_GiB}, {"vendor", 150_MiB}}));
+  ExpectDevicesAreMapped({T("system"), T("vendor")});
+}
+
+// Test that InitPartitionMetadata fail if there is not enough space on the
+// device.
+TEST_P(BootControlAndroidTestP, NotEnoughSpace) {
+  PartitionSizes initial{{S("system"), 3_GiB},
+                         {S("vendor"), 2_GiB},
+                         {T("system"), 0},
+                         {T("vendor"), 0}};
+  SetMetadata(source(), initial);
+  SetMetadata(target(), initial);
+  EXPECT_FALSE(bootctl_.InitPartitionMetadata(
+      target(), {{"system", 3_GiB}, {"vendor", 3_GiB}}))
+      << "Should not be able to fit 11GiB data into 10GiB space";
+}
+
+INSTANTIATE_TEST_CASE_P(ParamTest,
+                        BootControlAndroidTestP,
+                        testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
+const PartitionSizes update_sizes_0() {
+  return {{"grown_a", 2_GiB},
+          {"shrunk_a", 1_GiB},
+          {"same_a", 100_MiB},
+          {"deleted_a", 150_MiB},
+          {"grown_b", 200_MiB},
+          {"shrunk_b", 0},
+          {"same_b", 0}};
+}
+
+const PartitionSizes update_sizes_1() {
+  return {
+      {"grown_a", 2_GiB},
+      {"shrunk_a", 1_GiB},
+      {"same_a", 100_MiB},
+      {"deleted_a", 150_MiB},
+      {"grown_b", 3_GiB},
+      {"shrunk_b", 150_MiB},
+      {"same_b", 100_MiB},
+      {"added_b", 150_MiB},
+      {"deleted_b", 0},
+  };
+}
+
+const PartitionSizes update_sizes_2() {
+  return {{"grown_a", 4_GiB},
+          {"shrunk_a", 100_MiB},
+          {"same_a", 100_MiB},
+          {"added_a", 0_MiB},
+          {"deleted_a", 64_MiB},
+          {"grown_b", 3_GiB},
+          {"shrunk_b", 150_MiB},
+          {"same_b", 100_MiB},
+          {"added_b", 150_MiB},
+          {"deleted_b", 0}};
+}
+
+// Test case for first update after the device is manufactured, in which
+// case the "other" slot is likely of size "0" (except system, which is
+// non-zero because of system_other partition)
+TEST_F(BootControlAndroidTest, SimulatedFirstUpdate) {
+  SetSlots({0, 1});
+
+  SetMetadata(source(), update_sizes_0());
+  SetMetadata(target(), update_sizes_0());
+  EXPECT_CALL(
+      dynamicControl(),
+      StoreMetadata(
+          GetSuperDevice(), MetadataMatches(update_sizes_1()), target()))
+      .WillOnce(Return(true));
+  ExpectUnmap({"grown_b", "shrunk_b", "same_b", "added_b", "deleted_b"});
+  ExpectMap({"grown_b", "shrunk_b", "same_b", "added_b"});
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(target(),
+                                             {{"grown", 3_GiB},
+                                              {"shrunk", 150_MiB},
+                                              {"same", 100_MiB},
+                                              {"added", 150_MiB},
+                                              {"deleted", 0_MiB}}));
+  ExpectDevicesAreMapped({"grown_b", "shrunk_b", "same_b", "added_b"});
+}
+
+// After first update, test for the second update. In the second update, the
+// "added" partition is deleted and "deleted" partition is re-added.
+TEST_F(BootControlAndroidTest, SimulatedSecondUpdate) {
+  SetSlots({1, 0});
+
+  SetMetadata(source(), update_sizes_1());
+  SetMetadata(target(), update_sizes_0());
+
+  EXPECT_CALL(
+      dynamicControl(),
+      StoreMetadata(
+          GetSuperDevice(), MetadataMatches(update_sizes_2()), target()))
+      .WillOnce(Return(true));
+  ExpectUnmap({"grown_a", "shrunk_a", "same_a", "added_a", "deleted_a"});
+  ExpectMap({"grown_a", "shrunk_a", "same_a", "deleted_a"});
+
+  EXPECT_TRUE(bootctl_.InitPartitionMetadata(target(),
+                                             {{"grown", 4_GiB},
+                                              {"shrunk", 100_MiB},
+                                              {"same", 100_MiB},
+                                              {"added", 0_MiB},
+                                              {"deleted", 64_MiB}}));
+  ExpectDevicesAreMapped({"grown_a", "shrunk_a", "same_a", "deleted_a"});
+}
+
+TEST_F(BootControlAndroidTest, ApplyingToCurrentSlot) {
+  SetSlots({1, 1});
+  EXPECT_FALSE(bootctl_.InitPartitionMetadata(target(), {}))
+      << "Should not be able to apply to current slot.";
+}
+
+}  // namespace chromeos_update_engine