Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 19737a5b authored by Tianjie Xu's avatar Tianjie Xu Committed by Gerrit Code Review
Browse files

Merge "Add Updater class and remove UpdaterInfo"

parents 9423d2f6 58d59129
Loading
Loading
Loading
Loading
+86 −92
Original line number Diff line number Diff line
@@ -57,16 +57,14 @@ using namespace std::string_literals;

using PackageEntries = std::unordered_map<std::string, std::string>;

struct selabel_handle* sehandle = nullptr;

static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code,
                   UpdaterInfo* info = nullptr) {
                   Updater* updater = nullptr) {
  std::unique_ptr<Expr> e;
  int error_count = 0;
  ASSERT_EQ(0, ParseString(expr_str, &e, &error_count));
  ASSERT_EQ(0, error_count);

  State state(expr_str, info);
  State state(expr_str, updater);

  std::string result;
  bool status = Evaluate(&state, e, &result);
@@ -102,38 +100,6 @@ static void BuildUpdatePackage(const PackageEntries& entries, int fd) {
  ASSERT_EQ(0, fclose(zip_file_ptr));
}

static void RunBlockImageUpdate(bool is_verify, const PackageEntries& entries,
                                const std::string& image_file, const std::string& result,
                                CauseCode cause_code = kNoCause) {
  CHECK(entries.find("transfer_list") != entries.end());

  // Build the update package.
  TemporaryFile zip_file;
  BuildUpdatePackage(entries, zip_file.release());

  MemMapping map;
  ASSERT_TRUE(map.MapFile(zip_file.path));
  ZipArchiveHandle handle;
  ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle));

  // Set up the handler, command_pipe, patch offset & length.
  UpdaterInfo updater_info;
  updater_info.package_zip = handle;
  TemporaryFile temp_pipe;
  updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe");
  updater_info.package_zip_addr = map.addr;
  updater_info.package_zip_len = map.length;

  std::string new_data = entries.find("new_data.br") != entries.end() ? "new_data.br" : "new_data";
  std::string script = is_verify ? "block_image_verify" : "block_image_update";
  script += R"((")" + image_file + R"(", package_extract_file("transfer_list"), ")" + new_data +
            R"(", "patch_data"))";
  expect(result.c_str(), script, cause_code, &updater_info);

  ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
  CloseArchive(handle);
}

static std::string GetSha1(std::string_view content) {
  uint8_t digest[SHA_DIGEST_LENGTH];
  SHA1(reinterpret_cast<const uint8_t*>(content.data()), content.size(), digest);
@@ -159,29 +125,24 @@ static Value* BlobToString(const char* name, State* state,
  return args[0].release();
}

class UpdaterTest : public ::testing::Test {
class UpdaterTestBase {
 protected:
  void SetUp() override {
  void SetUp() {
    RegisterBuiltins();
    RegisterInstallFunctions();
    RegisterBlockImageFunctions();

    RegisterFunction("blob_to_string", BlobToString);

    // Each test is run in a separate process (isolated mode). Shared temporary files won't cause
    // conflicts.
    Paths::Get().set_cache_temp_source(temp_saved_source_.path);
    Paths::Get().set_last_command_file(temp_last_command_.path);
    Paths::Get().set_stash_directory_base(temp_stash_base_.path);

    // Enable a special command "abort" to simulate interruption.
    Command::abort_allowed_ = true;

    last_command_file_ = temp_last_command_.path;
    image_file_ = image_temp_file_.path;
  }

  void TearDown() override {
  void TearDown() {
    // Clean up the last_command_file if any.
    ASSERT_TRUE(android::base::RemoveFileIfExists(last_command_file_));

@@ -191,16 +152,80 @@ class UpdaterTest : public ::testing::Test {
    ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker));
  }

  void RunBlockImageUpdate(bool is_verify, PackageEntries entries, const std::string& image_file,
                           const std::string& result, CauseCode cause_code = kNoCause) {
    CHECK(entries.find("transfer_list") != entries.end());
    std::string new_data =
        entries.find("new_data.br") != entries.end() ? "new_data.br" : "new_data";
    std::string script = is_verify ? "block_image_verify" : "block_image_update";
    script += R"((")" + image_file + R"(", package_extract_file("transfer_list"), ")" + new_data +
              R"(", "patch_data"))";
    entries.emplace(Updater::SCRIPT_NAME, script);

    // Build the update package.
    TemporaryFile zip_file;
    BuildUpdatePackage(entries, zip_file.release());

    // Set up the handler, command_pipe, patch offset & length.
    TemporaryFile temp_pipe;
    ASSERT_TRUE(updater_.Init(temp_pipe.release(), zip_file.path, false, nullptr));
    ASSERT_TRUE(updater_.RunUpdate());
    ASSERT_EQ(result, updater_.result());

    // Parse the cause code written to the command pipe.
    int received_cause_code = kNoCause;
    std::string pipe_content;
    ASSERT_TRUE(android::base::ReadFileToString(temp_pipe.path, &pipe_content));
    auto lines = android::base::Split(pipe_content, "\n");
    for (std::string_view line : lines) {
      if (android::base::ConsumePrefix(&line, "log cause: ")) {
        ASSERT_TRUE(android::base::ParseInt(line.data(), &received_cause_code));
      }
    }
    ASSERT_EQ(cause_code, received_cause_code);
  }

  TemporaryFile temp_saved_source_;
  TemporaryDir temp_stash_base_;
  std::string last_command_file_;
  std::string image_file_;

  Updater updater_;

 private:
  TemporaryFile temp_last_command_;
  TemporaryFile image_temp_file_;
};

