[automerger skipped] Check for system_other existence in recovery mode as well. am: ddf27738af -s ours
am skip reason: Change-Id I1884755aad2a8d37f540dbee73c7c7baab2759e7 with SHA-1 13e4195d65 is in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/update_engine/+/11796618
Change-Id: Ie5831db87df958c5444ce3ae07829d62451a7c34
diff --git a/Android.bp b/Android.bp
index 3287b7b..59d698d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -184,6 +184,7 @@
"payload_consumer/verity_writer_android.cc",
"payload_consumer/xz_extent_writer.cc",
"payload_consumer/fec_file_descriptor.cc",
+ "payload_consumer/partition_update_generator_android.cc",
],
}
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/boot_control_android.cc b/boot_control_android.cc
index ec2ca0f..dee5fa8 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -82,12 +82,24 @@
return module_->getCurrentSlot();
}
+bool BootControlAndroid::GetPartitionDevice(const std::string& partition_name,
+ BootControlInterface::Slot slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) const {
+ return dynamic_control_->GetPartitionDevice(partition_name,
+ slot,
+ GetCurrentSlot(),
+ not_in_payload,
+ device,
+ is_dynamic);
+}
bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
- Slot slot,
+ BootControlInterface::Slot slot,
string* device) const {
- return dynamic_control_->GetPartitionDevice(
- partition_name, slot, GetCurrentSlot(), device);
+ return GetPartitionDevice(
+ partition_name, slot, false /* not_in_payload */, device, nullptr);
}
bool BootControlAndroid::IsSlotBootable(Slot slot) const {
diff --git a/boot_control_android.h b/boot_control_android.h
index 0b042e3..5009dbd 100644
--- a/boot_control_android.h
+++ b/boot_control_android.h
@@ -46,6 +46,11 @@
BootControlInterface::Slot GetCurrentSlot() const override;
bool GetPartitionDevice(const std::string& partition_name,
BootControlInterface::Slot slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) const override;
+ bool GetPartitionDevice(const std::string& partition_name,
+ BootControlInterface::Slot slot,
std::string* device) const override;
bool IsSlotBootable(BootControlInterface::Slot slot) const override;
bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
diff --git a/boot_control_chromeos.cc b/boot_control_chromeos.cc
index 0f47169..da84e99 100644
--- a/boot_control_chromeos.cc
+++ b/boot_control_chromeos.cc
@@ -148,9 +148,11 @@
return current_slot_;
}
-bool BootControlChromeOS::GetPartitionDevice(const string& partition_name,
- unsigned int slot,
- string* device) const {
+bool BootControlChromeOS::GetPartitionDevice(const std::string& partition_name,
+ BootControlInterface::Slot slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) const {
// Partition name prefixed with |kPartitionNamePrefixDlc| is a DLC module.
if (base::StartsWith(partition_name,
kPartitionNamePrefixDlc,
@@ -180,9 +182,18 @@
return false;
*device = part_device;
+ if (is_dynamic) {
+ *is_dynamic = false;
+ }
return true;
}
+bool BootControlChromeOS::GetPartitionDevice(const string& partition_name,
+ BootControlInterface::Slot slot,
+ string* device) const {
+ return GetPartitionDevice(partition_name, slot, false, device, nullptr);
+}
+
bool BootControlChromeOS::IsSlotBootable(Slot slot) const {
int partition_num = GetPartitionNumber(kChromeOSPartitionNameKernel, slot);
if (partition_num < 0)
diff --git a/boot_control_chromeos.h b/boot_control_chromeos.h
index 0209052..6edc148 100644
--- a/boot_control_chromeos.h
+++ b/boot_control_chromeos.h
@@ -47,6 +47,11 @@
BootControlInterface::Slot GetCurrentSlot() const override;
bool GetPartitionDevice(const std::string& partition_name,
BootControlInterface::Slot slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) const override;
+ bool GetPartitionDevice(const std::string& partition_name,
+ BootControlInterface::Slot slot,
std::string* device) const override;
bool IsSlotBootable(BootControlInterface::Slot slot) const override;
bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
diff --git a/cleanup_previous_update_action.cc b/cleanup_previous_update_action.cc
index 88dbc57..1a2476f 100644
--- a/cleanup_previous_update_action.cc
+++ b/cleanup_previous_update_action.cc
@@ -31,7 +31,7 @@
#include "update_engine/payload_consumer/delta_performer.h"
using android::base::GetBoolProperty;
-using android::snapshot::SnapshotManager;
+using android::snapshot::ISnapshotManager;
using android::snapshot::SnapshotMergeStats;
using android::snapshot::UpdateState;
using brillo::MessageLoop;
@@ -56,7 +56,7 @@
CleanupPreviousUpdateAction::CleanupPreviousUpdateAction(
PrefsInterface* prefs,
BootControlInterface* boot_control,
- android::snapshot::SnapshotManager* snapshot,
+ android::snapshot::ISnapshotManager* snapshot,
CleanupPreviousUpdateActionDelegateInterface* delegate)
: prefs_(prefs),
boot_control_(boot_control),
@@ -65,7 +65,7 @@
running_(false),
cancel_failed_(false),
last_percentage_(0),
- merge_stats_(SnapshotMergeStats::GetInstance(*snapshot)) {}
+ merge_stats_(nullptr) {}
void CleanupPreviousUpdateAction::PerformAction() {
ResumeAction();
@@ -111,8 +111,10 @@
processor_->ActionComplete(this, ErrorCode::kSuccess);
return;
}
- // SnapshotManager is only available on VAB devices.
- CHECK(snapshot_);
+ // SnapshotManager must be available on VAB devices.
+ CHECK(snapshot_ != nullptr);
+ merge_stats_ = snapshot_->GetSnapshotMergeStatsInstance();
+ CHECK(merge_stats_ != nullptr);
WaitBootCompletedOrSchedule();
}
diff --git a/cleanup_previous_update_action.h b/cleanup_previous_update_action.h
index 91e08b0..6f6ce07 100644
--- a/cleanup_previous_update_action.h
+++ b/cleanup_previous_update_action.h
@@ -49,7 +49,7 @@
CleanupPreviousUpdateAction(
PrefsInterface* prefs,
BootControlInterface* boot_control,
- android::snapshot::SnapshotManager* snapshot,
+ android::snapshot::ISnapshotManager* snapshot,
CleanupPreviousUpdateActionDelegateInterface* delegate);
void PerformAction() override;
@@ -67,13 +67,13 @@
private:
PrefsInterface* prefs_;
BootControlInterface* boot_control_;
- android::snapshot::SnapshotManager* snapshot_;
+ android::snapshot::ISnapshotManager* snapshot_;
CleanupPreviousUpdateActionDelegateInterface* delegate_;
std::unique_ptr<android::snapshot::AutoDevice> metadata_device_;
bool running_{false};
bool cancel_failed_{false};
unsigned int last_percentage_{0};
- android::snapshot::SnapshotMergeStats* merge_stats_;
+ android::snapshot::ISnapshotMergeStats* merge_stats_;
void StartActionInternal();
void ScheduleWaitBootCompleted();
diff --git a/common/boot_control_interface.h b/common/boot_control_interface.h
index 3906e2f..c93de5c 100644
--- a/common/boot_control_interface.h
+++ b/common/boot_control_interface.h
@@ -59,8 +59,18 @@
// every slot. In order to access the dynamic partitions in the target slot,
// GetDynamicPartitionControl()->PreparePartitionsForUpdate() must be called
// (with |update| == true for the first time for a payload, and |false| for
- // for the rest of the times) prior to calling this function. On success,
- // returns true and stores the block device in |device|.
+ // for the rest of the times) prior to calling this function.
+ // The handling may be different based on whether the partition is included
+ // in the update payload. On success, returns true; and stores the block
+ // device in |device|, if the partition is dynamic in |is_dynamic|.
+ virtual bool GetPartitionDevice(const std::string& partition_name,
+ Slot slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) const = 0;
+
+ // Overload of the above function. We assume the partition is always included
+ // in the payload.
virtual bool GetPartitionDevice(const std::string& partition_name,
Slot slot,
std::string* device) const = 0;
diff --git a/common/boot_control_stub.cc b/common/boot_control_stub.cc
index 2eb9211..907f670 100644
--- a/common/boot_control_stub.cc
+++ b/common/boot_control_stub.cc
@@ -35,6 +35,15 @@
return 0;
}
+bool BootControlStub::GetPartitionDevice(const std::string& partition_name,
+ BootControlInterface::Slot slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) const {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
bool BootControlStub::GetPartitionDevice(const string& partition_name,
Slot slot,
string* device) const {
diff --git a/common/boot_control_stub.h b/common/boot_control_stub.h
index cc16190..a1bdb96 100644
--- a/common/boot_control_stub.h
+++ b/common/boot_control_stub.h
@@ -41,6 +41,11 @@
unsigned int GetNumSlots() const override;
BootControlInterface::Slot GetCurrentSlot() const override;
bool GetPartitionDevice(const std::string& partition_name,
+ Slot slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) const override;
+ bool GetPartitionDevice(const std::string& partition_name,
BootControlInterface::Slot slot,
std::string* device) const override;
bool IsSlotBootable(BootControlInterface::Slot slot) const override;
diff --git a/common/fake_boot_control.h b/common/fake_boot_control.h
index bd9d9ca..adbacd6 100644
--- a/common/fake_boot_control.h
+++ b/common/fake_boot_control.h
@@ -48,7 +48,9 @@
bool GetPartitionDevice(const std::string& partition_name,
BootControlInterface::Slot slot,
- std::string* device) const override {
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) const override {
if (slot >= num_slots_)
return false;
auto part_it = devices_[slot].find(partition_name);
@@ -58,6 +60,12 @@
return true;
}
+ bool GetPartitionDevice(const std::string& partition_name,
+ BootControlInterface::Slot slot,
+ std::string* device) const override {
+ return GetPartitionDevice(partition_name, slot, false, device, nullptr);
+ }
+
bool IsSlotBootable(BootControlInterface::Slot slot) const override {
return slot < num_slots_ && is_bootable_[slot];
}
diff --git a/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc
index ecd6252..79c269c 100644
--- a/dynamic_partition_control_android.cc
+++ b/dynamic_partition_control_android.cc
@@ -35,6 +35,7 @@
#include <libavb/libavb.h>
#include <libdm/dm.h>
#include <libsnapshot/snapshot.h>
+#include <libsnapshot/snapshot_stub.h>
#include "update_engine/cleanup_previous_update_action.h"
#include "update_engine/common/boot_control_interface.h"
@@ -58,6 +59,7 @@
using android::snapshot::OptimizeSourceCopyOperation;
using android::snapshot::Return;
using android::snapshot::SnapshotManager;
+using android::snapshot::SnapshotManagerStub;
using android::snapshot::UpdateState;
namespace chromeos_update_engine {
@@ -108,8 +110,10 @@
virtual_ab_(GetFeatureFlag(kVirtualAbEnabled, kVirtualAbRetrofit)) {
if (GetVirtualAbFeatureFlag().IsEnabled()) {
snapshot_ = SnapshotManager::New();
- CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
+ } else {
+ snapshot_ = SnapshotManagerStub::New();
}
+ CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
}
FeatureFlag DynamicPartitionControlAndroid::GetDynamicPartitionsFeatureFlag() {
@@ -489,8 +493,13 @@
}
}
- return PrepareDynamicPartitionsForUpdate(
- source_slot, target_slot, manifest, delete_source);
+ TEST_AND_RETURN_FALSE(PrepareDynamicPartitionsForUpdate(
+ source_slot, target_slot, manifest, delete_source));
+
+ if (required_size != nullptr) {
+ *required_size = 0;
+ }
+ return true;
}
namespace {
@@ -874,22 +883,35 @@
const std::string& partition_name,
uint32_t slot,
uint32_t current_slot,
- std::string* device) {
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic) {
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);
+ if (is_dynamic) {
+ *is_dynamic = false;
+ }
+
// 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)) {
+ switch (GetDynamicPartitionDevice(device_dir,
+ partition_name_suffix,
+ slot,
+ current_slot,
+ not_in_payload,
+ device)) {
case DynamicPartitionDeviceStatus::SUCCESS:
+ if (is_dynamic) {
+ *is_dynamic = true;
+ }
return true;
case DynamicPartitionDeviceStatus::TRY_STATIC:
break;
@@ -908,6 +930,15 @@
return true;
}
+bool DynamicPartitionControlAndroid::GetPartitionDevice(
+ const std::string& partition_name,
+ uint32_t slot,
+ uint32_t current_slot,
+ std::string* device) {
+ return GetPartitionDevice(
+ partition_name, slot, current_slot, false, device, nullptr);
+}
+
bool DynamicPartitionControlAndroid::IsSuperBlockDevice(
const base::FilePath& device_dir,
uint32_t current_slot,
@@ -924,6 +955,7 @@
const std::string& partition_name_suffix,
uint32_t slot,
uint32_t current_slot,
+ bool not_in_payload,
std::string* device) {
std::string super_device =
device_dir.Append(GetSuperPartitionName(slot)).value();
@@ -963,7 +995,7 @@
}
}
- bool force_writable = slot != current_slot;
+ bool force_writable = (slot != current_slot) && !not_in_payload;
if (MapPartitionOnDeviceMapper(
super_device, partition_name_suffix, slot, force_writable, device)) {
return DynamicPartitionDeviceStatus::SUCCESS;
diff --git a/dynamic_partition_control_android.h b/dynamic_partition_control_android.h
index 8ad7593..e3bedbc 100644
--- a/dynamic_partition_control_android.h
+++ b/dynamic_partition_control_android.h
@@ -61,6 +61,13 @@
bool GetPartitionDevice(const std::string& partition_name,
uint32_t slot,
uint32_t current_slot,
+ bool not_in_payload,
+ std::string* device,
+ bool* is_dynamic);
+
+ bool GetPartitionDevice(const std::string& partition_name,
+ uint32_t slot,
+ uint32_t current_slot,
std::string* device);
protected:
@@ -173,8 +180,19 @@
virtual bool EraseSystemOtherAvbFooter(uint32_t source_slot,
uint32_t target_slot);
+ // Helper for PreparePartitionsForUpdate. Used for devices with dynamic
+ // partitions updating without snapshots.
+ // If |delete_source| is set, source partitions are deleted before resizing
+ // target partitions (using DeleteSourcePartitions).
+ virtual bool PrepareDynamicPartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest,
+ bool delete_source);
+
private:
friend class DynamicPartitionControlAndroidTest;
+ friend class SnapshotPartitionTestP;
void UnmapAllPartitions();
bool MapPartitionInternal(const std::string& super_device,
@@ -189,15 +207,6 @@
uint32_t target_slot,
const DeltaArchiveManifest& manifest);
- // Helper for PreparePartitionsForUpdate. Used for devices with dynamic
- // partitions updating without snapshots.
- // If |delete_source| is set, source partitions are deleted before resizing
- // target partitions (using DeleteSourcePartitions).
- bool PrepareDynamicPartitionsForUpdate(uint32_t source_slot,
- uint32_t target_slot,
- const DeltaArchiveManifest& manifest,
- bool delete_source);
-
// Helper for PreparePartitionsForUpdate. Used for snapshotted partitions for
// Virtual A/B update.
bool PrepareSnapshotPartitionsForUpdate(uint32_t source_slot,
@@ -220,6 +229,7 @@
const std::string& partition_name_suffix,
uint32_t slot,
uint32_t current_slot,
+ bool not_in_payload,
std::string* device);
// Return true if |partition_name_suffix| is a block device of
@@ -258,7 +268,7 @@
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::ISnapshotManager> 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
diff --git a/dynamic_partition_control_android_unittest.cc b/dynamic_partition_control_android_unittest.cc
index 2081918..3738170 100644
--- a/dynamic_partition_control_android_unittest.cc
+++ b/dynamic_partition_control_android_unittest.cc
@@ -24,6 +24,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libavb/libavb.h>
+#include <libsnapshot/mock_snapshot.h>
#include "update_engine/common/mock_prefs.h"
#include "update_engine/common/test_utils.h"
@@ -31,6 +32,7 @@
#include "update_engine/mock_dynamic_partition_control.h"
using android::dm::DmDeviceState;
+using android::snapshot::MockSnapshotManager;
using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
using chromeos_update_engine::test_utils::ScopedTempFile;
using std::string;
@@ -72,6 +74,17 @@
ON_CALL(dynamicControl(), EraseSystemOtherAvbFooter(_, _))
.WillByDefault(Return(true));
+
+ ON_CALL(dynamicControl(), IsRecovery()).WillByDefault(Return(false));
+
+ ON_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
+ .WillByDefault(Invoke([&](uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest,
+ bool delete_source) {
+ return dynamicControl().RealPrepareDynamicPartitionsForUpdate(
+ source_slot, target_slot, manifest, delete_source);
+ }));
}
// Return the mocked DynamicPartitionControlInterface.
@@ -892,4 +905,112 @@
ASSERT_EQ(new_expected, device_content);
}
+class FakeAutoDevice : public android::snapshot::AutoDevice {
+ public:
+ FakeAutoDevice() : AutoDevice("") {}
+};
+
+class SnapshotPartitionTestP : public DynamicPartitionControlAndroidTestP {
+ public:
+ void SetUp() override {
+ DynamicPartitionControlAndroidTestP::SetUp();
+ ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+
+ snapshot_ = new NiceMock<MockSnapshotManager>();
+ dynamicControl().snapshot_.reset(snapshot_); // takes ownership
+ EXPECT_CALL(*snapshot_, BeginUpdate()).WillOnce(Return(true));
+ EXPECT_CALL(*snapshot_, EnsureMetadataMounted())
+ .WillRepeatedly(
+ Invoke([]() { return std::make_unique<FakeAutoDevice>(); }));
+
+ manifest_ =
+ PartitionSizesToManifest({{"system", 3_GiB}, {"vendor", 1_GiB}});
+ }
+ void ExpectCreateUpdateSnapshots(android::snapshot::Return val) {
+ manifest_.mutable_dynamic_partition_metadata()->set_snapshot_enabled(true);
+ EXPECT_CALL(*snapshot_, CreateUpdateSnapshots(_))
+ .WillRepeatedly(Invoke([&, val](const auto& manifest) {
+ // Deep comparison requires full protobuf library. Comparing the
+ // pointers are sufficient.
+ EXPECT_EQ(&manifest_, &manifest);
+ LOG(WARNING) << "CreateUpdateSnapshots returning " << val.string();
+ return val;
+ }));
+ }
+ bool PreparePartitionsForUpdate(uint64_t* required_size) {
+ return dynamicControl().PreparePartitionsForUpdate(
+ source(), target(), manifest_, true /* update */, required_size);
+ }
+ MockSnapshotManager* snapshot_ = nullptr;
+ DeltaArchiveManifest manifest_;
+};
+
+// Test happy path of PreparePartitionsForUpdate on a Virtual A/B device.
+TEST_P(SnapshotPartitionTestP, PreparePartitions) {
+ ExpectCreateUpdateSnapshots(android::snapshot::Return::Ok());
+ uint64_t required_size = 0;
+ EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
+ EXPECT_EQ(0u, required_size);
+}
+
+// Test that if not enough space, required size returned by SnapshotManager is
+// passed up.
+TEST_P(SnapshotPartitionTestP, PreparePartitionsNoSpace) {
+ ExpectCreateUpdateSnapshots(android::snapshot::Return::NoSpace(1_GiB));
+ uint64_t required_size = 0;
+ EXPECT_FALSE(PreparePartitionsForUpdate(&required_size));
+ EXPECT_EQ(1_GiB, required_size);
+}
+
+// Test that in recovery, use empty space in super partition for a snapshot
+// update first.
+TEST_P(SnapshotPartitionTestP, RecoveryUseSuperEmpty) {
+ ExpectCreateUpdateSnapshots(android::snapshot::Return::Ok());
+ EXPECT_CALL(dynamicControl(), IsRecovery()).WillRepeatedly(Return(true));
+ // Must not call PrepareDynamicPartitionsForUpdate if
+ // PrepareSnapshotPartitionsForUpdate succeeds.
+ EXPECT_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
+ .Times(0);
+ uint64_t required_size = 0;
+ EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
+ EXPECT_EQ(0u, required_size);
+}
+
+// Test that in recovery, if CreateUpdateSnapshots throws an error, try
+// the flashing path for full updates.
+TEST_P(SnapshotPartitionTestP, RecoveryErrorShouldDeleteSource) {
+ // Expectation on PreparePartitionsForUpdate
+ ExpectCreateUpdateSnapshots(android::snapshot::Return::NoSpace(1_GiB));
+ EXPECT_CALL(dynamicControl(), IsRecovery()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*snapshot_, CancelUpdate()).WillOnce(Return(true));
+ EXPECT_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
+ .WillRepeatedly(Invoke([&](auto source_slot,
+ auto target_slot,
+ const auto& manifest,
+ auto delete_source) {
+ EXPECT_EQ(source(), source_slot);
+ EXPECT_EQ(target(), target_slot);
+ // Deep comparison requires full protobuf library. Comparing the
+ // pointers are sufficient.
+ EXPECT_EQ(&manifest_, &manifest);
+ EXPECT_TRUE(delete_source);
+ return dynamicControl().RealPrepareDynamicPartitionsForUpdate(
+ source_slot, target_slot, manifest, delete_source);
+ }));
+ // Expectation on PrepareDynamicPartitionsForUpdate
+ SetMetadata(source(), {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}});
+ ExpectUnmap({T("system"), T("vendor")});
+ // Expect that the source partitions aren't present in target super metadata.
+ ExpectStoreMetadata({{T("system"), 3_GiB}, {T("vendor"), 1_GiB}});
+
+ uint64_t required_size = 0;
+ EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
+ EXPECT_EQ(0u, required_size);
+}
+
+INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
+ SnapshotPartitionTestP,
+ testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
} // namespace chromeos_update_engine
diff --git a/logging_android.cc b/logging_android.cc
index 88b068b..0219075 100644
--- a/logging_android.cc
+++ b/logging_android.cc
@@ -241,8 +241,8 @@
ignore_result(android::base::ConsumeSuffix(&sv, "\n"));
std::string str(sv.data(), sv.size());
// This will eventually be redirected to CombinedLogger.
- // |tag| is ignored by CombinedLogger, so just leave it empty.
- __android_log_write(priority, "" /* tag */, str.c_str());
+ // Use nullptr as tag so that liblog infers log tag from getprogname().
+ __android_log_write(priority, nullptr /* tag */, str.c_str());
return true;
}
diff --git a/mock_dynamic_partition_control.h b/mock_dynamic_partition_control.h
index 1e4e5fd..1aaebd8 100644
--- a/mock_dynamic_partition_control.h
+++ b/mock_dynamic_partition_control.h
@@ -28,60 +28,55 @@
namespace chromeos_update_engine {
-class MockDynamicPartitionControl : public DynamicPartitionControlInterface {
- public:
- MOCK_METHOD5(MapPartitionOnDeviceMapper,
- bool(const std::string&,
- const std::string&,
- uint32_t,
- bool,
- std::string*));
- MOCK_METHOD0(Cleanup, void());
- MOCK_METHOD0(GetDynamicPartitionsFeatureFlag, FeatureFlag());
- MOCK_METHOD5(
- PreparePartitionsForUpdate,
- bool(uint32_t, uint32_t, const DeltaArchiveManifest&, bool, uint64_t*));
- MOCK_METHOD0(GetVirtualAbFeatureFlag, FeatureFlag());
- MOCK_METHOD1(FinishUpdate, bool(bool));
- MOCK_METHOD0(CleanupSuccessfulUpdate, ErrorCode());
- MOCK_METHOD3(GetCleanupPreviousUpdateAction,
- std::unique_ptr<AbstractAction>(
- BootControlInterface*,
- PrefsInterface*,
- CleanupPreviousUpdateActionDelegateInterface*));
-};
-
class MockDynamicPartitionControlAndroid
: public DynamicPartitionControlAndroid {
public:
- MOCK_METHOD5(MapPartitionOnDeviceMapper,
- bool(const std::string&,
- const std::string&,
- uint32_t,
- bool,
- std::string*));
- MOCK_METHOD1(UnmapPartitionOnDeviceMapper, bool(const 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_METHOD3(LoadMetadataBuilder,
- std::unique_ptr<::android::fs_mgr::MetadataBuilder>(
- const std::string&, uint32_t, uint32_t));
- MOCK_METHOD3(StoreMetadata,
- bool(const std::string&,
- android::fs_mgr::MetadataBuilder*,
- uint32_t));
- MOCK_METHOD1(GetDeviceDir, bool(std::string*));
- MOCK_METHOD0(GetDynamicPartitionsFeatureFlag, FeatureFlag());
- MOCK_METHOD1(GetSuperPartitionName, std::string(uint32_t));
- MOCK_METHOD0(GetVirtualAbFeatureFlag, FeatureFlag());
- MOCK_METHOD1(FinishUpdate, bool(bool));
- MOCK_METHOD5(
- GetSystemOtherPath,
- bool(uint32_t, uint32_t, const std::string&, std::string*, bool*));
- MOCK_METHOD2(EraseSystemOtherAvbFooter, bool(uint32_t, uint32_t));
- MOCK_METHOD0(IsAvbEnabledOnSystemOther, std::optional<bool>());
+ MOCK_METHOD(
+ bool,
+ MapPartitionOnDeviceMapper,
+ (const std::string&, const std::string&, uint32_t, bool, std::string*),
+ (override));
+ MOCK_METHOD(bool,
+ UnmapPartitionOnDeviceMapper,
+ (const std::string&),
+ (override));
+ MOCK_METHOD(void, Cleanup, (), (override));
+ MOCK_METHOD(bool, DeviceExists, (const std::string&), (override));
+ MOCK_METHOD(::android::dm::DmDeviceState,
+ GetState,
+ (const std::string&),
+ (override));
+ MOCK_METHOD(bool,
+ GetDmDevicePathByName,
+ (const std::string&, std::string*),
+ (override));
+ MOCK_METHOD(std::unique_ptr<::android::fs_mgr::MetadataBuilder>,
+ LoadMetadataBuilder,
+ (const std::string&, uint32_t, uint32_t),
+ (override));
+ MOCK_METHOD(bool,
+ StoreMetadata,
+ (const std::string&, android::fs_mgr::MetadataBuilder*, uint32_t),
+ (override));
+ MOCK_METHOD(bool, GetDeviceDir, (std::string*), (override));
+ MOCK_METHOD(FeatureFlag, GetDynamicPartitionsFeatureFlag, (), (override));
+ MOCK_METHOD(std::string, GetSuperPartitionName, (uint32_t), (override));
+ MOCK_METHOD(FeatureFlag, GetVirtualAbFeatureFlag, (), (override));
+ MOCK_METHOD(bool, FinishUpdate, (bool), (override));
+ MOCK_METHOD(bool,
+ GetSystemOtherPath,
+ (uint32_t, uint32_t, const std::string&, std::string*, bool*),
+ (override));
+ MOCK_METHOD(bool,
+ EraseSystemOtherAvbFooter,
+ (uint32_t, uint32_t),
+ (override));
+ MOCK_METHOD(std::optional<bool>, IsAvbEnabledOnSystemOther, (), (override));
+ MOCK_METHOD(bool, IsRecovery, (), (override));
+ MOCK_METHOD(bool,
+ PrepareDynamicPartitionsForUpdate,
+ (uint32_t, uint32_t, const DeltaArchiveManifest&, bool),
+ (override));
void set_fake_mapped_devices(const std::set<std::string>& fake) override {
DynamicPartitionControlAndroid::set_fake_mapped_devices(fake);
@@ -105,6 +100,15 @@
std::optional<bool> RealIsAvbEnabledInFstab(const std::string& path) {
return DynamicPartitionControlAndroid::IsAvbEnabledInFstab(path);
}
+
+ bool RealPrepareDynamicPartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest,
+ bool delete_source) {
+ return DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
+ source_slot, target_slot, manifest, delete_source);
+ }
};
} // namespace chromeos_update_engine
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 4c4ff04..d1de9f4 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -23,6 +23,7 @@
#include <cstring>
#include <map>
#include <memory>
+#include <set>
#include <string>
#include <utility>
#include <vector>
@@ -50,6 +51,7 @@
#include "update_engine/payload_consumer/download_action.h"
#include "update_engine/payload_consumer/extent_reader.h"
#include "update_engine/payload_consumer/extent_writer.h"
+#include "update_engine/payload_consumer/partition_update_generator_interface.h"
#if USE_FEC
#include "update_engine/payload_consumer/fec_file_descriptor.h"
#endif // USE_FEC
@@ -357,12 +359,15 @@
install_plan_->partitions.size() - partitions_.size();
const InstallPlan::Partition& install_part =
install_plan_->partitions[num_previous_partitions + current_partition_];
- // Open source fds if we have a delta payload with minor version >= 2.
- if (payload_->type == InstallPayloadType::kDelta &&
- GetMinorVersion() != kInPlaceMinorPayloadVersion &&
- // With dynamic partitions we could create a new partition in a
- // delta payload, and we shouldn't open source partition in that case.
- install_part.source_size > 0) {
+ // Open source fds if we have a delta payload with minor version >= 2, or for
+ // partitions in the partial update.
+ bool source_may_exist = manifest_.partial_update() ||
+ (payload_->type == InstallPayloadType::kDelta &&
+ GetMinorVersion() != kInPlaceMinorPayloadVersion);
+ // We shouldn't open the source partition in certain cases, e.g. some dynamic
+ // partitions in delta payload, partitions included in the full payload for
+ // partial updates. Use the source size as the indicator.
+ if (source_may_exist && install_part.source_size > 0) {
source_path_ = install_part.source_path;
int err;
source_fd_ = OpenFile(source_path_.c_str(), O_RDONLY, false, &err);
@@ -851,6 +856,41 @@
partitions_.push_back(std::move(kern_part));
}
+ // For VAB and partial updates, the partition preparation will copy the
+ // dynamic partitions metadata to the target metadata slot, and rename the
+ // slot suffix of the partitions in the metadata.
+ if (install_plan_->target_slot != BootControlInterface::kInvalidSlot) {
+ uint64_t required_size = 0;
+ if (!PreparePartitionsForUpdate(&required_size)) {
+ if (required_size > 0) {
+ *error = ErrorCode::kNotEnoughSpace;
+ } else {
+ *error = ErrorCode::kInstallDeviceOpenError;
+ }
+ return false;
+ }
+ }
+
+ // TODO(xunchang) TBD: allow partial update only on devices with dynamic
+ // partition.
+ if (manifest_.partial_update()) {
+ std::set<std::string> touched_partitions;
+ for (const auto& partition_update : partitions_) {
+ touched_partitions.insert(partition_update.partition_name());
+ }
+
+ auto generator = partition_update_generator::Create(boot_control_);
+ std::vector<PartitionUpdate> other_partitions;
+ TEST_AND_RETURN_FALSE(
+ generator->GenerateOperationsForPartitionsNotInPayload(
+ install_plan_->source_slot,
+ install_plan_->target_slot,
+ touched_partitions,
+ &other_partitions));
+ partitions_.insert(
+ partitions_.end(), other_partitions.begin(), other_partitions.end());
+ }
+
// Fill in the InstallPlan::partitions based on the partitions from the
// payload.
for (const auto& partition : partitions_) {
@@ -924,22 +964,13 @@
install_plan_->partitions.push_back(install_part);
}
- if (install_plan_->target_slot != BootControlInterface::kInvalidSlot) {
- uint64_t required_size = 0;
- if (!PreparePartitionsForUpdate(&required_size)) {
- if (required_size > 0) {
- *error = ErrorCode::kNotEnoughSpace;
- } else {
- *error = ErrorCode::kInstallDeviceOpenError;
- }
- return false;
- }
- }
-
if (major_payload_version_ == kBrilloMajorPayloadVersion) {
manifest_.clear_partitions();
}
+ // TODO(xunchang) only need to load the partitions for those in payload.
+ // Because we have already loaded the other once when generating SOURCE_COPY
+ // operations.
if (!install_plan_->LoadPartitionsFromSlots(boot_control_)) {
LOG(ERROR) << "Unable to determine all the partition devices.";
*error = ErrorCode::kInstallDeviceOpenError;
@@ -1712,7 +1743,6 @@
ErrorCode DeltaPerformer::ValidateManifest() {
// Perform assorted checks to sanity check the manifest, make sure it
// matches data from other sources, and that it is a supported version.
-
bool has_old_fields =
(manifest_.has_old_kernel_info() || manifest_.has_old_rootfs_info());
for (const PartitionUpdate& partition : manifest_.partitions()) {
@@ -1737,8 +1767,8 @@
<< "' payload.";
return ErrorCode::kPayloadMismatchedType;
}
-
// Check that the minor version is compatible.
+ // TODO(xunchang) increment minor version & add check for partial update
if (actual_payload_type == InstallPayloadType::kFull) {
if (manifest_.minor_version() != kFullPayloadMinorVersion) {
LOG(ERROR) << "Manifest contains minor version "
diff --git a/payload_consumer/partition_update_generator_android.cc b/payload_consumer/partition_update_generator_android.cc
new file mode 100644
index 0000000..fcacc86
--- /dev/null
+++ b/payload_consumer/partition_update_generator_android.cc
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2020 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/payload_consumer/partition_update_generator_android.h"
+
+#include <memory>
+
+namespace chromeos_update_engine {
+
+bool PartitionUpdateGeneratorAndroid::
+ GenerateOperationsForPartitionsNotInPayload(
+ BootControlInterface::Slot source_slot,
+ BootControlInterface::Slot target_slot,
+ const std::set<std::string>& partitions_in_payload,
+ std::vector<PartitionUpdate>* update_list) {
+ // TODO(xunchang) implement the function
+ CHECK(boot_control_);
+ return true;
+}
+
+namespace partition_update_generator {
+std::unique_ptr<PartitionUpdateGeneratorInterface> Create(
+ BootControlInterface* boot_control) {
+ return std::make_unique<PartitionUpdateGeneratorAndroid>(boot_control);
+}
+} // namespace partition_update_generator
+
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/partition_update_generator_android.h b/payload_consumer/partition_update_generator_android.h
new file mode 100644
index 0000000..bb50133
--- /dev/null
+++ b/payload_consumer/partition_update_generator_android.h
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2020 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_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_ANDROID_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_ANDROID_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/payload_consumer/partition_update_generator_interface.h"
+
+namespace chromeos_update_engine {
+class PartitionUpdateGeneratorAndroid
+ : public PartitionUpdateGeneratorInterface {
+ public:
+ explicit PartitionUpdateGeneratorAndroid(BootControlInterface* boot_control)
+ : boot_control_(boot_control) {}
+
+ bool GenerateOperationsForPartitionsNotInPayload(
+ BootControlInterface::Slot source_slot,
+ BootControlInterface::Slot target_slot,
+ const std::set<std::string>& partitions_in_payload,
+ std::vector<PartitionUpdate>* update_list) override;
+
+ private:
+ BootControlInterface* boot_control_;
+};
+
+} // namespace chromeos_update_engine
+
+#endif
diff --git a/payload_consumer/partition_update_generator_interface.h b/payload_consumer/partition_update_generator_interface.h
new file mode 100644
index 0000000..0341d40
--- /dev/null
+++ b/payload_consumer/partition_update_generator_interface.h
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2020 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_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_INTERFACE_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_INTERFACE_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "update_engine/common/boot_control_interface.h"
+
+namespace chromeos_update_engine {
+class PartitionUpdate;
+
+// This class parses the partitions that are not included in the payload of a
+// partial A/B update. And it generates additional operations for these
+// partitions to make the update complete.
+class PartitionUpdateGeneratorInterface {
+ public:
+ virtual ~PartitionUpdateGeneratorInterface() = default;
+
+ // Adds PartitionUpdate for partitions not included in the payload. For static
+ // partitions, it generates SOURCE_COPY operations to copy the bytes from the
+ // source slot to target slot. For dynamic partitions, it only calculates the
+ // partition hash for the filesystem verification later.
+ virtual bool GenerateOperationsForPartitionsNotInPayload(
+ BootControlInterface::Slot source_slot,
+ BootControlInterface::Slot target_slot,
+ const std::set<std::string>& partitions_in_payload,
+ std::vector<PartitionUpdate>* update_list) = 0;
+};
+
+namespace partition_update_generator {
+std::unique_ptr<PartitionUpdateGeneratorInterface> Create(
+ BootControlInterface* boot_control);
+}
+
+} // namespace chromeos_update_engine
+
+#endif
diff --git a/payload_consumer/partition_update_generator_stub.cc b/payload_consumer/partition_update_generator_stub.cc
new file mode 100644
index 0000000..e2b64ec
--- /dev/null
+++ b/payload_consumer/partition_update_generator_stub.cc
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2020 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/payload_consumer/partition_update_generator_stub.h"
+
+#include <memory>
+
+namespace chromeos_update_engine {
+
+bool PartitionUpdateGeneratorStub::GenerateOperationsForPartitionsNotInPayload(
+ chromeos_update_engine::BootControlInterface::Slot source_slot,
+ chromeos_update_engine::BootControlInterface::Slot target_slot,
+ const std::set<std::string>& partitions_in_payload,
+ std::vector<PartitionUpdate>* update_list) {
+ return true;
+}
+
+namespace partition_update_generator {
+std::unique_ptr<PartitionUpdateGeneratorInterface> Create(
+ BootControlInterface* boot_control) {
+ return std::make_unique<PartitionUpdateGeneratorStub>();
+}
+} // namespace partition_update_generator
+
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/partition_update_generator_stub.h b/payload_consumer/partition_update_generator_stub.h
new file mode 100644
index 0000000..282875e
--- /dev/null
+++ b/payload_consumer/partition_update_generator_stub.h
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2020 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_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_STUB_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_PARTITION_UPDATE_GENERATOR_STUB_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/payload_consumer/partition_update_generator_interface.h"
+
+namespace chromeos_update_engine {
+class PartitionUpdateGeneratorStub : public PartitionUpdateGeneratorInterface {
+ public:
+ PartitionUpdateGeneratorStub() = default;
+ bool GenerateOperationsForPartitionsNotInPayload(
+ BootControlInterface::Slot source_slot,
+ BootControlInterface::Slot target_slot,
+ const std::set<std::string>& partitions_in_payload,
+ std::vector<PartitionUpdate>* update_list) override;
+};
+
+} // namespace chromeos_update_engine
+
+#endif
diff --git a/test_config.xml b/test_config.xml
index 2639e7f..fe3cbfd 100644
--- a/test_config.xml
+++ b/test_config.xml
@@ -16,13 +16,14 @@
<configuration description="Config to run update_engine_unittests on device">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
- <option name="push" value="update_engine_unittests->/data/local/tmp/update_engine_unittests" />
+ <option name="push" value="update_engine_unittests->/data/nativetest/update_engine_unittests" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="native-test-device-path" value="/data/nativetest" />
<!-- The following rules avoid test runner from calling the following helper executables
directly as gtests. -->
<option name="file-exclusion-filter-regex" value=".*/delta_generator$" />
diff --git a/update_metadata.proto b/update_metadata.proto
index 9bc0d8a..4b4c327 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -367,4 +367,7 @@
// Metadata related to all dynamic partitions.
optional DynamicPartitionMetadata dynamic_partition_metadata = 15;
+
+ // If the payload only updates a subset of partitions on the device.
+ optional bool partial_update = 16;
}