Support sideload full update on VAB
On devices with Virtual A/B, certain regions are snapshotted
during the udpate. Snapshots may fail to be created in
recovery because fiemap of userdata cannot be retrieved (it may
succeed if empty space in super is enough to hold CoW, although
this is likely not the case for launch VAB).
In this case (on VAB devices when super is not big enough,
esp. for launch VAB devices):
- sideloading incremental OTAs are not allowed
- sideloading full OTAs is similar to flashing. Source partitions
are deleted from super.
Note that to reduce the difference between launch and retrofit
VAB devices, most code only checks
GetVirtualAbFeatureFlag().IsEnabled(). IsLaunch() is only used when
doing sanity checks and providing more detailed logging information.
Test: manually make PrepareSnapshotPartitionsForUpdate fail, then sideload
Test: update_engine_unittests
Bug: 140749209
Change-Id: I64927c85bfb10b34d8bd19bd88c18663f4a2a917
diff --git a/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc
index 881ff11..072a3ec 100644
--- a/dynamic_partition_control_android.cc
+++ b/dynamic_partition_control_android.cc
@@ -68,8 +68,14 @@
// needs to be mapped, this timeout is longer than |kMapTimeout|.
constexpr std::chrono::milliseconds kMapSnapshotTimeout{5000};
+#ifdef __ANDROID_RECOVERY__
+constexpr bool kIsRecovery = true;
+#else
+constexpr bool kIsRecovery = false;
+#endif
+
DynamicPartitionControlAndroid::~DynamicPartitionControlAndroid() {
- CleanupInternal();
+ Cleanup();
}
static FeatureFlag GetFeatureFlag(const char* enable_prop,
@@ -234,8 +240,7 @@
return true;
}
-void DynamicPartitionControlAndroid::CleanupInternal() {
- metadata_device_.reset();
+void DynamicPartitionControlAndroid::UnmapAllPartitions() {
if (mapped_devices_.empty()) {
return;
}
@@ -249,7 +254,8 @@
}
void DynamicPartitionControlAndroid::Cleanup() {
- CleanupInternal();
+ UnmapAllPartitions();
+ metadata_device_.reset();
}
bool DynamicPartitionControlAndroid::DeviceExists(const std::string& path) {
@@ -419,28 +425,53 @@
if (!update)
return true;
+ bool delete_source = false;
+
if (GetVirtualAbFeatureFlag().IsEnabled()) {
// On Virtual A/B device, either CancelUpdate() or BeginUpdate() must be
// called before calling UnmapUpdateSnapshot.
// - If target_supports_snapshot_, PrepareSnapshotPartitionsForUpdate()
// calls BeginUpdate() which resets update state
- // - If !target_supports_snapshot_, explicitly CancelUpdate().
+ // - If !target_supports_snapshot_ or PrepareSnapshotPartitionsForUpdate
+ // failed in recovery, explicitly CancelUpdate().
if (target_supports_snapshot_) {
- return PrepareSnapshotPartitionsForUpdate(
- source_slot, target_slot, manifest, required_size);
+ if (PrepareSnapshotPartitionsForUpdate(
+ source_slot, target_slot, manifest, required_size)) {
+ return true;
+ }
+
+ // Virtual A/B device doing Virtual A/B update in Android mode must use
+ // snapshots.
+ if (!IsRecovery()) {
+ LOG(ERROR) << "PrepareSnapshotPartitionsForUpdate failed in Android "
+ << "mode";
+ return false;
+ }
+
+ delete_source = true;
+ LOG(INFO) << "PrepareSnapshotPartitionsForUpdate failed in recovery. "
+ << "Attempt to overwrite existing partitions if possible";
+ } else {
+ // Downgrading to an non-Virtual A/B build or is secondary OTA.
+ LOG(INFO) << "Using regular A/B on Virtual A/B because package disabled "
+ << "snapshots.";
}
+
if (!snapshot_->CancelUpdate()) {
LOG(ERROR) << "Cannot cancel previous update.";
return false;
}
}
- return PrepareDynamicPartitionsForUpdate(source_slot, target_slot, manifest);
+
+ return PrepareDynamicPartitionsForUpdate(
+ source_slot, target_slot, manifest, delete_source);
}
bool DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
uint32_t source_slot,
uint32_t target_slot,
- const DeltaArchiveManifest& manifest) {
+ const DeltaArchiveManifest& manifest,
+ bool delete_source) {
const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
// Unmap all the target dynamic partitions because they would become
@@ -468,6 +499,11 @@
return false;
}
+ if (delete_source) {
+ TEST_AND_RETURN_FALSE(
+ DeleteSourcePartitions(builder.get(), source_slot, manifest));
+ }
+
if (!UpdatePartitionMetadata(builder.get(), target_slot, manifest)) {
return false;
}
@@ -585,7 +621,7 @@
}
bool DynamicPartitionControlAndroid::FinishUpdate() {
- if (GetVirtualAbFeatureFlag().IsEnabled() && target_supports_snapshot_) {
+ if (snapshot_->GetUpdateState() == UpdateState::Initiated) {
LOG(INFO) << "Snapshot writes are done.";
return snapshot_->FinishedSnapshotWrites();
}
@@ -713,4 +749,40 @@
return ErrorCode::kDeviceCorrupted;
}
+bool DynamicPartitionControlAndroid::IsRecovery() {
+ return kIsRecovery;
+}
+
+static bool IsIncrementalUpdate(const DeltaArchiveManifest& manifest) {
+ const auto& partitions = manifest.partitions();
+ return std::any_of(partitions.begin(), partitions.end(), [](const auto& p) {
+ return p.has_old_partition_info();
+ });
+}
+
+bool DynamicPartitionControlAndroid::DeleteSourcePartitions(
+ MetadataBuilder* builder,
+ uint32_t source_slot,
+ const DeltaArchiveManifest& manifest) {
+ TEST_AND_RETURN_FALSE(IsRecovery());
+
+ if (IsIncrementalUpdate(manifest)) {
+ LOG(ERROR) << "Cannot sideload incremental OTA because snapshots cannot "
+ << "be created.";
+ if (GetVirtualAbFeatureFlag().IsLaunch()) {
+ LOG(ERROR) << "Sideloading incremental updates on devices launches "
+ << " Virtual A/B is not supported.";
+ }
+ return false;
+ }
+
+ LOG(INFO) << "Will overwrite existing partitions. Slot "
+ << BootControlInterface::SlotName(source_slot)
+ << "may be unbootable until update finishes!";
+ const std::string source_suffix = SlotSuffixForSlotNumber(source_slot);
+ DeleteGroupsWithSuffix(builder, source_suffix);
+
+ return true;
+}
+
} // namespace chromeos_update_engine