Loading tests/unit/updater_test.cpp +86 −92 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading @@ -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_)); Loading @@ -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\")", Loading Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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) { Loading Loading @@ -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; Loading updater/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ cc_library_static { "commands.cpp", "dynamic_partitions.cpp", "install.cpp", "updater.cpp", ], include_dirs: [ Loading updater/Android.mk +1 −1 Original line number Diff line number Diff line Loading @@ -59,7 +59,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := updater LOCAL_SRC_FILES := \ updater.cpp updater_main.cpp LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include Loading updater/blockimg.cpp +21 −19 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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(""); } Loading @@ -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) { Loading Loading @@ -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); } } Loading @@ -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. Loading Loading @@ -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"); Loading updater/include/updater/install.h +1 −10 Original line number Diff line number Diff line Loading @@ -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
tests/unit/updater_test.cpp +86 −92 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading @@ -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_)); Loading @@ -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\")", Loading Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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) { Loading Loading @@ -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; Loading
updater/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ cc_library_static { "commands.cpp", "dynamic_partitions.cpp", "install.cpp", "updater.cpp", ], include_dirs: [ Loading
updater/Android.mk +1 −1 Original line number Diff line number Diff line Loading @@ -59,7 +59,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := updater LOCAL_SRC_FILES := \ updater.cpp updater_main.cpp LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include Loading
updater/blockimg.cpp +21 −19 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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(""); } Loading @@ -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) { Loading Loading @@ -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); } } Loading @@ -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. Loading Loading @@ -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"); Loading
updater/include/updater/install.h +1 −10 Original line number Diff line number Diff line Loading @@ -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