diff options
-rw-r--r-- | applypatch/Android.bp | 36 | ||||
-rw-r--r-- | edify/include/edify/updater_interface.h | 1 | ||||
-rw-r--r-- | install/adb_install.cpp | 4 | ||||
-rw-r--r-- | install/fuse_install.cpp | 7 | ||||
-rw-r--r-- | minadbd/minadbd_services.cpp | 9 | ||||
-rw-r--r-- | minadbd/minadbd_types.h | 1 | ||||
-rw-r--r-- | otautil/sysutil.cpp | 2 | ||||
-rw-r--r-- | recovery.cpp | 28 | ||||
-rw-r--r-- | recovery_ui/Android.bp | 1 | ||||
-rw-r--r-- | recovery_ui/include/recovery_ui/stub_ui.h | 6 | ||||
-rw-r--r-- | recovery_ui/include/recovery_ui/ui.h | 17 | ||||
-rw-r--r-- | recovery_ui/screen_ui.cpp | 2 | ||||
-rw-r--r-- | recovery_ui/stub_ui.cpp | 36 | ||||
-rw-r--r-- | recovery_ui/ui.cpp | 14 | ||||
-rw-r--r-- | tests/Android.bp | 22 | ||||
-rw-r--r-- | tests/unit/host/imgdiff_test.cpp (renamed from tests/unit/imgdiff_test.cpp) | 0 | ||||
-rw-r--r-- | tests/unit/host/update_simulator_test.cpp | 403 | ||||
-rw-r--r-- | updater/build_info.cpp | 2 | ||||
-rw-r--r-- | updater/include/updater/updater.h | 4 | ||||
-rw-r--r-- | updater/target_files.cpp | 9 |
20 files changed, 521 insertions, 83 deletions
diff --git a/applypatch/Android.bp b/applypatch/Android.bp index 620ca6cc9..42aa52954 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -114,11 +114,9 @@ cc_binary { ], } -cc_library_static { +cc_library_host_static { name: "libimgdiff", - host_supported: true, - defaults: [ "applypatch_defaults", ], @@ -170,35 +168,3 @@ cc_binary_host { "libz", ], } - -cc_library_static { - name: "libimgpatch", - - // The host module is for recovery_host_test (Linux only). - host_supported: true, - - defaults: [ - "applypatch_defaults", - ], - - srcs: [ - "bspatch.cpp", - "imgpatch.cpp", - ], - - static_libs: [ - "libbase", - "libbspatch", - "libbz", - "libcrypto", - "libedify", - "libotautil", - "libz", - ], - - target: { - darwin: { - enabled: false, - }, - }, -} diff --git a/edify/include/edify/updater_interface.h b/edify/include/edify/updater_interface.h index a4d581eec..aa977e3c8 100644 --- a/edify/include/edify/updater_interface.h +++ b/edify/include/edify/updater_interface.h @@ -44,4 +44,5 @@ class UpdaterInterface { virtual ZipArchiveHandle GetPackageHandle() const = 0; virtual std::string GetResult() const = 0; virtual uint8_t* GetMappedPackageAddress() const = 0; + virtual size_t GetMappedPackageLength() const = 0; }; diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 37280a34c..ed664429a 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -367,11 +367,13 @@ InstallResult ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinActi "\n\nNow send the package you want to apply\n" "to the device with \"adb sideload <filename>\"...\n"); } else { - ui->Print("\n\nWaiting for rescue commands...\n"); command_map.emplace(MinadbdCommand::kWipeData, [&device]() { bool result = WipeData(device, false); return std::make_pair(result, true); }); + command_map.emplace(MinadbdCommand::kNoOp, []() { return std::make_pair(true, true); }); + + ui->Print("\n\nWaiting for rescue commands...\n"); } CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode); diff --git a/install/fuse_install.cpp b/install/fuse_install.cpp index ffde4a348..8a7a278e0 100644 --- a/install/fuse_install.cpp +++ b/install/fuse_install.cpp @@ -128,11 +128,12 @@ static bool StartInstallPackageFuse(std::string_view path) { constexpr auto FUSE_BLOCK_SIZE = 65536; bool is_block_map = android::base::ConsumePrefix(&path, "@"); - auto file_data_reader = + auto fuse_data_provider = is_block_map ? FuseBlockDataProvider::CreateFromBlockMap(std::string(path), FUSE_BLOCK_SIZE) : FuseFileDataProvider::CreateFromFile(std::string(path), FUSE_BLOCK_SIZE); - if (!file_data_reader->Valid()) { + if (!fuse_data_provider || !fuse_data_provider->Valid()) { + LOG(ERROR) << "Failed to create fuse data provider."; return false; } @@ -142,7 +143,7 @@ static bool StartInstallPackageFuse(std::string_view path) { umount2(SDCARD_ROOT, MNT_DETACH); } - return run_fuse_sideload(std::move(file_data_reader)) == 0; + return run_fuse_sideload(std::move(fuse_data_provider)) == 0; } InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui) { diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 1e6eb3165..c31afbe06 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -41,7 +41,6 @@ #include "adb.h" #include "adb_unique_fd.h" #include "adb_utils.h" -#include "fdevent.h" #include "fuse_adb_provider.h" #include "fuse_sideload.h" #include "minadbd_types.h" @@ -190,6 +189,14 @@ static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { if (!android::base::WriteFully(sfd, result.data(), result.size())) { exit(kMinadbdHostSocketIOError); } + + // Send heartbeat signal to keep the rescue service alive. + if (!WriteCommandToFd(MinadbdCommand::kNoOp, minadbd_socket)) { + exit(kMinadbdSocketIOError); + } + if (MinadbdCommandStatus status; !WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } } // Reboots into the given target. We don't reboot directly from minadbd, but going through recovery diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h index 99fd45e83..002523f1f 100644 --- a/minadbd/minadbd_types.h +++ b/minadbd/minadbd_types.h @@ -53,6 +53,7 @@ enum class MinadbdCommand : uint32_t { kRebootRescue = 6, kWipeCache = 7, kWipeData = 8, + kNoOp = 9, // Last but invalid command. kError, diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp index a8829858d..6cd46c6a9 100644 --- a/otautil/sysutil.cpp +++ b/otautil/sysutil.cpp @@ -38,7 +38,7 @@ BlockMapData BlockMapData::ParseBlockMapFile(const std::string& block_map_path) { std::string content; if (!android::base::ReadFileToString(block_map_path, &content)) { - LOG(ERROR) << "Failed to read " << block_map_path; + PLOG(ERROR) << "Failed to read " << block_map_path; return {}; } diff --git a/recovery.cpp b/recovery.cpp index b18a8e74a..97ca0a504 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -84,6 +84,8 @@ const char* reason = nullptr; * * The arguments which may be supplied in the recovery.command file: * --update_package=path - verify install an OTA package file + * --install_with_fuse - install the update package with FUSE. This allows installation of large + * packages on LP32 builds. Since the mmap will otherwise fail due to out of memory. * --wipe_data - erase user data (and cache), then reboot * --prompt_and_wipe_data - prompt the user that data is corrupt, with their consent erase user * data (and cache), then reboot @@ -577,6 +579,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri static constexpr struct option OPTIONS[] = { { "fastboot", no_argument, nullptr, 0 }, { "fsck_unshare_blocks", no_argument, nullptr, 0 }, + { "install_with_fuse", no_argument, nullptr, 0 }, { "just_exit", no_argument, nullptr, 'x' }, { "locale", required_argument, nullptr, 0 }, { "prompt_and_wipe_data", no_argument, nullptr, 0 }, @@ -597,6 +600,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri }; const char* update_package = nullptr; + bool install_with_fuse = false; // memory map the update package by default. bool should_wipe_data = false; bool should_prompt_and_wipe_data = false; bool should_wipe_cache = false; @@ -632,6 +636,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri std::string option = OPTIONS[option_index].name; if (option == "fsck_unshare_blocks") { fsck_unshare_blocks = true; + } else if (option == "install_with_fuse") { + install_with_fuse = true; } else if (option == "locale" || option == "fastboot") { // Handled in recovery_main.cpp } else if (option == "prompt_and_wipe_data") { @@ -737,10 +743,24 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri } else { ensure_path_mounted(update_package); } - // TODO(xunchang) install package from fuse for large packages on ILP32 builds. - auto package = Package::CreateMemoryPackage( - update_package, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); - status = InstallPackage(package.get(), update_package, should_wipe_cache, retry_count, ui); + + if (install_with_fuse) { + LOG(INFO) << "Installing package " << update_package << " with fuse"; + status = InstallWithFuseFromPath(update_package, ui); + } else if (auto memory_package = Package::CreateMemoryPackage( + update_package, + std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); + memory_package != nullptr) { + status = InstallPackage(memory_package.get(), update_package, should_wipe_cache, + retry_count, ui); + } else { + // We may fail to memory map the package on 32 bit builds for packages with 2GiB+ size. + // In such cases, we will try to install the package with fuse. This is not the default + // installation method because it introduces a layer of indirection from the kernel space. + LOG(WARNING) << "Failed to memory map package " << update_package + << "; falling back to install with fuse"; + status = InstallWithFuseFromPath(update_package, ui); + } if (status != INSTALL_SUCCESS) { ui->Print("Installation aborted.\n"); diff --git a/recovery_ui/Android.bp b/recovery_ui/Android.bp index ee3149d5e..149ef8acc 100644 --- a/recovery_ui/Android.bp +++ b/recovery_ui/Android.bp @@ -23,6 +23,7 @@ cc_library { srcs: [ "device.cpp", "screen_ui.cpp", + "stub_ui.cpp", "ui.cpp", "vr_ui.cpp", "wear_ui.cpp", diff --git a/recovery_ui/include/recovery_ui/stub_ui.h b/recovery_ui/include/recovery_ui/stub_ui.h index fb1d8c7a6..511b1314a 100644 --- a/recovery_ui/include/recovery_ui/stub_ui.h +++ b/recovery_ui/include/recovery_ui/stub_ui.h @@ -62,11 +62,9 @@ class StubRecoveryUI : public RecoveryUI { // menu display size_t ShowMenu(const std::vector<std::string>& /* headers */, - const std::vector<std::string>& /* items */, size_t initial_selection, + const std::vector<std::string>& /* items */, size_t /* initial_selection */, bool /* menu_only */, - const std::function<int(int, bool)>& /* key_handler */) override { - return initial_selection; - } + const std::function<int(int, bool)>& /* key_handler */) override; size_t ShowPromptWipeDataMenu(const std::vector<std::string>& /* backup_headers */, const std::vector<std::string>& /* backup_items */, diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h index 797e2f0d5..a95f935e4 100644 --- a/recovery_ui/include/recovery_ui/ui.h +++ b/recovery_ui/include/recovery_ui/ui.h @@ -230,20 +230,23 @@ class RecoveryUI { bool InitScreensaver(); void SetScreensaverState(ScreensaverState state); + // Key event input queue std::mutex key_queue_mutex; std::condition_variable key_queue_cond; bool key_interrupted_; int key_queue[256], key_queue_len; - char key_pressed[KEY_MAX + 1]; // under key_queue_mutex - int key_last_down; // under key_queue_mutex - bool key_long_press; // under key_queue_mutex - int key_down_count; // under key_queue_mutex - bool enable_reboot; // under key_queue_mutex - int rel_sum; + // key press events + std::mutex key_press_mutex; + char key_pressed[KEY_MAX + 1]; + int key_last_down; + bool key_long_press; + int key_down_count; + bool enable_reboot; + + int rel_sum; int consecutive_power_keys; - int last_key; bool has_power_key; bool has_up_key; diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp index 823004521..f2af66c45 100644 --- a/recovery_ui/screen_ui.cpp +++ b/recovery_ui/screen_ui.cpp @@ -1263,7 +1263,7 @@ size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers, return initial_selection; } - return ShowMenu(CreateMenu(headers, items, initial_selection), menu_only, key_handler); + return ShowMenu(std::move(menu), menu_only, key_handler); } size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers, diff --git a/recovery_ui/stub_ui.cpp b/recovery_ui/stub_ui.cpp new file mode 100644 index 000000000..a56b3f725 --- /dev/null +++ b/recovery_ui/stub_ui.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 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 "recovery_ui/stub_ui.h" + +#include <android-base/logging.h> + +#include "recovery_ui/device.h" + +size_t StubRecoveryUI::ShowMenu(const std::vector<std::string>& /* headers */, + const std::vector<std::string>& /* items */, + size_t /* initial_selection */, bool /* menu_only */, + const std::function<int(int, bool)>& /*key_handler*/) { + while (true) { + int key = WaitKey(); + // Exit the loop in the case of interruption or time out. + if (key == static_cast<int>(KeyError::INTERRUPTED) || + key == static_cast<int>(KeyError::TIMED_OUT)) { + return static_cast<size_t>(key); + } + } + LOG(FATAL) << "Unreachable key selected in ShowMenu of stub UI"; +} diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index cf0d3b563..98c654ddb 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -70,7 +70,6 @@ RecoveryUI::RecoveryUI() key_down_count(0), enable_reboot(true), consecutive_power_keys(0), - last_key(-1), has_power_key(false), has_up_key(false), has_down_key(false), @@ -346,7 +345,7 @@ void RecoveryUI::ProcessKey(int key_code, int updown) { bool long_press = false; { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(key_press_mutex); key_pressed[key_code] = updown; if (updown) { ++key_down_count; @@ -393,7 +392,7 @@ void RecoveryUI::TimeKey(int key_code, int count) { std::this_thread::sleep_for(750ms); // 750 ms == "long" bool long_press = false; { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(key_press_mutex); if (key_last_down == key_code && key_down_count == count) { long_press = key_long_press = true; } @@ -518,13 +517,13 @@ bool RecoveryUI::IsUsbConnected() { } bool RecoveryUI::IsKeyPressed(int key) { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(key_press_mutex); int pressed = key_pressed[key]; return pressed; } bool RecoveryUI::IsLongPress() { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(key_press_mutex); bool result = key_long_press; return result; } @@ -548,7 +547,7 @@ void RecoveryUI::FlushKeys() { RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(key_press_mutex); key_long_press = false; } @@ -585,13 +584,12 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { consecutive_power_keys = 0; } - last_key = key; return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE; } void RecoveryUI::KeyLongPress(int) {} void RecoveryUI::SetEnableReboot(bool enabled) { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(key_press_mutex); enable_reboot = enabled; } diff --git a/tests/Android.bp b/tests/Android.bp index 4969c087b..a86704015 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -50,13 +50,11 @@ cc_defaults { }, } -// libapplypatch, libapplypatch_modes, libimgdiff, libimgpatch +// libapplypatch, libapplypatch_modes libapplypatch_static_libs = [ "libapplypatch_modes", "libapplypatch", "libedify", - "libimgdiff", - "libimgpatch", "libotautil", "libbsdiff", "libbspatch", @@ -157,26 +155,22 @@ cc_test_host { defaults: [ "recovery_test_defaults", + "libupdater_defaults", ], srcs: [ - "unit/imgdiff_test.cpp", + "unit/host/*", ], static_libs: [ + "libupdater_host", + "libupdater_core", "libimgdiff", - "libimgpatch", - "libotautil", "libbsdiff", - "libbspatch", - "libziparchive", - "libutils", - "libcrypto", - "libbrotli", - "libbz", "libdivsufsort64", "libdivsufsort", - "libz", + "libfstab", + "libc++fs", ], test_suites: ["general-tests"], @@ -185,7 +179,7 @@ cc_test_host { target: { darwin: { - // libimgdiff is not available on the Mac. + // libapplypatch in "libupdater_defaults" is not available on the Mac. enabled: false, }, }, diff --git a/tests/unit/imgdiff_test.cpp b/tests/unit/host/imgdiff_test.cpp index e76ccbdfb..e76ccbdfb 100644 --- a/tests/unit/imgdiff_test.cpp +++ b/tests/unit/host/imgdiff_test.cpp diff --git a/tests/unit/host/update_simulator_test.cpp b/tests/unit/host/update_simulator_test.cpp new file mode 100644 index 000000000..bf89b7859 --- /dev/null +++ b/tests/unit/host/update_simulator_test.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2019 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 <stdint.h> +#include <stdio.h> + +#include <map> +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <bsdiff/bsdiff.h> +#include <gtest/gtest.h> +#include <ziparchive/zip_writer.h> + +#include "otautil/paths.h" +#include "otautil/print_sha1.h" +#include "updater/blockimg.h" +#include "updater/build_info.h" +#include "updater/install.h" +#include "updater/simulator_runtime.h" +#include "updater/target_files.h" +#include "updater/updater.h" + +using std::string; + +// echo -n "system.img" > system.img && img2simg system.img sparse_system_string_.img 4096 && +// hexdump -v -e '" " 12/1 "0x%02x, " "\n"' sparse_system_string_.img +// The total size of the result sparse image is 4136 bytes; and we can append 0s in the end to get +// the full image. +constexpr uint8_t SPARSE_SYSTEM_HEADER[] = { + 0x3a, 0xff, 0x26, 0xed, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x0c, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xca, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x10, 0x00, 0x00, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x2e, 0x69, 0x6d, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static void AddZipEntries(int fd, const std::map<string, string>& entries) { + FILE* zip_file = fdopen(fd, "w"); + ZipWriter writer(zip_file); + for (const auto& pair : entries) { + ASSERT_EQ(0, writer.StartEntry(pair.first.c_str(), 0)); + ASSERT_EQ(0, writer.WriteBytes(pair.second.data(), pair.second.size())); + ASSERT_EQ(0, writer.FinishEntry()); + } + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); +} + +static string CalculateSha1(const string& data) { + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest); + return print_sha1(digest); +} + +static void CreateBsdiffPatch(const string& src, const string& tgt, string* patch) { + TemporaryFile patch_file; + ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src.data()), src.size(), + reinterpret_cast<const uint8_t*>(tgt.data()), tgt.size(), + patch_file.path, nullptr)); + ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, patch)); +} + +static void RunSimulation(std::string_view src_tf, std::string_view ota_package, bool expected) { + TemporaryFile cmd_pipe; + TemporaryFile temp_saved_source; + TemporaryFile temp_last_command; + TemporaryDir temp_stash_base; + + 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); + + // Configure edify's functions. + RegisterBuiltins(); + RegisterInstallFunctions(); + RegisterBlockImageFunctions(); + + // Run the update simulation and check the result. + TemporaryDir work_dir; + BuildInfo build_info(work_dir.path); + ASSERT_TRUE(build_info.ParseTargetFile(src_tf, false)); + Updater updater(std::make_unique<SimulatorRuntime>(&build_info)); + ASSERT_TRUE(updater.Init(cmd_pipe.release(), ota_package, false)); + ASSERT_EQ(expected, updater.RunUpdate()); + // TODO(xunchang) check the recovery&system has the expected contents. +} + +class UpdateSimulatorTest : public ::testing::Test { + protected: + void SetUp() override { + std::vector<string> props = { + "import /oem/oem.prop oem*", + "# begin build properties", + "# autogenerated by buildinfo.sh", + "ro.build.id=OPR1.170510.001", + "ro.build.display.id=OPR1.170510.001 dev-keys", + "ro.build.version.incremental=3993052", + "ro.build.version.release=O", + "ro.build.date=Wed May 10 11:10:29 UTC 2017", + "ro.build.date.utc=1494414629", + "ro.build.type=user", + "ro.build.tags=dev-keys", + "ro.build.flavor=angler-user", + "ro.product.system.brand=google", + "ro.product.system.name=angler", + "ro.product.system.device=angler", + }; + build_prop_string_ = android::base::Join(props, "\n"); + + fstab_content_ = R"( +#<src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags> +# More comments..... + +/dev/block/by-name/system /system ext4 ro,barrier=1 wait +/dev/block/by-name/vendor /vendor ext4 ro wait,verify=/dev/metadata +/dev/block/by-name/cache /cache ext4 noatime,errors=panic wait,check +/dev/block/by-name/modem /firmware vfat ro,uid=1000,gid=1000, wait +/dev/block/by-name/boot /boot emmc defaults defaults +/dev/block/by-name/recovery /recovery emmc defaults defaults +/dev/block/by-name/misc /misc emmc defaults +/dev/block/by-name/modem /modem emmc defaults defaults)"; + + raw_system_string_ = "system.img" + string(4086, '\0'); // raw image is 4096 bytes in total + sparse_system_string_ = string(SPARSE_SYSTEM_HEADER, std::end(SPARSE_SYSTEM_HEADER)) + + string(4136 - sizeof(SPARSE_SYSTEM_HEADER), '\0'); + } + + string build_prop_string_; + string fstab_content_; + string raw_system_string_; + string sparse_system_string_; +}; + +TEST_F(UpdateSimulatorTest, TargetFile_ExtractImage) { + TemporaryFile zip_file; + AddZipEntries(zip_file.release(), { { "META/misc_info.txt", "extfs_sparse_flag=-s" }, + { "IMAGES/system.img", sparse_system_string_ } }); + TargetFile target_file(zip_file.path, false); + ASSERT_TRUE(target_file.Open()); + + TemporaryDir temp_dir; + TemporaryFile raw_image; + ASSERT_TRUE(target_file.ExtractImage( + "IMAGES/system.img", FstabInfo("/dev/system", "system", "ext4"), temp_dir.path, &raw_image)); + + // Check the raw image has expected contents. + string content; + ASSERT_TRUE(android::base::ReadFileToString(raw_image.path, &content)); + string expected_content = "system.img" + string(4086, '\0'); + ASSERT_EQ(expected_content, content); +} + +TEST_F(UpdateSimulatorTest, TargetFile_ParseFstabInfo) { + TemporaryFile zip_file; + AddZipEntries(zip_file.release(), + { { "META/misc_info.txt", "" }, + { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ } }); + TargetFile target_file(zip_file.path, false); + ASSERT_TRUE(target_file.Open()); + + std::vector<FstabInfo> fstab_info; + EXPECT_TRUE(target_file.ParseFstabInfo(&fstab_info)); + + std::vector<std::vector<string>> transformed; + std::transform(fstab_info.begin(), fstab_info.end(), std::back_inserter(transformed), + [](const FstabInfo& info) { + return std::vector<string>{ info.blockdev_name, info.mount_point, info.fs_type }; + }); + + std::vector<std::vector<string>> expected = { + { "/dev/block/by-name/system", "/system", "ext4" }, + { "/dev/block/by-name/vendor", "/vendor", "ext4" }, + { "/dev/block/by-name/cache", "/cache", "ext4" }, + { "/dev/block/by-name/boot", "/boot", "emmc" }, + { "/dev/block/by-name/recovery", "/recovery", "emmc" }, + { "/dev/block/by-name/misc", "/misc", "emmc" }, + { "/dev/block/by-name/modem", "/modem", "emmc" }, + }; + EXPECT_EQ(expected, transformed); +} + +TEST_F(UpdateSimulatorTest, BuildInfo_ParseTargetFile) { + std::map<string, string> entries = { + { "META/misc_info.txt", "" }, + { "SYSTEM/build.prop", build_prop_string_ }, + { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ }, + { "IMAGES/recovery.img", "" }, + { "IMAGES/boot.img", "" }, + { "IMAGES/misc.img", "" }, + { "IMAGES/system.map", "" }, + { "IMAGES/system.img", sparse_system_string_ }, + }; + + TemporaryFile zip_file; + AddZipEntries(zip_file.release(), entries); + + TemporaryDir temp_dir; + BuildInfo build_info(temp_dir.path); + ASSERT_TRUE(build_info.ParseTargetFile(zip_file.path, false)); + + std::map<string, string> expected_result = { + { "ro.build.id", "OPR1.170510.001" }, + { "ro.build.display.id", "OPR1.170510.001 dev-keys" }, + { "ro.build.version.incremental", "3993052" }, + { "ro.build.version.release", "O" }, + { "ro.build.date", "Wed May 10 11:10:29 UTC 2017" }, + { "ro.build.date.utc", "1494414629" }, + { "ro.build.type", "user" }, + { "ro.build.tags", "dev-keys" }, + { "ro.build.flavor", "angler-user" }, + { "ro.product.brand", "google" }, + { "ro.product.name", "angler" }, + { "ro.product.device", "angler" }, + }; + + for (const auto& [key, value] : expected_result) { + ASSERT_EQ(value, build_info.GetProperty(key, "")); + } + + // Check that the temp files for each block device are created successfully. + for (auto name : { "/dev/block/by-name/system", "/dev/block/by-name/recovery", + "/dev/block/by-name/boot", "/dev/block/by-name/misc" }) { + ASSERT_EQ(0, access(build_info.FindBlockDeviceName(name).c_str(), R_OK)); + } +} + +TEST_F(UpdateSimulatorTest, RunUpdateSmoke) { + string recovery_img_string = "recovery.img"; + string boot_img_string = "boot.img"; + + std::map<string, string> src_entries{ + { "META/misc_info.txt", "extfs_sparse_flag=-s" }, + { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ }, + { "SYSTEM/build.prop", build_prop_string_ }, + { "IMAGES/recovery.img", "" }, + { "IMAGES/boot.img", boot_img_string }, + { "IMAGES/system.img", sparse_system_string_ }, + }; + + // Construct the source target-files. + TemporaryFile src_tf; + AddZipEntries(src_tf.release(), src_entries); + + string recovery_from_boot; + CreateBsdiffPatch(boot_img_string, recovery_img_string, &recovery_from_boot); + + // Set up the apply patch commands to patch the recovery image. + string recovery_sha1 = CalculateSha1(recovery_img_string); + string boot_sha1 = CalculateSha1(boot_img_string); + string apply_patch_source_string = android::base::StringPrintf( + "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str()); + string apply_patch_target_string = android::base::StringPrintf( + "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str()); + string check_command = android::base::StringPrintf( + R"(patch_partition_check("%s", "%s") || abort("check failed");)", + apply_patch_target_string.c_str(), apply_patch_source_string.c_str()); + string patch_command = android::base::StringPrintf( + R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("patch failed");)", + apply_patch_target_string.c_str(), apply_patch_source_string.c_str()); + + // Add the commands to update the system image. Test common commands: + // * getprop + // * ui_print + // * patch_partition + // * package_extract_file (single argument) + // * block_image_verify, block_image_update + string tgt_system_string = string(4096, 'a'); + string system_patch; + CreateBsdiffPatch(raw_system_string_, tgt_system_string, &system_patch); + + string tgt_system_hash = CalculateSha1(tgt_system_string); + string src_system_hash = CalculateSha1(raw_system_string_); + + std::vector<std::string> transfer_list = { + "4", + "1", + "0", + "0", + android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,1 1 2,0,1", system_patch.size(), + src_system_hash.c_str(), tgt_system_hash.c_str()), + }; + + // Construct the updater_script. + std::vector<string> updater_commands = { + R"(getprop("ro.product.device") == "angler" || abort("This package is for \"angler\"");)", + R"(ui_print("Source: angler/OPR1.170510.001");)", + check_command, + patch_command, + R"(block_image_verify("/dev/block/by-name/system", )" + R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )" + R"(abort("Failed to verify system.");)", + R"(block_image_update("/dev/block/by-name/system", )" + R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )" + R"(abort("Failed to verify system.");)", + }; + string updater_script = android::base::Join(updater_commands, '\n'); + + // Construct the ota update package. + std::map<string, string> ota_entries{ + { "system.new.dat", "" }, + { "system.patch.dat", system_patch }, + { "system.transfer.list", android::base::Join(transfer_list, '\n') }, + { "META-INF/com/google/android/updater-script", updater_script }, + { "patch.p", recovery_from_boot }, + }; + + TemporaryFile ota_package; + AddZipEntries(ota_package.release(), ota_entries); + + RunSimulation(src_tf.path, ota_package.path, true); +} + +TEST_F(UpdateSimulatorTest, RunUpdateUnrecognizedFunction) { + std::map<string, string> src_entries{ + { "META/misc_info.txt", "extfs_sparse_flag=-s" }, + { "IMAGES/system.img", sparse_system_string_ }, + { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ }, + { "SYSTEM/build.prop", build_prop_string_ }, + }; + + TemporaryFile src_tf; + AddZipEntries(src_tf.release(), src_entries); + + std::map<string, string> ota_entries{ + { "system.new.dat", "" }, + { "system.patch.dat", "" }, + { "system.transfer.list", "" }, + { "META-INF/com/google/android/updater-script", R"(bad_function("");)" }, + }; + + TemporaryFile ota_package; + AddZipEntries(ota_package.release(), ota_entries); + + RunSimulation(src_tf.path, ota_package.path, false); +} + +TEST_F(UpdateSimulatorTest, RunUpdateApplyPatchFailed) { + string recovery_img_string = "recovery.img"; + string boot_img_string = "boot.img"; + + std::map<string, string> src_entries{ + { "META/misc_info.txt", "extfs_sparse_flag=-s" }, + { "IMAGES/recovery.img", "" }, + { "IMAGES/boot.img", boot_img_string }, + { "IMAGES/system.img", sparse_system_string_ }, + { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ }, + { "SYSTEM/build.prop", build_prop_string_ }, + }; + + TemporaryFile src_tf; + AddZipEntries(src_tf.release(), src_entries); + + string recovery_sha1 = CalculateSha1(recovery_img_string); + string boot_sha1 = CalculateSha1(boot_img_string); + string apply_patch_source_string = android::base::StringPrintf( + "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str()); + string apply_patch_target_string = android::base::StringPrintf( + "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str()); + string check_command = android::base::StringPrintf( + R"(patch_partition_check("%s", "%s") || abort("check failed");)", + apply_patch_target_string.c_str(), apply_patch_source_string.c_str()); + string patch_command = android::base::StringPrintf( + R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("failed");)", + apply_patch_target_string.c_str(), apply_patch_source_string.c_str()); + + // Give an invalid recovery patch and expect the apply patch to fail. + // TODO(xunchang) check the cause code. + std::vector<string> updater_commands = { + R"(ui_print("Source: angler/OPR1.170510.001");)", + check_command, + patch_command, + }; + + string updater_script = android::base::Join(updater_commands, '\n'); + std::map<string, string> ota_entries{ + { "system.new.dat", "" }, + { "system.patch.dat", "" }, + { "system.transfer.list", "" }, + { "META-INF/com/google/android/updater-script", updater_script }, + { "patch.p", "random string" }, + }; + + TemporaryFile ota_package; + AddZipEntries(ota_package.release(), ota_entries); + + RunSimulation(src_tf.path, ota_package.path, false); +} diff --git a/updater/build_info.cpp b/updater/build_info.cpp index 8e87bd3e5..3072aab54 100644 --- a/updater/build_info.cpp +++ b/updater/build_info.cpp @@ -79,7 +79,7 @@ std::string BuildInfo::GetProperty(const std::string_view key, "ro.product.name" }; const std::vector<std::string> source_order = { - "product", "product_services", "odm", "vendor", "system", + "product", "odm", "vendor", "system_ext", "system", }; if (ro_product_props.find(key) != ro_product_props.end()) { std::string_view key_suffix(key); diff --git a/updater/include/updater/updater.h b/updater/include/updater/updater.h index 08816bf36..8676b6038 100644 --- a/updater/include/updater/updater.h +++ b/updater/include/updater/updater.h @@ -62,10 +62,12 @@ class Updater : public UpdaterInterface { std::string GetResult() const override { return result_; } - uint8_t* GetMappedPackageAddress() const override { return mapped_package_.addr; } + size_t GetMappedPackageLength() const override { + return mapped_package_.length; + } private: friend class UpdaterTestBase; diff --git a/updater/target_files.cpp b/updater/target_files.cpp index 93540b2e5..919ec4e04 100644 --- a/updater/target_files.cpp +++ b/updater/target_files.cpp @@ -132,6 +132,11 @@ bool TargetFile::ReadEntryToString(const std::string_view name, std::string* con return false; } + if (entry.uncompressed_length == 0) { + content->clear(); + return true; + } + content->resize(entry.uncompressed_length); if (auto extract_err = ExtractToMemory( handle_, &entry, reinterpret_cast<uint8_t*>(&content->at(0)), entry.uncompressed_length); @@ -193,10 +198,10 @@ bool TargetFile::GetBuildProps(std::map<std::string, std::string, std::less<>>* "SYSTEM/build.prop", "VENDOR/build.prop", "PRODUCT/build.prop", - "PRODUCT_SERVICES/build.prop", + "SYSTEM_EXT/build.prop", "SYSTEM/vendor/build.prop", "SYSTEM/product/build.prop", - "SYSTEM/product_services/build.prop", + "SYSTEM/system_ext/build.prop", "ODM/build.prop", // legacy "ODM/etc/build.prop", "VENDOR/odm/build.prop", // legacy |