Merge tag 'android-16.0.0_r1' of https://android.googlesource.com/platform/system/update_engine into HEAD
Android 16.0.0 release 1
Change-Id: Ibada584254630817cecbe5b3f65d5054f8f71f8d
# -----BEGIN PGP SIGNATURE-----
#
# iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCaEhhHQAKCRDorT+BmrEO
# eEvtAJ45hKB0ZwGKiCmLoLO0D9ZxmDQL1ACfZhsozHYy9zTUHJSXtOs5k9twF6w=
# =VzNl
# -----END PGP SIGNATURE-----
# gpg: Signature faite le mar 10 jun 2025 12:45:17 EDT
# gpg: avec la clef DSA 4340D13570EF945E83810964E8AD3F819AB10E78
# gpg: Impossible de vérifier la signature : Pas de clef publique
diff --git a/Android.bp b/Android.bp
index 759b9e2..c85d910 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1291,6 +1291,47 @@
],
}
+// update_engine_unittests (type: executable)
+// ========================================================
+// Main unittest file.
+cc_test {
+ name: "update_engine_recovery_unittests",
+ defaults: [
+ "ue_defaults",
+ "update_metadata-protos_exports",
+ ],
+ cflags: [
+ "-D__ANDROID_RECOVERY__",
+ ],
+
+ static_libs: [
+ "libbrillo-test-helpers",
+ "libbase",
+ "liblog",
+ "libgmock",
+ "libchrome_test_helpers",
+ "libupdate_engine_android",
+ "update_metadata-protos",
+ "libsnapshot_cow",
+ "libdm",
+ ],
+
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
+ test_suites: ["device-tests"],
+ srcs: [
+ "payload_consumer/postinstall_runner_action_recovery_unittest.cc",
+ "payload_consumer/postinstall_runner_action.cc",
+ "common/subprocess.cc",
+ "common/test_utils.cc",
+ "common/utils.cc",
+ "common/dynamic_partition_control_stub.cc",
+ "common/action_processor.cc",
+ "common/error_code_utils.cc",
+ ],
+}
+
// Brillo update payload generation script
// ========================================================
sh_binary {
diff --git a/METADATA b/METADATA
deleted file mode 100644
index d97975c..0000000
--- a/METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-third_party {
- license_type: NOTICE
-}
diff --git a/OWNERS b/OWNERS
index 1900cf4..7495e15 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,7 +1,6 @@
set noparent
# Android et. al. maintainers:
-deymo@google.com
senj@google.com
zhangkelvin@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 85fd5ec..e79b4c1 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -2,6 +2,7 @@
clang_format = true
cpplint = true
pylint = true
+bpfmt = true
[Hook Scripts]
protobuflint = ./protobuflint.py ${PREUPLOAD_COMMIT} ${PREUPLOAD_FILES}
diff --git a/aosp/boot_control_android.cc b/aosp/boot_control_android.cc
index 0a1d3de..343be9f 100644
--- a/aosp/boot_control_android.cc
+++ b/aosp/boot_control_android.cc
@@ -18,13 +18,13 @@
#include <memory>
#include <utility>
-#include <vector>
#include <base/bind.h>
#include <base/logging.h>
#include <bootloader_message/bootloader_message.h>
#include <brillo/message_loops/message_loop.h>
+
#include "update_engine/aosp/dynamic_partition_control_android.h"
using std::string;
diff --git a/aosp/boot_control_android.h b/aosp/boot_control_android.h
index 51923e2..57bf5de 100644
--- a/aosp/boot_control_android.h
+++ b/aosp/boot_control_android.h
@@ -17,7 +17,6 @@
#ifndef UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
#define UPDATE_ENGINE_AOSP_BOOT_CONTROL_ANDROID_H_
-#include <map>
#include <memory>
#include <string>
diff --git a/aosp/dynamic_partition_control_android.cc b/aosp/dynamic_partition_control_android.cc
index d1c3bf2..b10be76 100644
--- a/aosp/dynamic_partition_control_android.cc
+++ b/aosp/dynamic_partition_control_android.cc
@@ -226,7 +226,7 @@
// One exception is when /metadata is not mounted. Fallback to
// CreateLogicalPartition as snapshots are not created in the first place.
params.timeout_ms = kMapSnapshotTimeout;
- success = snapshot_->MapUpdateSnapshot(params, path);
+ success = GetSnapshotManager()->MapUpdateSnapshot(params, path);
} else {
params.timeout_ms = kMapTimeout;
success = CreateLogicalPartition(params, path);
@@ -311,7 +311,7 @@
// a paused update. Clean up any underlying devices.
if (ExpectMetadataMounted() &&
!device_name.ends_with(kRWSourcePartitionSuffix)) {
- success &= snapshot_->UnmapUpdateSnapshot(device_name);
+ success &= GetSnapshotManager()->UnmapUpdateSnapshot(device_name);
} else {
LOG(INFO) << "Skip UnmapUpdateSnapshot(" << device_name << ")";
}
@@ -328,7 +328,7 @@
}
bool DynamicPartitionControlAndroid::UnmapAllPartitions() {
- snapshot_->UnmapAllSnapshots();
+ GetSnapshotManager()->UnmapAllSnapshots();
if (mapped_devices_.empty()) {
return false;
}
@@ -358,12 +358,9 @@
LOG(INFO) << "UnmapAllPartitions done";
metadata_device_.reset();
if (GetVirtualAbFeatureFlag().IsEnabled()) {
- snapshot_ = SnapshotManager::New();
- } else {
- snapshot_ = SnapshotManagerStub::New();
+ // Release ISnapshotManager instance so GSID can be gracefully shutdown
+ snapshot_ = nullptr;
}
- CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
- LOG(INFO) << "SnapshotManager initialized.";
}
bool DynamicPartitionControlAndroid::DeviceExists(const std::string& path) {
@@ -585,7 +582,7 @@
// should not proceed because during next boot, snapshots will overlay on
// the devices incorrectly.
if (ExpectMetadataMounted()) {
- TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate());
+ TEST_AND_RETURN_FALSE(GetSnapshotManager()->CancelUpdate());
} else {
LOG(INFO) << "Skip canceling previous update because metadata is not "
<< "mounted";
@@ -996,6 +993,20 @@
return true;
}
+android::snapshot::ISnapshotManager*
+DynamicPartitionControlAndroid::GetSnapshotManager() {
+ if (snapshot_ == nullptr) {
+ if (GetVirtualAbFeatureFlag().IsEnabled()) {
+ snapshot_ = SnapshotManager::New();
+ } else {
+ snapshot_ = SnapshotManagerStub::New();
+ }
+ }
+ CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
+ LOG(INFO) << "SnapshotManager initialized.";
+ return snapshot_.get();
+}
+
bool DynamicPartitionControlAndroid::PrepareSnapshotPartitionsForUpdate(
uint32_t source_slot,
uint32_t target_slot,
@@ -1018,11 +1029,11 @@
TEST_AND_RETURN_FALSE(
CheckSuperPartitionAllocatableSpace(builder.get(), manifest, true));
- if (!snapshot_->BeginUpdate()) {
+ if (!GetSnapshotManager()->BeginUpdate()) {
LOG(ERROR) << "Cannot begin new update.";
return false;
}
- auto ret = snapshot_->CreateUpdateSnapshots(manifest);
+ auto ret = GetSnapshotManager()->CreateUpdateSnapshots(manifest);
if (!ret) {
LOG(ERROR) << "Cannot create update snapshots: " << ret.string();
if (required_size != nullptr &&
@@ -1124,9 +1135,9 @@
bool DynamicPartitionControlAndroid::FinishUpdate(bool powerwash_required) {
if (ExpectMetadataMounted()) {
- if (snapshot_->GetUpdateState() == UpdateState::Initiated) {
+ if (GetSnapshotManager()->GetUpdateState() == UpdateState::Initiated) {
LOG(INFO) << "Snapshot writes are done.";
- return snapshot_->FinishedSnapshotWrites(powerwash_required);
+ return GetSnapshotManager()->FinishedSnapshotWrites(powerwash_required);
}
} else {
LOG(INFO) << "Skip FinishedSnapshotWrites() because /metadata is not "
@@ -1350,7 +1361,7 @@
return std::make_unique<NoOpAction>();
}
return std::make_unique<CleanupPreviousUpdateAction>(
- prefs, boot_control, snapshot_.get(), delegate);
+ prefs, boot_control, GetSnapshotManager(), delegate);
}
bool DynamicPartitionControlAndroid::ResetUpdate(PrefsInterface* prefs) {
@@ -1372,7 +1383,7 @@
prefs, false /* quick */, false /* skip dynamic partitions metadata */));
if (ExpectMetadataMounted()) {
- TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate());
+ TEST_AND_RETURN_FALSE(GetSnapshotManager()->CancelUpdate());
} else {
LOG(INFO) << "Skip cancelling update in ResetUpdate because /metadata is "
<< "not mounted";
@@ -1473,7 +1484,7 @@
}
if (metadata_device_ == nullptr) {
- metadata_device_ = snapshot_->EnsureMetadataMounted();
+ metadata_device_ = GetSnapshotManager()->EnsureMetadataMounted();
}
return metadata_device_ != nullptr;
}
@@ -1497,7 +1508,7 @@
.timeout_ms = kMapSnapshotTimeout};
// TODO(zhangkelvin) Open an APPEND mode CowWriter once there's an API to do
// it.
- return snapshot_->OpenSnapshotWriter(params, label);
+ return GetSnapshotManager()->OpenSnapshotWriter(params, label);
} // namespace chromeos_update_engine
std::unique_ptr<FileDescriptor> DynamicPartitionControlAndroid::OpenCowFd(
@@ -1531,7 +1542,13 @@
}
bool DynamicPartitionControlAndroid::MapAllPartitions() {
- return snapshot_->MapAllSnapshots(kMapSnapshotTimeout);
+ // This flag tells us if VAB is enabled. In the case it's not (e.g. for
+ // secondary payloads) we are falling back on A/B and MapAllPartitions should
+ // just be a no-op
+ if (!target_supports_snapshot_) {
+ return true;
+ }
+ return GetSnapshotManager()->MapAllSnapshots(kMapSnapshotTimeout);
}
bool DynamicPartitionControlAndroid::IsDynamicPartition(
@@ -1555,7 +1572,7 @@
bool DynamicPartitionControlAndroid::UpdateUsesSnapshotCompression() {
return GetVirtualAbFeatureFlag().IsEnabled() &&
- snapshot_->UpdateUsesCompression();
+ GetSnapshotManager()->UpdateUsesCompression();
}
FeatureFlag
diff --git a/aosp/dynamic_partition_control_android.h b/aosp/dynamic_partition_control_android.h
index 1f70184..e5ba86a 100644
--- a/aosp/dynamic_partition_control_android.h
+++ b/aosp/dynamic_partition_control_android.h
@@ -339,6 +339,7 @@
bool SetTargetBuildVars(const DeltaArchiveManifest& manifest);
std::string GetDeviceName(std::string partition_name, uint32_t slot) const;
+ android::snapshot::ISnapshotManager* GetSnapshotManager();
std::set<std::string> mapped_devices_;
const FeatureFlag dynamic_partitions_;
diff --git a/aosp/service_delegate_android_interface.h b/aosp/service_delegate_android_interface.h
index c73c6de..5e139d7 100644
--- a/aosp/service_delegate_android_interface.h
+++ b/aosp/service_delegate_android_interface.h
@@ -17,8 +17,6 @@
#ifndef UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
#define UPDATE_ENGINE_AOSP_SERVICE_DELEGATE_ANDROID_INTERFACE_H_
-#include <inttypes.h>
-
#include <memory>
#include <string>
#include <vector>
diff --git a/aosp/sideload_main.cc b/aosp/sideload_main.cc
index 4a92ca7..9e7512e 100644
--- a/aosp/sideload_main.cc
+++ b/aosp/sideload_main.cc
@@ -120,7 +120,7 @@
};
// Apply an update payload directly from the given payload URI.
-bool ApplyUpdatePayload(const string& payload,
+ErrorCode ApplyUpdatePayload(const string& payload,
int64_t payload_offset,
int64_t payload_size,
const vector<string>& headers,
@@ -145,13 +145,13 @@
boot_control::CreateBootControl();
if (!boot_control) {
LOG(ERROR) << "Error initializing the BootControlInterface.";
- return false;
+ return ErrorCode::kError;
}
std::unique_ptr<HardwareInterface> hardware = hardware::CreateHardware();
if (!hardware) {
LOG(ERROR) << "Error initializing the HardwareInterface.";
- return false;
+ return ErrorCode::kError;
}
UpdateAttempterAndroid update_attempter(&sideload_daemon_state,
@@ -161,11 +161,13 @@
nullptr);
update_attempter.Init();
- TEST_AND_RETURN_FALSE(update_attempter.ApplyPayload(
- payload, payload_offset, payload_size, headers, nullptr));
+ if (!update_attempter.ApplyPayload(
+ payload, payload_offset, payload_size, headers, nullptr)) {
+ LOG(ERROR) << "Error attempting the ApplyPayload.";
+ }
loop.Run();
- return sideload_daemon_state.status() == UpdateStatus::UPDATED_NEED_REBOOT;
+ return sideload_daemon_state.error_code();
}
} // namespace
@@ -198,9 +200,6 @@
vector<string> headers = base::SplitString(
FLAGS_headers, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- if (!chromeos_update_engine::ApplyUpdatePayload(
- FLAGS_payload, FLAGS_offset, FLAGS_size, headers, FLAGS_status_fd))
- return 1;
-
- return 0;
+ return static_cast<int>(chromeos_update_engine::ApplyUpdatePayload(
+ FLAGS_payload, FLAGS_offset, FLAGS_size, headers, FLAGS_status_fd));
}
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
index f29383a..461cece 100644
--- a/aosp/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -17,6 +17,7 @@
#include "update_engine/aosp/update_attempter_android.h"
#include <algorithm>
+#include <iterator>
#include <map>
#include <memory>
#include <ostream>
@@ -56,6 +57,7 @@
#include "update_engine/payload_consumer/payload_verifier.h"
#include "update_engine/payload_consumer/postinstall_runner_action.h"
#include "update_engine/update_boot_flags_action.h"
+#include "update_engine/update_metadata.pb.h"
#include "update_engine/update_status.h"
#include "update_engine/update_status_utils.h"
@@ -480,6 +482,18 @@
}
bool UpdateAttempterAndroid::CancelUpdate(Error* error) {
+ auto action = processor_->current_action();
+ if (action != nullptr &&
+ action->Type() == CleanupPreviousUpdateAction::StaticType()) {
+ return LogAndSetError(
+ error,
+ __LINE__,
+ __FILE__,
+ "CleanupPreviousUpdateAction is running, this action cannot be "
+ "canceled. As it often performs critical merge operations after "
+ "reboot.",
+ ErrorCode::kRollbackNotPossible);
+ }
if (!processor_->IsRunning())
return LogAndSetGenericError(
error, __LINE__, __FILE__, "No ongoing update to cancel.");
@@ -546,6 +560,32 @@
return !(a == b);
}
+bool VerifyPayloadMetadata(Error* error,
+ std::string_view metadata,
+ const PayloadMetadata& payload_metadata) {
+ auto payload_verifier = PayloadVerifier::CreateInstanceFromZipPath(
+ constants::kUpdateCertificatesPath);
+ if (!payload_verifier) {
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Failed to create the payload verifier from " +
+ std::string(constants::kUpdateCertificatesPath),
+ ErrorCode::kDownloadManifestParseError);
+ }
+ auto errorcode = payload_metadata.ValidateMetadataSignature(
+ metadata, "", *payload_verifier);
+ if (errorcode != ErrorCode::kSuccess) {
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Failed to validate metadata signature: " +
+ utils::ErrorCodeToString(errorcode),
+ errorcode);
+ }
+ return true;
+}
+
bool UpdateAttempterAndroid::VerifyPayloadParseManifest(
const std::string& metadata_filename,
std::string_view expected_metadata_hash,
@@ -619,27 +659,9 @@
<< HexEncode(metadata_hash);
}
}
+ TEST_AND_RETURN_FALSE(
+ VerifyPayloadMetadata(error, ToStringView(metadata), payload_metadata));
- auto payload_verifier = PayloadVerifier::CreateInstanceFromZipPath(
- constants::kUpdateCertificatesPath);
- if (!payload_verifier) {
- return LogAndSetError(error,
- __LINE__,
- __FILE__,
- "Failed to create the payload verifier from " +
- std::string(constants::kUpdateCertificatesPath),
- ErrorCode::kDownloadManifestParseError);
- }
- errorcode = payload_metadata.ValidateMetadataSignature(
- metadata, "", *payload_verifier);
- if (errorcode != ErrorCode::kSuccess) {
- return LogAndSetError(error,
- __LINE__,
- __FILE__,
- "Failed to validate metadata signature: " +
- utils::ErrorCodeToString(errorcode),
- errorcode);
- }
if (!payload_metadata.GetManifest(metadata, manifest)) {
return LogAndSetError(error,
__LINE__,
@@ -853,6 +875,8 @@
return;
}
+ boot_control_->GetDynamicPartitionControl()->Cleanup();
+
if (status_ == UpdateStatus::CLEANUP_PREVIOUS_UPDATE) {
ClearUpdateCompletedMarker();
LOG(INFO) << "Terminating cleanup previous update.";
@@ -862,8 +886,6 @@
return;
}
- boot_control_->GetDynamicPartitionControl()->Cleanup();
-
for (auto observer : daemon_state_->service_observers())
observer->SendPayloadApplicationComplete(error_code);
@@ -1341,6 +1363,7 @@
// previous ApplyPayload() call may have requested powerwash, these
// settings would be saved in `this->install_plan_`. Inherit that setting.
install_plan_.powerwash_required = this->install_plan_.powerwash_required;
+ install_plan_.switch_slot_on_reboot = true;
CHECK_NE(install_plan_.source_slot, UINT32_MAX);
CHECK_NE(install_plan_.target_slot, UINT32_MAX);
@@ -1454,16 +1477,162 @@
processor_->StartProcessing();
}
+bool ParsePayloadMetadata(Error* error,
+ std::string_view manifest_bytes,
+ DeltaArchiveManifest* manifest) {
+ PayloadMetadata payload_metadata;
+ ErrorCode errorcode{};
+ if (payload_metadata.ParsePayloadHeader(manifest_bytes, &errorcode) !=
+ MetadataParseResult::kSuccess) {
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Failed to parse payload header: " +
+ utils::ErrorCodeToString(errorcode),
+ errorcode);
+ }
+ uint64_t metadata_size = payload_metadata.GetMetadataSize() +
+ payload_metadata.GetMetadataSignatureSize();
+ if (metadata_size < kMaxPayloadHeaderSize ||
+ metadata_size > manifest_bytes.size()) {
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Invalid metadata size on cached manifest: " +
+ std::to_string(metadata_size),
+ ErrorCode::kDownloadManifestParseError);
+ }
+ TEST_AND_RETURN_FALSE(
+ VerifyPayloadMetadata(error, manifest_bytes, payload_metadata));
+
+ if (!payload_metadata.GetManifest(manifest_bytes, manifest)) {
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Failed to parse manifest. Might need to install "
+ "OTA first and re-try this API",
+ ErrorCode::kDownloadManifestParseError);
+ }
+ return true;
+}
+
bool UpdateAttempterAndroid::TriggerPostinstall(const std::string& partition,
Error* error) {
- if (error) {
- return LogAndSetGenericError(
+ if (processor_->IsRunning()) {
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Already processing an update, cancel it first.",
+ ErrorCode::kUpdateProcessing);
+ }
+ bool postinstall_succeeded = false;
+ if (!prefs_->GetBoolean(kPrefsPostInstallSucceeded, &postinstall_succeeded)) {
+ return LogAndSetError(
error,
__LINE__,
__FILE__,
- __FUNCTION__ + std::string(" is not implemented"));
+ "Postinstall action did not run. "
+ "OTA update must first reach the "
+ "Postinstall phase(which verfies that all partitions can be mounted) "
+ "before calling TriggerPostinstall",
+ ErrorCode::kPostinstallRunnerError);
}
- return false;
+ if (!postinstall_succeeded) {
+ return LogAndSetError(
+ error,
+ __LINE__,
+ __FILE__,
+ "Postinstall action did not complete successfully. "
+ "OTA update must first reach the "
+ "Postinstall phase(which verfies that all partitions can be mounted) "
+ "before calling TriggerPostinstall",
+ ErrorCode::kPostinstallRunnerError);
+ }
+
+ InstallPlan install_plan;
+ install_plan.source_slot = GetCurrentSlot();
+ install_plan.target_slot = GetTargetSlot();
+ install_plan.switch_slot_on_reboot = false;
+ install_plan.run_post_install = true;
+ install_plan.download_url =
+ std::string(kPrefsManifestBytes) + ":" + install_plan_.download_url;
+
+ std::string manifest_bytes;
+ // kPrefsManifestBytes is set during DownloadAction
+ if (!prefs_->GetString(kPrefsManifestBytes, &manifest_bytes)) {
+ return LogAndSetError(
+ error,
+ __LINE__,
+ __FILE__,
+ "Cached manifest not found. TriggerPostinstall can only be called "
+ "after OTA get past at least FilesystemVerification stage",
+ ErrorCode::kDownloadStateInitializationError);
+ }
+ DeltaArchiveManifest manifest;
+ TEST_AND_RETURN_FALSE(ParsePayloadMetadata(error, manifest_bytes, &manifest));
+ ErrorCode errorcode{};
+ if (!boot_control_->GetDynamicPartitionControl()->PreparePartitionsForUpdate(
+ GetCurrentSlot(),
+ GetTargetSlot(),
+ manifest,
+ false /* should update */,
+ nullptr,
+ &errorcode)) {
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Failed to PreparePartitionsForUpdate",
+ errorcode);
+ }
+ std::vector<PartitionUpdate> partitions;
+ std::copy_if(manifest.partitions().begin(),
+ manifest.partitions().end(),
+ std::back_inserter(partitions),
+ [&partition](const PartitionUpdate& part) {
+ return part.partition_name() == partition;
+ });
+ if (partitions.empty()) {
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Partition " + partition + " not found",
+ ErrorCode::kDownloadStateInitializationError);
+ }
+ // We only want to trigger postinstall for a specific partition,
+ // and since we already checked partitions array is non-empty, reading just
+ // the first partition is enough.
+ if (!partitions[0].has_postinstall_path() ||
+ partitions[0].postinstall_path().empty()) {
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Partition " + partition +
+ " does not have a postinstall script defined",
+ ErrorCode::kDownloadStateInitializationError);
+ }
+ if (!install_plan.ParsePartitions(
+ partitions, boot_control_, manifest.block_size(), &errorcode)) {
+ return LogAndSetError(error,
+ __LINE__,
+ __FILE__,
+ "Failed to parse manifest partitions. Might need "
+ "to install OTA first and re-try this API",
+ ErrorCode::kDownloadManifestParseError);
+ }
+ LOG(INFO) << "Trigger postinstall with this install plan: "
+ << install_plan.ToString();
+
+ auto postinstall_runner_action =
+ std::make_unique<PostinstallRunnerAction>(boot_control_, hardware_);
+ postinstall_runner_action->set_delegate(this);
+
+ auto install_plan_action = std::make_unique<InstallPlanAction>(install_plan);
+ BondActions(install_plan_action.get(), postinstall_runner_action.get());
+ processor_->EnqueueAction(std::move(install_plan_action));
+ processor_->EnqueueAction(std::move(postinstall_runner_action));
+ SetStatusAndNotify(UpdateStatus::FINALIZING);
+ ScheduleProcessingStart();
+ return true;
}
void UpdateAttempterAndroid::OnCleanupProgressUpdate(double progress) {
diff --git a/common/boot_control_interface.h b/common/boot_control_interface.h
index 045236a..1a451d3 100644
--- a/common/boot_control_interface.h
+++ b/common/boot_control_interface.h
@@ -18,15 +18,12 @@
#define UPDATE_ENGINE_COMMON_BOOT_CONTROL_INTERFACE_H_
#include <climits>
-#include <map>
#include <string>
-#include <vector>
#include <base/callback.h>
#include <android-base/macros.h>
#include "update_engine/common/dynamic_partition_control_interface.h"
-#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
diff --git a/common/fake_boot_control.h b/common/fake_boot_control.h
index 8a68501..82d4827 100644
--- a/common/fake_boot_control.h
+++ b/common/fake_boot_control.h
@@ -40,6 +40,11 @@
dynamic_partition_control_.reset(new DynamicPartitionControlStub());
}
+ void SetDynamicPartitionControl(
+ std::unique_ptr<DynamicPartitionControlInterface> dynamic_control) {
+ dynamic_partition_control_ = std::move(dynamic_control);
+ }
+
// BootControlInterface overrides.
unsigned int GetNumSlots() const override { return num_slots_; }
BootControlInterface::Slot GetCurrentSlot() const override {
diff --git a/flags/Android.bp b/flags/Android.bp
new file mode 100644
index 0000000..30194c2
--- /dev/null
+++ b/flags/Android.bp
@@ -0,0 +1,20 @@
+aconfig_declarations {
+ name: "update_engine_aconfig_declarations",
+ package: "com.android.update_engine",
+ container: "system",
+ srcs: [
+ "update_engine_flags.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "update_engine_flags_java_lib",
+ aconfig_declarations: "update_engine_aconfig_declarations",
+ sdk_version: "core_platform",
+ libs: ["fake_device_config"],
+}
+
+cc_aconfig_library {
+ name: "update_engine_flags_cc_lib",
+ aconfig_declarations: "update_engine_aconfig_declarations",
+}
diff --git a/flags/update_engine_flags.aconfig b/flags/update_engine_flags.aconfig
new file mode 100644
index 0000000..a7c286e
--- /dev/null
+++ b/flags/update_engine_flags.aconfig
@@ -0,0 +1,11 @@
+package: "com.android.update_engine"
+container: "system"
+
+flag {
+ name: "minor_changes_2025q4"
+ is_exported: true
+ namespace: "phoenix"
+ description: "Enable 2025Q4 minor changes"
+ bug: "396669769"
+ is_fixed_read_only: true
+}
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index 08c8a67..db94a6f 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -648,11 +648,17 @@
return;
}
CHECK(curl_handle_);
- CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_CONT), CURLE_OK);
- // Since the transfer is in progress, we need to dispatch a CurlPerformOnce()
- // now to let the connection continue, otherwise it would be called by the
- // TimeoutCallback but with a delay.
- CurlPerformOnce();
+ auto ret = curl_easy_pause(curl_handle_, CURLPAUSE_CONT);
+ if (ret != CURLE_OK) {
+ LOG(ERROR) << "Failed to unpause connection, reason: " << ret
+ << ". Terminating transfer.";
+ TerminateTransfer();
+ } else {
+ // Since the transfer is in progress, we need to dispatch a
+ // CurlPerformOnce() now to let the connection continue, otherwise it would
+ // be called by the TimeoutCallback but with a delay.
+ CurlPerformOnce();
+ }
}
// This method sets up callbacks with the MessageLoop.
diff --git a/liburing_cpp/Android.bp b/liburing_cpp/Android.bp
index e17f080..8566bc2 100644
--- a/liburing_cpp/Android.bp
+++ b/liburing_cpp/Android.bp
@@ -1,4 +1,3 @@
-
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
@@ -9,31 +8,32 @@
}
cc_library {
- name: "liburing_cpp",
- host_supported: true,
- recovery_available: true,
- srcs: [
- "src/IoUring.cpp",
- "src/IoUringSQE.cpp",
- ],
- static_libs: [
- "liburing",
- ],
- export_include_dirs: [
- "include",
- ],
+ name: "liburing_cpp",
+ host_supported: true,
+ recovery_available: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ srcs: [
+ "src/IoUring.cpp",
+ "src/IoUringSQE.cpp",
+ ],
+ static_libs: [
+ "liburing",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
}
-
cc_test_host {
- name: "liburing_cpp_tests",
- srcs: [
- "tests/BasicTests.cpp",
- "tests/main.cpp",
- ],
- static_libs: [
- "libgtest",
- "liburing",
- "liburing_cpp",
- ],
+ name: "liburing_cpp_tests",
+ srcs: [
+ "tests/BasicTests.cpp",
+ "tests/main.cpp",
+ ],
+ static_libs: [
+ "libgtest",
+ "liburing",
+ "liburing_cpp",
+ ],
}
diff --git a/liburing_cpp/include/liburing_cpp/IoUring.h b/liburing_cpp/include/liburing_cpp/IoUring.h
index 09ed5cc..b382630 100644
--- a/liburing_cpp/include/liburing_cpp/IoUring.h
+++ b/liburing_cpp/include/liburing_cpp/IoUring.h
@@ -53,6 +53,12 @@
// Register a set of file descriptors to kernel.
virtual Errno RegisterFiles(const int* files, size_t files_size) = 0;
virtual Errno UnregisterFiles() = 0;
+
+ // Prepare read to a registered buffer. This does not submit the operation
+ // to the kernel. For that, call |IoUringInterface::Submit()|
+ virtual IoUringSQE PrepReadFixed(
+ int fd, void* buf, unsigned nbytes, uint64_t offset, int buf_index) = 0;
+
// Append a submission entry into this io_uring. This does not submit the
// operation to the kernel. For that, call |IoUringInterface::Submit()|
virtual IoUringSQE PrepRead(int fd, void *buf, unsigned nbytes,
diff --git a/liburing_cpp/src/IoUring.cpp b/liburing_cpp/src/IoUring.cpp
index cf10272..8589220 100644
--- a/liburing_cpp/src/IoUring.cpp
+++ b/liburing_cpp/src/IoUring.cpp
@@ -82,6 +82,19 @@
return ret;
}
+ IoUringSQE PrepReadFixed(int fd,
+ void* buf,
+ unsigned nbytes,
+ uint64_t offset,
+ int buf_index) override {
+ auto sqe = io_uring_get_sqe(&ring);
+ if (sqe == nullptr) {
+ return IoUringSQE{nullptr};
+ }
+ io_uring_prep_read_fixed(sqe, fd, buf, nbytes, offset, buf_index);
+ return IoUringSQE{static_cast<void*>(sqe)};
+ }
+
IoUringSQE PrepRead(int fd, void* buf, unsigned nbytes,
uint64_t offset) override {
auto sqe = io_uring_get_sqe(&ring);
diff --git a/liburing_cpp/tests/BasicTests.cpp b/liburing_cpp/tests/BasicTests.cpp
index 81288f6..680fb64 100644
--- a/liburing_cpp/tests/BasicTests.cpp
+++ b/liburing_cpp/tests/BasicTests.cpp
@@ -191,4 +191,51 @@
for (int i = 0; i < data.size(); ++i) {
ASSERT_EQ(data[i], i % 256);
}
-}
\ No newline at end of file
+}
+
+TEST_F(IoUringTest, ExtentReadFixedBuffers) {
+ const int fd = fileno(fp);
+ ASSERT_NO_FATAL_FAILURE(WriteTestData(fd, kBlockSize * 3, kBlockSize));
+ ASSERT_NO_FATAL_FAILURE(WriteTestData(fd, kBlockSize * 5, kBlockSize));
+ ASSERT_NO_FATAL_FAILURE(WriteTestData(fd, kBlockSize * 8, kBlockSize));
+ ASSERT_NO_FATAL_FAILURE(WriteTestData(fd, kBlockSize * 13, kBlockSize));
+ fsync(fd);
+
+ std::vector<unsigned char> data;
+ data.resize(kBlockSize * 4);
+ std::unique_ptr<struct iovec[]> vecs = std::make_unique<struct iovec[]>(4);
+ for (int i = 0; i < 4; i++) {
+ vecs[i].iov_base = data.data() + i * kBlockSize;
+ vecs[i].iov_len = kBlockSize;
+ }
+
+ ASSERT_TRUE(ring->RegisterBuffers(vecs.get(), 4).IsOk());
+
+ ASSERT_TRUE(
+ ring->PrepReadFixed(fd, data.data(), kBlockSize, 3 * kBlockSize, 0)
+ .IsOk());
+ ASSERT_TRUE(
+ ring->PrepReadFixed(
+ fd, data.data() + kBlockSize, kBlockSize, 5 * kBlockSize, 1)
+ .IsOk());
+ ASSERT_TRUE(
+ ring->PrepReadFixed(
+ fd, data.data() + kBlockSize * 2, kBlockSize, 8 * kBlockSize, 2)
+ .IsOk());
+ ASSERT_TRUE(
+ ring->PrepReadFixed(
+ fd, data.data() + kBlockSize * 3, kBlockSize, 13 * kBlockSize, 3)
+ .IsOk());
+ ring->SubmitAndWait(4);
+ const auto cqes = ring->PopCQE(4);
+ if (cqes.IsErr()) {
+ FAIL() << cqes.GetError().ErrMsg();
+ return;
+ }
+ for (const auto& cqe : cqes.GetResult()) {
+ ASSERT_GT(cqe.res, 0);
+ }
+ for (int i = 0; i < data.size(); ++i) {
+ ASSERT_EQ(data[i], i % 256);
+ }
+}
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 0b4a13e..14a707f 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -598,6 +598,15 @@
// new_cow_size per partition = partition_size - (#blocks in Copy
// operations part of the partition)
if (install_plan_->vabc_none) {
+ size_t cowOpsize = android::snapshot::GetCowOpSize(
+ manifest_.dynamic_partition_metadata().cow_version());
+ if (cowOpsize == 0) {
+ cowOpsize = sizeof(android::snapshot::CowOperationV2);
+ LOG(WARNING) << "Failed to determine cow op size for COW version "
+ << manifest_.dynamic_partition_metadata().cow_version()
+ << ", defaulting to " << cowOpsize;
+ }
+
LOG(INFO) << "Setting Virtual AB Compression algorithm to none. This "
"would also disable VABC XOR as XOR only saves space if "
"compression is enabled.";
@@ -628,13 +637,11 @@
// Every block written to COW device will come with a header which
// stores src/dst block info along with other data.
const auto cow_metadata_size = partition.new_partition_info().size() /
- manifest_.block_size() *
- sizeof(android::snapshot::CowOperation);
+ manifest_.block_size() * cowOpsize;
// update_engine will emit a label op every op or every two seconds,
// whichever one is longer. In the worst case, we add 1 label per
// InstallOp. So take size of label ops into account.
- const auto label_ops_size =
- partition.operations_size() * sizeof(android::snapshot::CowOperation);
+ const auto label_ops_size = partition.operations_size() * cowOpsize;
// Adding extra 2MB headroom just for any unexpected space usage.
// If we overrun reserved COW size, entire OTA will fail
// and no way for user to retry OTA
diff --git a/payload_consumer/filesystem_verifier_action.cc b/payload_consumer/filesystem_verifier_action.cc
index 8c21673..956f90b 100644
--- a/payload_consumer/filesystem_verifier_action.cc
+++ b/payload_consumer/filesystem_verifier_action.cc
@@ -16,7 +16,6 @@
#include "update_engine/payload_consumer/filesystem_verifier_action.h"
-#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -28,7 +27,6 @@
#include <memory>
#include <numeric>
#include <string>
-#include <utility>
#include <base/bind.h>
#include <brillo/data_encoding.h>
diff --git a/payload_consumer/payload_metadata.cc b/payload_consumer/payload_metadata.cc
index d2e42f0..649d9be 100644
--- a/payload_consumer/payload_metadata.cc
+++ b/payload_consumer/payload_metadata.cc
@@ -154,7 +154,7 @@
}
ErrorCode PayloadMetadata::ValidateMetadataSignature(
- const brillo::Blob& payload,
+ const std::string_view payload,
const string& metadata_signature,
const PayloadVerifier& payload_verifier) const {
if (payload.size() < metadata_size_ + metadata_signature_size_)
diff --git a/payload_consumer/payload_metadata.h b/payload_consumer/payload_metadata.h
index 4d2d5b0..fd24f20 100644
--- a/payload_consumer/payload_metadata.h
+++ b/payload_consumer/payload_metadata.h
@@ -17,14 +17,12 @@
#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_PAYLOAD_METADATA_H_
#define UPDATE_ENGINE_PAYLOAD_CONSUMER_PAYLOAD_METADATA_H_
-#include <inttypes.h>
-
#include <string>
-#include <vector>
#include <android-base/macros.h>
#include <brillo/secure_blob.h>
+#include "update_engine/common/utils.h"
#include "update_engine/common/error_code.h"
#include "update_engine/payload_consumer/payload_verifier.h"
#include "update_engine/update_metadata.pb.h"
@@ -55,6 +53,12 @@
// the payload.
MetadataParseResult ParsePayloadHeader(const brillo::Blob& payload,
ErrorCode* error);
+ MetadataParseResult ParsePayloadHeader(std::string_view payload,
+ ErrorCode* error) {
+ return ParsePayloadHeader(reinterpret_cast<const uint8_t*>(payload.data()),
+ payload.size(),
+ error);
+ }
MetadataParseResult ParsePayloadHeader(const unsigned char* payload,
size_t size,
ErrorCode* error);
@@ -69,9 +73,16 @@
// to the payload server doesn't exploit any vulnerability in the code that
// parses the protocol buffer.
ErrorCode ValidateMetadataSignature(
- const brillo::Blob& payload,
+ std::string_view payload,
const std::string& metadata_signature,
const PayloadVerifier& payload_verifier) const;
+ ErrorCode ValidateMetadataSignature(
+ const std::vector<uint8_t>& payload,
+ const std::string& metadata_signature,
+ const PayloadVerifier& payload_verifier) const {
+ return ValidateMetadataSignature(
+ ToStringView(payload), metadata_signature, payload_verifier);
+ }
// Returns the major payload version. If the version was not yet parsed,
// returns zero.
@@ -93,6 +104,12 @@
bool GetManifest(const unsigned char* payload,
size_t size,
DeltaArchiveManifest* out_manifest) const;
+ bool GetManifest(std::string_view payload,
+ DeltaArchiveManifest* out_manifest) const {
+ return GetManifest(reinterpret_cast<const uint8_t*>(payload.data()),
+ payload.size(),
+ out_manifest);
+ }
// Parses a payload file |payload_path| and prepares the metadata properties,
// manifest and metadata signatures. Can be used as an easy to use utility to
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index 1f423a9..02f002c 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -110,14 +110,17 @@
auto dynamic_control = boot_control_->GetDynamicPartitionControl();
CHECK(dynamic_control);
- // Mount snapshot partitions for Virtual AB Compression Compression.
- if (dynamic_control->UpdateUsesSnapshotCompression()) {
- // If we are switching slots, then we are required to MapAllPartitions,
- // as FinishUpdate() requires all partitions to be mapped.
- // And switching slots requires FinishUpdate() to be called first
+ // Mount snapshot partitions for Virtual AB updates.
+ // If we are switching slots, then we are required to MapAllPartitions,
+ // as FinishUpdate() requires all partitions to be mapped.
+ // And switching slots requires FinishUpdate() to be called first
+ if (dynamic_control->GetVirtualAbFeatureFlag().IsEnabled() &&
+ !constants::kIsRecovery) {
if (!install_plan_.partitions.empty() ||
install_plan_.switch_slot_on_reboot) {
if (!dynamic_control->MapAllPartitions()) {
+ LOG(ERROR) << "Failed to map all partitions, this would cause "
+ "FinishUpdate to fail. Abort early.";
return CompletePostinstall(ErrorCode::kPostInstallMountError);
}
}
@@ -350,14 +353,20 @@
// Runs the postinstall script asynchronously to free up the main loop while
// it's running.
vector<string> command = {abs_path};
-#ifdef __ANDROID__
// In Brillo and Android, we pass the slot number and status fd.
command.push_back(std::to_string(install_plan_.target_slot));
command.push_back(std::to_string(kPostinstallStatusFd));
-#else
- // Chrome OS postinstall expects the target rootfs as the first parameter.
- command.push_back(partition.target_path);
-#endif // __ANDROID__
+ // If install plan only contains one partition, notify the script. Most likely
+ // we are scheduled by `triggerPostinstall` API. Certain scripts might want
+ // different behaviors when triggered by `triggerPostinstall` API. For
+ // example, call scheduler API to schedule a postinstall run during
+ // applyPayload(), and only run actual postinstall work if scheduled by
+ // external async scheduler.
+ if (install_plan_.partitions.size() == 1 &&
+ !install_plan_.switch_slot_on_reboot &&
+ install_plan_.download_url.starts_with(kPrefsManifestBytes)) {
+ command.push_back("1");
+ }
current_command_ = Subprocess::Get().ExecFlags(
command,
diff --git a/payload_consumer/postinstall_runner_action_recovery_unittest.cc b/payload_consumer/postinstall_runner_action_recovery_unittest.cc
new file mode 100644
index 0000000..3a869c9
--- /dev/null
+++ b/payload_consumer/postinstall_runner_action_recovery_unittest.cc
@@ -0,0 +1,202 @@
+//
+// Copyright (C) 2012 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 "gmock/gmock.h"
+#include "update_engine/payload_consumer/postinstall_runner_action.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include "common/dynamic_partition_control_interface.h"
+
+#include <base/bind.h>
+#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
+#include <android-base/stringprintf.h>
+#include <brillo/message_loops/base_message_loop.h>
+#include <brillo/message_loops/message_loop_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/common/mock_dynamic_partition_control.h"
+
+using brillo::MessageLoop;
+using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
+using std::string;
+using testing::_;
+using testing::AtLeast;
+using testing::Return;
+
+namespace chromeos_update_engine {
+
+class PostinstActionProcessorDelegate : public ActionProcessorDelegate {
+ public:
+ PostinstActionProcessorDelegate() = default;
+ void ProcessingDone(const ActionProcessor* processor,
+ ErrorCode code) override {
+ MessageLoop::current()->BreakLoop();
+ processing_done_called_ = true;
+ }
+ void ProcessingStopped(const ActionProcessor* processor) override {
+ MessageLoop::current()->BreakLoop();
+ processing_stopped_called_ = true;
+ }
+
+ void ActionCompleted(ActionProcessor* processor,
+ AbstractAction* action,
+ ErrorCode code) override {
+ if (action->Type() == PostinstallRunnerAction::StaticType()) {
+ code_ = code;
+ code_set_ = true;
+ }
+ }
+
+ ErrorCode code_{ErrorCode::kError};
+ bool code_set_{false};
+ bool processing_done_called_{false};
+ bool processing_stopped_called_{false};
+};
+
+class MockPostinstallRunnerActionDelegate
+ : public PostinstallRunnerAction::DelegateInterface {
+ public:
+ MOCK_METHOD1(ProgressUpdate, void(double progress));
+};
+
+class PostinstallRunnerActionTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ loop_.SetAsCurrent();
+ {
+ auto mock_dynamic_control =
+ std::make_unique<MockDynamicPartitionControl>();
+ mock_dynamic_control_ = mock_dynamic_control.get();
+ fake_boot_control_.SetDynamicPartitionControl(
+ std::move(mock_dynamic_control));
+ }
+ ON_CALL(*mock_dynamic_control_, FinishUpdate(_))
+ .WillByDefault(Return(true));
+ ON_CALL(*mock_dynamic_control_, GetVirtualAbFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+ }
+
+ // Setup an action processor and run the PostinstallRunnerAction with a single
+ // partition |device_path|, running the |postinstall_program| command from
+ // there.
+ void RunPostinstallAction(bool powerwash_required, bool save_rollback_data);
+
+ void RunPostinstallActionWithInstallPlan(const InstallPlan& install_plan);
+
+ public:
+ void ResumeRunningAction() {
+ ASSERT_NE(nullptr, postinstall_action_);
+ postinstall_action_->ResumeAction();
+ }
+
+ protected:
+ base::MessageLoopForIO base_loop_;
+ brillo::BaseMessageLoop loop_{&base_loop_};
+
+ FakeBootControl fake_boot_control_;
+ FakeHardware fake_hardware_;
+ MockDynamicPartitionControl* mock_dynamic_control_;
+ PostinstActionProcessorDelegate processor_delegate_;
+
+ // The PostinstallRunnerAction delegate receiving the progress updates.
+ PostinstallRunnerAction::DelegateInterface* setup_action_delegate_{nullptr};
+
+ // A pointer to the posinstall_runner action and the processor.
+ PostinstallRunnerAction* postinstall_action_{nullptr};
+ ActionProcessor* processor_{nullptr};
+};
+
+void PostinstallRunnerActionTest::RunPostinstallAction(
+ bool powerwash_required, bool save_rollback_data) {
+ InstallPlan::Partition part;
+ part.name = "part";
+ part.target_path = "/dev/invalid";
+ part.readonly_target_path = "/dev/invalid";
+ part.run_postinstall = false;
+ part.postinstall_path.clear();
+ InstallPlan install_plan;
+ install_plan.partitions = {part};
+ install_plan.download_url = "http://127.0.0.1:8080/update";
+ install_plan.powerwash_required = powerwash_required;
+ RunPostinstallActionWithInstallPlan(install_plan);
+}
+
+void PostinstallRunnerActionTest::RunPostinstallActionWithInstallPlan(
+ const chromeos_update_engine::InstallPlan& install_plan) {
+ ActionProcessor processor;
+ processor_ = &processor;
+ auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
+ feeder_action->set_obj(install_plan);
+ auto runner_action = std::make_unique<PostinstallRunnerAction>(
+ &fake_boot_control_, &fake_hardware_);
+ postinstall_action_ = runner_action.get();
+ base::FilePath temp_dir;
+ TEST_AND_RETURN(base::CreateNewTempDirectory("postinstall", &temp_dir));
+ postinstall_action_->SetMountDir(temp_dir.value());
+ runner_action->set_delegate(setup_action_delegate_);
+ BondActions(feeder_action.get(), runner_action.get());
+ auto collector_action =
+ std::make_unique<ObjectCollectorAction<InstallPlan>>();
+ BondActions(runner_action.get(), collector_action.get());
+ processor.EnqueueAction(std::move(feeder_action));
+ processor.EnqueueAction(std::move(runner_action));
+ processor.EnqueueAction(std::move(collector_action));
+ processor.set_delegate(&processor_delegate_);
+
+ loop_.PostTask(
+ FROM_HERE,
+ base::Bind(
+ [](ActionProcessor* processor) { processor->StartProcessing(); },
+ base::Unretained(&processor)));
+ loop_.Run();
+ ASSERT_FALSE(processor.IsRunning());
+ postinstall_action_ = nullptr;
+ processor_ = nullptr;
+ ASSERT_TRUE(processor_delegate_.processing_stopped_called_ ||
+ processor_delegate_.processing_done_called_);
+ if (processor_delegate_.processing_done_called_) {
+ // Validation check that the code was set when the processor finishes.
+ ASSERT_TRUE(processor_delegate_.code_set_);
+ }
+}
+
+// Test that postinstall succeeds in the simple case of running the default
+// /postinst command which only exits 0.
+TEST_F(PostinstallRunnerActionTest, RunAsRootSimpleTest) {
+ EXPECT_CALL(*mock_dynamic_control_, GetVirtualAbFeatureFlag())
+ .WillOnce(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+ RunPostinstallAction(false, false);
+ ASSERT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
+ ASSERT_TRUE(processor_delegate_.processing_done_called_);
+
+ // Since powerwash_required was false, this should not trigger a powerwash.
+ ASSERT_FALSE(fake_hardware_.IsPowerwashScheduled());
+ ASSERT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index 028402a..8579144 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -23,6 +23,7 @@
#include <memory>
#include <string>
#include <utility>
+#include "common/dynamic_partition_control_interface.h"
#include <base/bind.h>
#include <base/files/file_util.h>
@@ -44,10 +45,14 @@
#include "update_engine/common/subprocess.h"
#include "update_engine/common/test_utils.h"
#include "update_engine/common/utils.h"
+#include "update_engine/common/mock_dynamic_partition_control.h"
using brillo::MessageLoop;
using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
using std::string;
+using testing::_;
+using testing::AtLeast;
+using testing::Return;
namespace chromeos_update_engine {
@@ -95,6 +100,21 @@
// stored in the "disk_ext2_unittest.img" image.
postinstall_image_ =
test_utils::GetBuildArtifactsPath("gen/disk_ext2_unittest.img");
+ {
+ auto mock_dynamic_control =
+ std::make_unique<MockDynamicPartitionControl>();
+ mock_dynamic_control_ = mock_dynamic_control.get();
+ fake_boot_control_.SetDynamicPartitionControl(
+ std::move(mock_dynamic_control));
+ }
+ ON_CALL(*mock_dynamic_control_, FinishUpdate(_))
+ .WillByDefault(Return(true));
+ ON_CALL(*mock_dynamic_control_, MapAllPartitions())
+ .WillByDefault(Return(true));
+ ON_CALL(*mock_dynamic_control_, UnmapAllPartitions())
+ .WillByDefault(Return(true));
+ ON_CALL(*mock_dynamic_control_, GetVirtualAbFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
}
// Setup an action processor and run the PostinstallRunnerAction with a single
@@ -173,6 +193,7 @@
FakeBootControl fake_boot_control_;
FakeHardware fake_hardware_;
+ MockDynamicPartitionControl* mock_dynamic_control_;
PostinstActionProcessorDelegate processor_delegate_;
// The PostinstallRunnerAction delegate receiving the progress updates.
@@ -398,6 +419,7 @@
// Test that we can cancel a postinstall action while it is running.
TEST_F(PostinstallRunnerActionTest, RunAsRootCancelPostinstallActionTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
+ EXPECT_CALL(*mock_dynamic_control_, MapAllPartitions()).Times(AtLeast(1));
// Wait for the action to start and then cancel it.
CancelWhenStarted();
@@ -411,6 +433,10 @@
// Test that we parse and process the progress reports from the progress
// file descriptor.
TEST_F(PostinstallRunnerActionTest, RunAsRootProgressUpdatesTest) {
+ EXPECT_CALL(*mock_dynamic_control_, MapAllPartitions())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_dynamic_control_, FinishUpdate(_)).Times(AtLeast(1));
testing::StrictMock<MockPostinstallRunnerActionDelegate> mock_delegate_;
testing::InSequence s;
EXPECT_CALL(mock_delegate_, ProgressUpdate(0));
diff --git a/payload_generator/full_update_generator.cc b/payload_generator/full_update_generator.cc
index 2491f76..5362525 100644
--- a/payload_generator/full_update_generator.cc
+++ b/payload_generator/full_update_generator.cc
@@ -17,11 +17,8 @@
#include "update_engine/payload_generator/full_update_generator.h"
#include <fcntl.h>
-#include <inttypes.h>
#include <algorithm>
-#include <deque>
-#include <memory>
#include <base/format_macros.h>
#include <android-base/stringprintf.h>
@@ -98,7 +95,7 @@
fd_, buffer_in_.data(), buffer_in_.size(), offset_, &bytes_read));
TEST_AND_RETURN_FALSE(bytes_read == static_cast<ssize_t>(size_));
- InstallOperation::Type op_type;
+ InstallOperation::Type op_type{};
TEST_AND_RETURN_FALSE(diff_utils::GenerateBestFullOperation(
buffer_in_, version_, &op_blob, &op_type));
@@ -122,7 +119,7 @@
// For performance reasons, we force a small default hard limit of 1 MiB. This
// limit can be changed in the config, and we will use the smaller of the two
// soft/hard limits.
- size_t full_chunk_size;
+ size_t full_chunk_size{};
if (config.hard_chunk_size >= 0) {
full_chunk_size = std::min(static_cast<size_t>(config.hard_chunk_size),
config.soft_chunk_size);
diff --git a/scripts/Android.bp b/scripts/Android.bp
index e86a9f2..b4d8539 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -68,12 +68,4 @@
libs: [
"update_payload",
],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- },
- },
}