summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--applypatch/Android.bp36
-rw-r--r--edify/include/edify/updater_interface.h1
-rw-r--r--install/adb_install.cpp4
-rw-r--r--install/fuse_install.cpp7
-rw-r--r--minadbd/minadbd_services.cpp9
-rw-r--r--minadbd/minadbd_types.h1
-rw-r--r--otautil/sysutil.cpp2
-rw-r--r--recovery.cpp28
-rw-r--r--recovery_ui/Android.bp1
-rw-r--r--recovery_ui/include/recovery_ui/stub_ui.h6
-rw-r--r--recovery_ui/include/recovery_ui/ui.h17
-rw-r--r--recovery_ui/screen_ui.cpp2
-rw-r--r--recovery_ui/stub_ui.cpp36
-rw-r--r--recovery_ui/ui.cpp14
-rw-r--r--tests/Android.bp22
-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.cpp403
-rw-r--r--updater/build_info.cpp2
-rw-r--r--updater/include/updater/updater.h4
-rw-r--r--updater/target_files.cpp9
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