Proper split of BootControl and DynamicPartitionControl.
am: 3a1a561839
Change-Id: I3caeb55d696c2d60cdcffd0a35bb851a67e6d9e7
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());
};