blob: de42623dc0cdf7156606d600990ee3f5dd080fa7 [file] [log] [blame]
//
// Copyright (C) 2021 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 <memory>
#include <string>
#include <utility>
#include <sys/fcntl.h>
#include <unistd.h>
#include <brillo/data_encoding.h>
#include <brillo/message_loops/fake_message_loop.h>
#include <gtest/gtest.h>
#include <liblp/builder.h>
#include <fs_mgr.h>
#include <liblp/liblp.h>
#include "update_engine/aosp/boot_control_android.h"
#include "update_engine/aosp/daemon_state_android.h"
#include "update_engine/aosp/update_attempter_android.h"
#include "update_engine/common/constants.h"
#include "update_engine/common/prefs.h"
#include "update_engine/common/testing_constants.h"
#include "update_engine/common/hash_calculator.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/payload_consumer/payload_constants.h"
#include "update_engine/payload_consumer/install_plan.h"
#include "update_engine/payload_generator/delta_diff_generator.h"
#include "update_engine/payload_generator/extent_ranges.h"
#include "update_engine/payload_generator/payload_file.h"
#include "update_engine/payload_generator/payload_signer.h"
#include "update_engine/update_metadata.pb.h"
#include "update_engine/update_status_utils.h"
namespace chromeos_update_engine {
class UpdateAttempterAndroidIntegrationTest : public ::testing::Test,
public ServiceObserverInterface {
void AddFakePartitionGroup() {
dynamic_control_ = boot_control_.dynamic_control_.get();
auto super_device = dynamic_control_->GetSuperDevice();
ASSERT_TRUE(super_device.has_value());
super_device_ = super_device->value();
builder_ = android::fs_mgr::MetadataBuilder::New(
super_device->value(), boot_control_.GetCurrentSlot());
ASSERT_NE(builder_, nullptr);
// Remove dangling fake partitions, if test crashed before they might not
// get cleaned up properly.
RemoveFakePartitionGroup();
ASSERT_TRUE(builder_->AddGroup("fake_group", kFakePartitionSize * 2));
ExportPartitionTable();
}
void RemoveFakePartitionGroup() {
builder_->RemovePartition("fake_a");
builder_->RemovePartition("fake_b");
builder_->RemoveGroupAndPartitions("fake_group");
}
void SetUp() override {
ASSERT_TRUE(boot_control_.Init());
if (!DynamicPartitionEnabled()) {
return;
}
ASSERT_NO_FATAL_FAILURE(AddFakePartitionGroup());
message_loop_.SetAsCurrent();
// Set official build to false so that hash checks are non-mandatory
hardware_.SetIsOfficialBuild(false);
truncate64(blob_file_.path().c_str(), 0);
update_attempter_android_.set_update_certificates_path(
test_utils::GetBuildArtifactsPath(kUnittestOTACertsPath));
// Basic setup to create VABC OTA
manifest_.set_partial_update(true);
manifest_.set_minor_version(kPartialUpdateMinorPayloadVersion);
auto dap_group =
manifest_.mutable_dynamic_partition_metadata()->add_groups();
dap_group->set_name("fake_group");
dap_group->add_partition_names("fake");
manifest_.mutable_dynamic_partition_metadata()->set_snapshot_enabled(true);
manifest_.mutable_dynamic_partition_metadata()->set_vabc_enabled(true);
manifest_.mutable_dynamic_partition_metadata()->set_cow_version(
android::snapshot::kCowVersionMajor);
}
void TearDown() override {
if (!builder_ || !DynamicPartitionEnabled()) {
return;
}
auto dynamic_control = boot_control_.GetDynamicPartitionControl();
if (dynamic_control) {
dynamic_control->UnmapAllPartitions();
dynamic_control->ResetUpdate(&prefs_);
}
RemoveFakePartitionGroup();
ExportPartitionTable();
}
void CreateFakePartition() {
// Create a fake partition for testing purposes
auto partition_a = builder_->AddPartition("fake_a", "fake_group", 0);
ASSERT_NE(partition_a, nullptr);
ASSERT_TRUE(builder_->ResizePartition(partition_a, kFakePartitionSize));
ExportPartitionTable();
}
void SendStatusUpdate(
const update_engine::UpdateEngineStatus& update_engine_status) override {
LOG(INFO) << UpdateStatusToString(update_engine_status.status) << ", "
<< update_engine_status.progress;
}
// Called whenever an update attempt is completed.
void SendPayloadApplicationComplete(ErrorCode error_code) override {
completion_code_ = error_code;
}
void ExportPartitionTable() {
auto metadata = builder_->Export();
ASSERT_NE(metadata, nullptr);
android::fs_mgr::UpdatePartitionTable(
super_device_, *metadata, boot_control_.GetCurrentSlot());
}
public:
bool DynamicPartitionEnabled() {
auto dynamic_control = boot_control_.GetDynamicPartitionControl();
return dynamic_control &&
dynamic_control->GetDynamicPartitionsFeatureFlag().IsEnabled();
}
void AddSignatureInfoToPayload(DeltaArchiveManifest* manifest,
const std::string& private_key_path) {
size_t total_blob_size = 0;
for (const auto& part : manifest->partitions()) {
for (const auto& op : part.operations()) {
if (!op.has_data_offset())
continue;
ASSERT_EQ(total_blob_size, op.data_offset())
<< "Ops not ordered by blob isze";
total_blob_size += op.data_length();
}
}
// Signatures appear at the end of the blobs. Note the offset in the
// |manifest_|.
uint64_t signature_blob_length = 0;
if (!private_key_path.empty()) {
ASSERT_TRUE(PayloadSigner::SignatureBlobLength({private_key_path},
&signature_blob_length));
PayloadSigner::AddSignatureToManifest(
total_blob_size, signature_blob_length, manifest);
}
}
void ApplyPayload(DeltaArchiveManifest* manifest) {
ASSERT_FALSE(manifest->partitions().empty());
if (manifest->partitions().begin()->has_old_partition_info()) {
// Only create fake partition if the update is incremental
LOG(INFO) << "Creating fake partition";
ASSERT_NO_FATAL_FAILURE(CreateFakePartition());
}
const auto private_key_path =
test_utils::GetBuildArtifactsPath(kUnittestPrivateKeyPath);
ASSERT_NO_FATAL_FAILURE(
AddSignatureInfoToPayload(manifest, private_key_path));
brillo::Blob hash;
HashCalculator::RawHashOfFile(new_part_.path(), &hash);
auto partition = &manifest->mutable_partitions()->at(0);
partition->mutable_new_partition_info()->set_size(kFakePartitionSize);
partition->mutable_new_partition_info()->set_hash(hash.data(), hash.size());
uint64_t metadata_size = 0;
ASSERT_TRUE(PayloadFile::WritePayload(payload_file_.path(),
blob_file_.path(),
private_key_path,
kBrilloMajorPayloadVersion,
*manifest,
&metadata_size));
LOG(INFO) << "Signature offset: " << manifest->signatures_offset()
<< ", Signature size: " << manifest->signatures_size();
brillo::ErrorPtr error;
HashCalculator::RawHashOfFile(payload_file_.path(), &hash);
daemon_state_.AddObserver(this);
ASSERT_TRUE(update_attempter_android_.ApplyPayload(
"file://" + payload_file_.path(),
0,
utils::FileSize(payload_file_.path()),
{kPayloadPropertyMetadataSize + ("=" + std::to_string(metadata_size)),
kPayloadPropertyFileHash +
("=" + brillo::data_encoding::Base64Encode(hash))},
&error));
brillo::MessageLoop::current()->Run();
if (error) {
LOG(ERROR) << error->GetMessage();
}
ASSERT_EQ(error, nullptr);
ASSERT_EQ(completion_code_, ErrorCode::kSuccess);
}
// use 25MB max to avoid super not having enough space
static constexpr size_t kFakePartitionSize = 1024 * 1024 * 25;
static_assert(kFakePartitionSize % kBlockSize == 0);
BootControlAndroid boot_control_;
std::unique_ptr<android::fs_mgr::MetadataBuilder> builder_;
std::string super_device_;
FakeHardware hardware_;
ScopedTempFile payload_file_;
ScopedTempFile blob_file_;
// Contains expected data for old and new partition
ScopedTempFile old_part_{"old_part.XXXXXX", true, kFakePartitionSize};
ScopedTempFile new_part_{"new_part.XXXXXX", true, kFakePartitionSize};
DaemonStateAndroid daemon_state_;
MemoryPrefs prefs_;
ErrorCode completion_code_;
DynamicPartitionControlAndroid* dynamic_control_{nullptr};
brillo::FakeMessageLoop message_loop_{nullptr};
DeltaArchiveManifest manifest_;
UpdateAttempterAndroid update_attempter_android_{
&daemon_state_, &prefs_, &boot_control_, &hardware_, nullptr};
};
namespace {
TEST_F(UpdateAttempterAndroidIntegrationTest, NewPartitionTest) {
if (!DynamicPartitionEnabled()) {
return;
}
auto partition = manifest_.add_partitions();
partition->set_partition_name("fake");
partition->set_estimate_cow_size(kFakePartitionSize);
{
auto op = partition->add_operations();
op->set_type(InstallOperation::REPLACE);
*op->add_dst_extents() = ExtentForRange(0, 1);
op->set_data_offset(0);
op->set_data_length(kBlockSize);
truncate(blob_file_.path().c_str(), kBlockSize);
}
{
auto op = partition->add_operations();
op->set_type(InstallOperation::ZERO);
*op->add_dst_extents() =
ExtentForRange(1, kFakePartitionSize / kBlockSize - 1);
}
ApplyPayload(&manifest_);
}
} // namespace
} // namespace chromeos_update_engine