class UpdaterTest : public UpdaterTestBase, public ::testing::Test {
 protected:
  void SetUp() override {
    UpdaterTestBase::SetUp();

    RegisterFunction("blob_to_string", BlobToString);
    // Enable a special command "abort" to simulate interruption.
    Command::abort_allowed_ = true;
  }

  void TearDown() override {
    UpdaterTestBase::TearDown();
  }

  void SetUpdaterCmdPipe(int fd) {
    FILE* cmd_pipe = fdopen(fd, "w");
    ASSERT_NE(nullptr, cmd_pipe);
    updater_.cmd_pipe_.reset(cmd_pipe);
  }

  void SetUpdaterOtaPackageHandle(ZipArchiveHandle handle) {
    updater_.package_handle_ = handle;
  }

  void FlushUpdaterCommandPipe() const {
    fflush(updater_.cmd_pipe_.get());
  }
};

TEST_F(UpdaterTest, getprop) {
    expect(android::base::GetProperty("ro.product.device", "").c_str(),
           "getprop(\"ro.product.device\")",
@@ -317,13 +342,12 @@ TEST_F(UpdaterTest, package_extract_file) {
  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));

  // Need to set up the ziphandle.
  UpdaterInfo updater_info;
  updater_info.package_zip = handle;
  SetUpdaterOtaPackageHandle(handle);

  // Two-argument version.
  TemporaryFile temp_file1;
  std::string script("package_extract_file(\"a.txt\", \"" + std::string(temp_file1.path) + "\")");
  expect("t", script, kNoCause, &updater_info);
  expect("t", script, kNoCause, &updater_);

  // Verify the extracted entry.
  std::string data;
@@ -332,32 +356,30 @@ TEST_F(UpdaterTest, package_extract_file) {

  // Now extract another entry to the same location, which should overwrite.
  script = "package_extract_file(\"b.txt\", \"" + std::string(temp_file1.path) + "\")";
  expect("t", script, kNoCause, &updater_info);
  expect("t", script, kNoCause, &updater_);

  ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data));
  ASSERT_EQ(kBTxtContents, data);

  // Missing zip entry. The two-argument version doesn't abort.
  script = "package_extract_file(\"doesntexist\", \"" + std::string(temp_file1.path) + "\")";
  expect("", script, kNoCause, &updater_info);
  expect("", script, kNoCause, &updater_);

  // Extract to /dev/full should fail.
  script = "package_extract_file(\"a.txt\", \"/dev/full\")";
  expect("", script, kNoCause, &updater_info);
  expect("", script, kNoCause, &updater_);

  // One-argument version. package_extract_file() gives a VAL_BLOB, which needs to be converted to
  // VAL_STRING for equality test.
  script = "blob_to_string(package_extract_file(\"a.txt\")) == \"" + kATxtContents + "\"";
  expect("t", script, kNoCause, &updater_info);
  expect("t", script, kNoCause, &updater_);

  script = "blob_to_string(package_extract_file(\"b.txt\")) == \"" + kBTxtContents + "\"";
  expect("t", script, kNoCause, &updater_info);
  expect("t", script, kNoCause, &updater_);

  // Missing entry. The one-argument version aborts the evaluation.
  script = "package_extract_file(\"doesntexist\")";
  expect(nullptr, script, kPackageExtractFileFailure, &updater_info);

  CloseArchive(handle);
  expect(nullptr, script, kPackageExtractFileFailure, &updater_);
}

