Implement trigger postinstall API
Test: adb shell update_engine_client --trigger_postinstall=system
Bug: 377557752
Change-Id: Ieb28e86bd979502c5b208cd8df917f3e49a50f0b
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
index f29383a..89c79f3 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"
@@ -546,6 +548,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 +647,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__,
@@ -1454,16 +1464,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) {