TEST_F(UpdaterTest, read_file) {
@@ -563,17 +585,15 @@ TEST_F(UpdaterTest, set_progress) {
  expect(nullptr, "set_progress(\".3.5\")", kArgsParsingFailure);

  TemporaryFile tf;
  UpdaterInfo updater_info;
  updater_info.cmd_pipe = fdopen(tf.release(), "w");
  expect(".52", "set_progress(\".52\")", kNoCause, &updater_info);
  fflush(updater_info.cmd_pipe);
  SetUpdaterCmdPipe(tf.release());
  expect(".52", "set_progress(\".52\")", kNoCause, &updater_);
  FlushUpdaterCommandPipe();

  std::string cmd;
  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd));
  ASSERT_EQ(android::base::StringPrintf("set_progress %f\n", .52), cmd);
  // recovery-updater protocol expects 2 tokens ("set_progress <frac>").
  ASSERT_EQ(2U, android::base::Split(cmd, " ").size());
  ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
}

TEST_F(UpdaterTest, show_progress) {
@@ -588,17 +608,15 @@ TEST_F(UpdaterTest, show_progress) {
  expect(nullptr, "show_progress(\".3\", \"5a\")", kArgsParsingFailure);

  TemporaryFile tf;
  UpdaterInfo updater_info;
  updater_info.cmd_pipe = fdopen(tf.release(), "w");
  expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_info);
  fflush(updater_info.cmd_pipe);
  SetUpdaterCmdPipe(tf.release());
  expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_);
  FlushUpdaterCommandPipe();

  std::string cmd;
  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd));
  ASSERT_EQ(android::base::StringPrintf("progress %f %d\n", .52, 10), cmd);
  // recovery-updater protocol expects 3 tokens ("progress <frac> <secs>").
  ASSERT_EQ(3U, android::base::Split(cmd, " ").size());
  ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
}

TEST_F(UpdaterTest, block_image_update_parsing_error) {
@@ -993,44 +1011,20 @@ TEST_F(UpdaterTest, last_command_verify) {
  ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK));
}

class ResumableUpdaterTest : public testing::TestWithParam<size_t> {
class ResumableUpdaterTest : public UpdaterTestBase, public testing::TestWithParam<size_t> {
 protected:
  void SetUp() override {
    RegisterBuiltins();
    RegisterInstallFunctions();
    RegisterBlockImageFunctions();

    Paths::Get().set_cache_temp_source(temp_saved_source_.path);
    Paths::Get().set_last_command_file(temp_last_command_.path);
    Paths::Get().set_stash_directory_base(temp_stash_base_.path);

    UpdaterTestBase::SetUp();
    // Enable a special command "abort" to simulate interruption.
    Command::abort_allowed_ = true;

    index_ = GetParam();
    image_file_ = image_temp_file_.path;
    last_command_file_ = temp_last_command_.path;
  }

  void TearDown() override {
    // Clean up the last_command_file if any.
    ASSERT_TRUE(android::base::RemoveFileIfExists(last_command_file_));

    // Clear partition updated marker if any.
    std::string updated_marker{ temp_stash_base_.path };
    updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED";
    ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker));
    UpdaterTestBase::TearDown();
  }

  TemporaryFile temp_saved_source_;
  TemporaryDir temp_stash_base_;
  std::string last_command_file_;
  std::string image_file_;
  size_t index_;

 private:
  TemporaryFile temp_last_command_;
  TemporaryFile image_temp_file_;
};

static std::string g_source_image;
+1 −0
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ cc_library_static {
        "commands.cpp",
        "dynamic_partitions.cpp",
        "install.cpp",
        "updater.cpp",
    ],

    include_dirs: [
+1 −1
Original line number Diff line number Diff line
@@ -59,7 +59,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := updater

LOCAL_SRC_FILES := \
    updater.cpp
    updater_main.cpp

LOCAL_C_INCLUDES := \
    $(LOCAL_PATH)/include
+21 −19
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <applypatch/applypatch.h>
@@ -1668,15 +1669,9 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
    return StringValue("");
  }

  UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
  if (ui == nullptr) {
    return StringValue("");
  }

  FILE* cmd_pipe = ui->cmd_pipe;
  ZipArchiveHandle za = ui->package_zip;

  if (cmd_pipe == nullptr || za == nullptr) {
  auto updater = static_cast<Updater*>(state->cookie);
  ZipArchiveHandle za = updater->package_handle();
  if (za == nullptr) {
    return StringValue("");
  }

@@ -1686,8 +1681,8 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
    LOG(ERROR) << name << "(): no file \"" << patch_data_fn->data << "\" in package";
    return StringValue("");
  }
  params.patch_start = updater->GetMappedPackageAddress() + patch_entry.offset;

  params.patch_start = ui->package_zip_addr + patch_entry.offset;
  std::string_view new_data(new_data_fn->data);
  ZipEntry new_entry;
  if (FindEntry(za, new_data, &new_entry) != 0) {
@@ -1887,8 +1882,10 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
        LOG(WARNING) << "Failed to update the last command file.";
      }

      fprintf(cmd_pipe, "set_progress %.4f\n", static_cast<double>(params.written) / total_blocks);
      fflush(cmd_pipe);
      updater->WriteToCommandPipe(
          android::base::StringPrintf("set_progress %.4f",
                                      static_cast<double>(params.written) / total_blocks),
          true);
    }
  }

@@ -1915,11 +1912,13 @@ pbiudone:

      const char* partition = strrchr(blockdev_filename->data.c_str(), '/');
      if (partition != nullptr && *(partition + 1) != 0) {
        fprintf(cmd_pipe, "log bytes_written_%s: %" PRIu64 "\n", partition + 1,
                static_cast<uint64_t>(params.written) * BLOCKSIZE);
        fprintf(cmd_pipe, "log bytes_stashed_%s: %" PRIu64 "\n", partition + 1,
                static_cast<uint64_t>(params.stashed) * BLOCKSIZE);
        fflush(cmd_pipe);
        updater->WriteToCommandPipe(
            android::base::StringPrintf("log bytes_written_%s: %" PRIu64, partition + 1,
                                        static_cast<uint64_t>(params.written) * BLOCKSIZE));
        updater->WriteToCommandPipe(
            android::base::StringPrintf("log bytes_stashed_%s: %" PRIu64, partition + 1,
                                        static_cast<uint64_t>(params.stashed) * BLOCKSIZE),
            true);
      }
      // Delete stash only after successfully completing the update, as it may contain blocks needed
      // to complete the update later.
@@ -2172,8 +2171,11 @@ Value* CheckFirstBlockFn(const char* name, State* state,
  uint16_t mount_count = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x34]);

  if (mount_count > 0) {
    uiPrintf(state, "Device was remounted R/W %" PRIu16 " times", mount_count);
    uiPrintf(state, "Last remount happened on %s", ctime(&mount_time));
    auto updater = static_cast<Updater*>(state->cookie);
    updater->UiPrint(
        android::base::StringPrintf("Device was remounted R/W %" PRIu16 " times", mount_count));
    updater->UiPrint(
        android::base::StringPrintf("Last remount happened on %s", ctime(&mount_time)));
  }

  return StringValue("t");
+1 −10
Original line number Diff line number Diff line
@@ -14,15 +14,6 @@
 * limitations under the License.
 */

#ifndef _UPDATER_INSTALL_H_
#define _UPDATER_INSTALL_H_

struct State;
#pragma once

void RegisterInstallFunctions();

// uiPrintf function prints msg to screen as well as logs
void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...)
    __attribute__((__format__(printf, 2, 3)));

#endif
Loading