summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CleanSpec.mk4
-rw-r--r--README.md2
-rw-r--r--TEST_MAPPING3
-rw-r--r--applypatch/imgdiff.cpp7
-rw-r--r--bootloader_message/Android.bp37
-rw-r--r--bootloader_message/bootloader_message.cpp62
-rw-r--r--bootloader_message/include/bootloader_message/bootloader_message.h18
-rw-r--r--edify/expr.cpp4
-rw-r--r--edify/include/edify/expr.h9
-rw-r--r--edify/include/edify/updater_interface.h47
-rw-r--r--edify/include/edify/updater_runtime_interface.h69
-rw-r--r--fastboot/fastboot.cpp4
-rw-r--r--fsck_unshare_blocks.cpp3
-rw-r--r--fuse_sideload/Android.bp4
-rw-r--r--fuse_sideload/fuse_provider.cpp79
-rw-r--r--fuse_sideload/include/fuse_provider.h28
-rw-r--r--install/Android.bp1
-rw-r--r--install/adb_install.cpp31
-rw-r--r--install/fuse_sdcard_install.cpp9
-rw-r--r--install/include/install/adb_install.h12
-rw-r--r--install/include/install/fuse_sdcard_install.h3
-rw-r--r--install/include/install/install.h15
-rw-r--r--install/include/install/wipe_device.h29
-rw-r--r--install/include/private/setup_commands.h8
-rw-r--r--install/install.cpp112
-rw-r--r--install/verifier.cpp10
-rw-r--r--install/wipe_device.cpp197
-rw-r--r--minadbd/Android.bp5
-rw-r--r--minadbd/minadbd_services.cpp89
-rw-r--r--minadbd/minadbd_services_test.cpp2
-rw-r--r--minadbd/minadbd_types.h4
-rw-r--r--misc_writer/Android.bp40
-rw-r--r--misc_writer/misc_writer.cpp106
-rw-r--r--otautil/Android.bp8
-rw-r--r--otautil/include/otautil/rangeset.h7
-rw-r--r--otautil/include/otautil/roots.h4
-rw-r--r--otautil/include/otautil/sysutil.h15
-rw-r--r--otautil/rangeset.cpp52
-rw-r--r--otautil/roots.cpp8
-rw-r--r--otautil/sysutil.cpp18
-rw-r--r--recovery.cpp213
-rw-r--r--recovery_main.cpp36
-rw-r--r--recovery_ui/include/recovery_ui/device.h2
-rw-r--r--recovery_ui/ui.cpp2
-rw-r--r--tests/Android.bp50
-rw-r--r--tests/component/resources_test.cpp120
-rw-r--r--tests/unit/applypatch_modes_test.cpp (renamed from tests/component/applypatch_modes_test.cpp)0
-rw-r--r--tests/unit/bootloader_message_test.cpp (renamed from tests/component/bootloader_message_test.cpp)38
-rw-r--r--tests/unit/edify_test.cpp (renamed from tests/component/edify_test.cpp)0
-rw-r--r--tests/unit/fuse_provider_test.cpp103
-rw-r--r--tests/unit/fuse_sideload_test.cpp (renamed from tests/component/sideload_test.cpp)0
-rw-r--r--tests/unit/imgdiff_test.cpp (renamed from tests/component/imgdiff_test.cpp)0
-rw-r--r--tests/unit/install_test.cpp (renamed from tests/component/install_test.cpp)58
-rw-r--r--tests/unit/package_test.cpp5
-rw-r--r--tests/unit/rangeset_test.cpp24
-rw-r--r--tests/unit/resources_test.cpp99
-rw-r--r--tests/unit/sysutil_test.cpp2
-rw-r--r--tests/unit/uncrypt_test.cpp (renamed from tests/component/uncrypt_test.cpp)0
-rw-r--r--tests/unit/update_verifier_test.cpp (renamed from tests/component/update_verifier_test.cpp)0
-rw-r--r--tests/unit/updater_test.cpp (renamed from tests/component/updater_test.cpp)186
-rw-r--r--tests/unit/verifier_test.cpp (renamed from tests/component/verifier_test.cpp)0
-rw-r--r--tests/unit/zip_test.cpp3
-rw-r--r--tools/image_generator/Android.bp2
-rw-r--r--tools/recovery_l10n/res/values-gl/strings.xml4
-rw-r--r--tools/recovery_l10n/res/values-ja/strings.xml2
-rw-r--r--updater/Android.bp75
-rw-r--r--updater/Android.mk88
-rw-r--r--updater/blockimg.cpp116
-rw-r--r--updater/include/updater/install.h11
-rw-r--r--updater/include/updater/simulator_runtime.h58
-rw-r--r--updater/include/updater/target_files.h36
-rw-r--r--updater/include/updater/updater.h83
-rw-r--r--updater/include/updater/updater_runtime.h57
-rw-r--r--updater/install.cpp208
-rw-r--r--updater/simulator_runtime.cpp97
-rw-r--r--updater/target_files.cpp26
-rw-r--r--updater/update_simulator_main.cpp76
-rw-r--r--updater/updater.cpp278
-rw-r--r--updater/updater_main.cpp109
-rw-r--r--updater/updater_runtime.cpp132
80 files changed, 2528 insertions, 1036 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk
index a7ab0d9be..6bd1eb170 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -51,6 +51,10 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libinstall.recovery_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/system/lib64/libinstall.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest/recovery_component_test)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest64/recovery_component_test)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/testcases/recovery_component_test)
+
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/README.md b/README.md
index efcd318b5..0ccc10b50 100644
--- a/README.md
+++ b/README.md
@@ -22,11 +22,9 @@ Running the tests
# 32-bit device
adb shell /data/nativetest/recovery_unit_test/recovery_unit_test
- adb shell /data/nativetest/recovery_component_test/recovery_component_test
# Or 64-bit device
adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test
- adb shell /data/nativetest64/recovery_component_test/recovery_component_test
Running the manual tests
------------------------
diff --git a/TEST_MAPPING b/TEST_MAPPING
index c87ece24e..a3045828e 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -7,9 +7,6 @@
"name": "recovery_unit_test"
},
{
- "name": "recovery_component_test"
- },
- {
"name": "recovery_host_test",
"host": true
}
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index 415d95f14..6ad4a6105 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -675,7 +675,7 @@ bool ZipModeImage::Initialize(const std::string& filename) {
// Iterate the zip entries and compose the image chunks accordingly.
bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandle handle) {
void* cookie;
- int ret = StartIteration(handle, &cookie, nullptr, nullptr);
+ int ret = StartIteration(handle, &cookie);
if (ret != 0) {
LOG(ERROR) << "Failed to iterate over entries in " << filename << ": " << ErrorCodeString(ret);
return false;
@@ -683,12 +683,11 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl
// Create a list of deflated zip entries, sorted by offset.
std::vector<std::pair<std::string, ZipEntry>> temp_entries;
- ZipString name;
+ std::string name;
ZipEntry entry;
while ((ret = Next(cookie, &entry, &name)) == 0) {
if (entry.method == kCompressDeflated || limit_ > 0) {
- std::string entry_name(name.name, name.name + name.name_length);
- temp_entries.emplace_back(entry_name, entry);
+ temp_entries.emplace_back(name, entry);
}
}
diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp
index 5cd21323c..8d72a1163 100644
--- a/bootloader_message/Android.bp
+++ b/bootloader_message/Android.bp
@@ -14,9 +14,8 @@
// limitations under the License.
//
-cc_library {
- name: "libbootloader_message",
- recovery_available: true,
+cc_defaults {
+ name: "libbootloader_message_defaults",
srcs: ["bootloader_message.cpp"],
cflags: [
"-Wall",
@@ -24,7 +23,37 @@ cc_library {
],
shared_libs: [
"libbase",
- "libfs_mgr",
+ ],
+ static_libs: [
+ "libfstab",
],
export_include_dirs: ["include"],
}
+
+cc_library {
+ name: "libbootloader_message",
+ defaults: [
+ "libbootloader_message_defaults",
+ ],
+ recovery_available: true,
+ host_supported: true,
+
+ target: {
+ host: {
+ shared_libs: [
+ "libcutils", // for strlcpy
+ ],
+ },
+ darwin: {
+ enabled: false,
+ },
+ }
+}
+
+cc_library_static {
+ name: "libbootloader_message_vendor",
+ defaults: [
+ "libbootloader_message_defaults",
+ ],
+ vendor: true,
+}
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index 8c1d63bdd..e684abbad 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -21,6 +21,7 @@
#include <string.h>
#include <string>
+#include <string_view>
#include <vector>
#include <android-base/file.h>
@@ -29,10 +30,24 @@
#include <android-base/unique_fd.h>
#include <fstab/fstab.h>
+#ifndef __ANDROID__
+#include <cutils/memory.h> // for strlcpy
+#endif
+
using android::fs_mgr::Fstab;
using android::fs_mgr::ReadDefaultFstab;
+static std::string g_misc_device_for_test;
+
+// Exposed for test purpose.
+void SetMiscBlockDeviceForTest(std::string_view misc_device) {
+ g_misc_device_for_test = misc_device;
+}
+
static std::string get_misc_blk_device(std::string* err) {
+ if (!g_misc_device_for_test.empty()) {
+ return g_misc_device_for_test;
+ }
Fstab fstab;
if (!ReadDefaultFstab(&fstab)) {
*err = "failed to read default fstab";
@@ -168,6 +183,14 @@ bool write_bootloader_message(const std::vector<std::string>& options, std::stri
return write_bootloader_message(boot, err);
}
+bool write_bootloader_message_to(const std::vector<std::string>& options,
+ const std::string& misc_blk_device, std::string* err) {
+ bootloader_message boot = {};
+ update_bootloader_message_in_struct(&boot, options);
+
+ return write_bootloader_message_to(boot, misc_blk_device, err);
+}
+
bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
bootloader_message boot;
if (!read_bootloader_message(&boot, err)) {
@@ -186,13 +209,15 @@ bool update_bootloader_message_in_struct(bootloader_message* boot,
memset(boot->recovery, 0, sizeof(boot->recovery));
strlcpy(boot->command, "boot-recovery", sizeof(boot->command));
- strlcpy(boot->recovery, "recovery\n", sizeof(boot->recovery));
+
+ std::string recovery = "recovery\n";
for (const auto& s : options) {
- strlcat(boot->recovery, s.c_str(), sizeof(boot->recovery));
+ recovery += s;
if (s.back() != '\n') {
- strlcat(boot->recovery, "\n", sizeof(boot->recovery));
+ recovery += '\n';
}
}
+ strlcpy(boot->recovery, recovery.c_str(), sizeof(boot->recovery));
return true;
}
@@ -228,6 +253,37 @@ bool write_wipe_package(const std::string& package_data, std::string* err) {
WIPE_PACKAGE_OFFSET_IN_MISC, err);
}
+static bool OffsetAndSizeInVendorSpace(size_t offset, size_t size) {
+ auto total_size = WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC;
+ return size <= total_size && offset <= total_size - size;
+}
+
+bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err) {
+ if (!OffsetAndSizeInVendorSpace(offset, size)) {
+ *err = android::base::StringPrintf("Out of bound read (offset %zu size %zu)", offset, size);
+ return false;
+ }
+ auto misc_blk_device = get_misc_blk_device(err);
+ if (misc_blk_device.empty()) {
+ return false;
+ }
+ return read_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset,
+ err);
+}
+
+bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err) {
+ if (!OffsetAndSizeInVendorSpace(offset, size)) {
+ *err = android::base::StringPrintf("Out of bound write (offset %zu size %zu)", offset, size);
+ return false;
+ }
+ auto misc_blk_device = get_misc_blk_device(err);
+ if (misc_blk_device.empty()) {
+ return false;
+ }
+ return write_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset,
+ err);
+}
+
extern "C" bool write_reboot_bootloader(void) {
std::string err;
return write_reboot_bootloader(&err);
diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h
index 95c19ae54..5c0a450fc 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -28,8 +28,9 @@
// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices
// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
// are not configurable without changing all of them.
-static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
-static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
+constexpr size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
+constexpr size_t VENDOR_SPACE_OFFSET_IN_MISC = 2 * 1024;
+constexpr size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
/* Bootloader Message (2-KiB)
*
@@ -207,6 +208,11 @@ bool write_bootloader_message_to(const bootloader_message& boot,
// set the command and recovery fields, and reset the rest.
bool write_bootloader_message(const std::vector<std::string>& options, std::string* err);
+// Write bootloader message (boots into recovery with the options) to the specific BCB device. Will
+// set the command and recovery fields, and reset the rest.
+bool write_bootloader_message_to(const std::vector<std::string>& options,
+ const std::string& misc_blk_device, std::string* err);
+
// Update bootloader message (boots into recovery with the options) to BCB. Will
// only update the command and recovery fields.
bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
@@ -228,6 +234,14 @@ bool read_wipe_package(std::string* package_data, size_t size, std::string* err)
// Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC).
bool write_wipe_package(const std::string& package_data, std::string* err);
+// Reads data from the vendor space in /misc partition, with the given offset and size. Note that
+// offset is in relative to the start of vendor space.
+bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err);
+
+// Writes the given data to the vendor space in /misc partition, at the given offset. Note that
+// offset is in relative to the start of the vendor space.
+bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err);
+
#else
#include <stdbool.h>
diff --git a/edify/expr.cpp b/edify/expr.cpp
index c090eb28a..e5e0e240a 100644
--- a/edify/expr.cpp
+++ b/edify/expr.cpp
@@ -421,5 +421,5 @@ Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) {
return nullptr;
}
-State::State(const std::string& script, void* cookie)
- : script(script), cookie(cookie), error_code(kNoError), cause_code(kNoCause) {}
+State::State(const std::string& script, UpdaterInterface* interface)
+ : script(script), updater(interface), error_code(kNoError), cause_code(kNoCause) {}
diff --git a/edify/include/edify/expr.h b/edify/include/edify/expr.h
index 5cbd5e15d..cd9c70120 100644
--- a/edify/include/edify/expr.h
+++ b/edify/include/edify/expr.h
@@ -23,19 +23,20 @@
#include <string>
#include <vector>
+#include "edify/updater_interface.h"
+
// Forward declaration to avoid including "otautil/error_code.h".
enum ErrorCode : int;
enum CauseCode : int;
struct State {
- State(const std::string& script, void* cookie);
+ State(const std::string& script, UpdaterInterface* cookie);
// The source of the original script.
const std::string& script;
- // Optional pointer to app-specific data; the core of edify never
- // uses this value.
- void* cookie;
+ // A pointer to app-specific data; the libedify doesn't use this value.
+ UpdaterInterface* updater;
// The error message (if any) returned if the evaluation aborts.
// Should be empty initially, will be either empty or a string that
diff --git a/edify/include/edify/updater_interface.h b/edify/include/edify/updater_interface.h
new file mode 100644
index 000000000..a4d581eec
--- /dev/null
+++ b/edify/include/edify/updater_interface.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <string>
+#include <string_view>
+
+struct ZipArchive;
+typedef ZipArchive* ZipArchiveHandle;
+
+class UpdaterRuntimeInterface;
+
+class UpdaterInterface {
+ public:
+ virtual ~UpdaterInterface() = default;
+
+ // Writes the message to command pipe, adds a new line in the end.
+ virtual void WriteToCommandPipe(const std::string_view message, bool flush = false) const = 0;
+
+ // Sends over the message to recovery to print it on the screen.
+ virtual void UiPrint(const std::string_view message) const = 0;
+
+ // Given the name of the block device, returns |name| for updates on the device; or the file path
+ // to the fake block device for simulations.
+ virtual std::string FindBlockDeviceName(const std::string_view name) const = 0;
+
+ virtual UpdaterRuntimeInterface* GetRuntime() const = 0;
+ virtual ZipArchiveHandle GetPackageHandle() const = 0;
+ virtual std::string GetResult() const = 0;
+ virtual uint8_t* GetMappedPackageAddress() const = 0;
+};
diff --git a/edify/include/edify/updater_runtime_interface.h b/edify/include/edify/updater_runtime_interface.h
new file mode 100644
index 000000000..15ccd832d
--- /dev/null
+++ b/edify/include/edify/updater_runtime_interface.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <string_view>
+#include <vector>
+
+// This class serves as the base to updater runtime. It wraps the runtime dependent functions; and
+// updates on device and host simulations can have different implementations. e.g. block devices
+// during host simulation merely a temporary file. With this class, the caller side in registered
+// updater's functions will stay the same for both update and simulation.
+class UpdaterRuntimeInterface {
+ public:
+ virtual ~UpdaterRuntimeInterface() = default;
+
+ // Returns true if it's a runtime instance for simulation.
+ virtual bool IsSimulator() const = 0;
+
+ // Returns the value of system property |key|. If the property doesn't exist, returns
+ // |default_value|.
+ virtual std::string GetProperty(const std::string_view key,
+ const std::string_view default_value) const = 0;
+
+ // Given the name of the block device, returns |name| for updates on the device; or the file path
+ // to the fake block device for simulations.
+ virtual std::string FindBlockDeviceName(const std::string_view name) const = 0;
+
+ // Mounts the |location| on |mount_point|. Returns 0 on success.
+ virtual int Mount(const std::string_view location, const std::string_view mount_point,
+ const std::string_view fs_type, const std::string_view mount_options) = 0;
+
+ // Returns true if |mount_point| is mounted.
+ virtual bool IsMounted(const std::string_view mount_point) const = 0;
+
+ // Unmounts the |mount_point|. Returns a pair of results with the first value indicating
+ // if the |mount_point| is mounted, and the second value indicating the result of umount(2).
+ virtual std::pair<bool, int> Unmount(const std::string_view mount_point) = 0;
+
+ // Reads |filename| and puts its value to |content|.
+ virtual bool ReadFileToString(const std::string_view filename, std::string* content) const = 0;
+
+ // Updates the content of |filename| with |content|.
+ virtual bool WriteStringToFile(const std::string_view content,
+ const std::string_view filename) const = 0;
+
+ // Wipes the first |len| bytes of block device in |filename|.
+ virtual int WipeBlockDevice(const std::string_view filename, size_t len) const = 0;
+
+ // Starts a child process and runs the program with |args|. Uses vfork(2) if |is_vfork| is true.
+ virtual int RunProgram(const std::vector<std::string>& args, bool is_vfork) const = 0;
+
+ // Runs tune2fs with arguments |args|.
+ virtual int Tune2Fs(const std::vector<std::string>& args) const = 0;
+}; \ No newline at end of file
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 14f5e4bdc..202334997 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -30,10 +30,10 @@
#include "recovery_ui/ui.h"
static const std::vector<std::pair<std::string, Device::BuiltinAction>> kFastbootMenuActions{
- { "Reboot system now", Device::REBOOT },
+ { "Reboot system now", Device::REBOOT_FROM_FASTBOOT },
{ "Enter recovery", Device::ENTER_RECOVERY },
{ "Reboot to bootloader", Device::REBOOT_BOOTLOADER },
- { "Power off", Device::SHUTDOWN },
+ { "Power off", Device::SHUTDOWN_FROM_FASTBOOT },
};
Device::BuiltinAction StartFastboot(Device* device, const std::vector<std::string>& /* args */) {
diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp
index 0f8ffface..9dc0fd8ec 100644
--- a/fsck_unshare_blocks.cpp
+++ b/fsck_unshare_blocks.cpp
@@ -34,6 +34,7 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr/roots.h>
#include "otautil/roots.h"
@@ -119,7 +120,7 @@ bool do_fsck_unshare_blocks() {
std::vector<std::string> partitions = { "/odm", "/oem", "/product", "/vendor" };
// Temporarily mount system so we can copy e2fsck_static.
- std::string system_root = get_system_root();
+ auto system_root = android::fs_mgr::GetSystemRoot();
bool mounted = ensure_path_mounted_at(system_root, "/mnt/system") != -1;
partitions.push_back(system_root);
diff --git a/fuse_sideload/Android.bp b/fuse_sideload/Android.bp
index 8548548d2..9bf19eb85 100644
--- a/fuse_sideload/Android.bp
+++ b/fuse_sideload/Android.bp
@@ -34,6 +34,10 @@ cc_library {
"include",
],
+ static_libs: [
+ "libotautil",
+ ],
+
shared_libs: [
"libbase",
"libcrypto",
diff --git a/fuse_sideload/fuse_provider.cpp b/fuse_sideload/fuse_provider.cpp
index 58786f5f3..5ee6e247f 100644
--- a/fuse_sideload/fuse_provider.cpp
+++ b/fuse_sideload/fuse_provider.cpp
@@ -27,8 +27,11 @@
#include <functional>
#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
#include "fuse_sideload.h"
+#include "otautil/sysutil.h"
FuseFileDataProvider::FuseFileDataProvider(const std::string& path, uint32_t block_size) {
struct stat sb;
@@ -69,3 +72,79 @@ bool FuseFileDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_
void FuseFileDataProvider::Close() {
fd_.reset();
}
+
+FuseBlockDataProvider::FuseBlockDataProvider(uint64_t file_size, uint32_t fuse_block_size,
+ android::base::unique_fd&& fd,
+ uint32_t source_block_size, RangeSet ranges)
+ : FuseDataProvider(file_size, fuse_block_size),
+ fd_(std::move(fd)),
+ source_block_size_(source_block_size),
+ ranges_(std::move(ranges)) {
+ // Make sure the offset is also aligned with the blocks on the block device when we call
+ // ReadBlockAlignedData().
+ CHECK_EQ(0, fuse_block_size_ % source_block_size_);
+}
+
+bool FuseBlockDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
+ uint32_t start_block) const {
+ uint64_t offset = static_cast<uint64_t>(start_block) * fuse_block_size_;
+ if (fetch_size > file_size_ || offset > file_size_ - fetch_size) {
+ LOG(ERROR) << "Out of bound read, offset: " << offset << ", fetch size: " << fetch_size
+ << ", file size " << file_size_;
+ return false;
+ }
+
+ auto read_ranges =
+ ranges_.GetSubRanges(offset / source_block_size_, fetch_size / source_block_size_);
+ if (!read_ranges) {
+ return false;
+ }
+
+ uint8_t* next_out = buffer;
+ for (const auto& [range_start, range_end] : read_ranges.value()) {
+ uint64_t bytes_start = static_cast<uint64_t>(range_start) * source_block_size_;
+ uint64_t bytes_to_read = static_cast<uint64_t>(range_end - range_start) * source_block_size_;
+ if (!android::base::ReadFullyAtOffset(fd_, next_out, bytes_to_read, bytes_start)) {
+ PLOG(ERROR) << "Failed to read " << bytes_to_read << " bytes at offset " << bytes_start;
+ return false;
+ }
+
+ next_out += bytes_to_read;
+ }
+
+ if (uint64_t tailing_bytes = fetch_size % source_block_size_; tailing_bytes != 0) {
+ // Calculate the offset to last partial block.
+ uint64_t tailing_offset =
+ read_ranges.value()
+ ? static_cast<uint64_t>((read_ranges->cend() - 1)->second) * source_block_size_
+ : static_cast<uint64_t>(start_block) * source_block_size_;
+ if (!android::base::ReadFullyAtOffset(fd_, next_out, tailing_bytes, tailing_offset)) {
+ PLOG(ERROR) << "Failed to read tailing " << tailing_bytes << " bytes at offset "
+ << tailing_offset;
+ return false;
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<FuseBlockDataProvider> FuseBlockDataProvider::CreateFromBlockMap(
+ const std::string& block_map_path, uint32_t fuse_block_size) {
+ auto block_map = BlockMapData::ParseBlockMapFile(block_map_path);
+ if (!block_map) {
+ return nullptr;
+ }
+
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_map.path().c_str(), O_RDONLY)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << block_map.path();
+ return nullptr;
+ }
+
+ return std::unique_ptr<FuseBlockDataProvider>(
+ new FuseBlockDataProvider(block_map.file_size(), fuse_block_size, std::move(fd),
+ block_map.block_size(), block_map.block_ranges()));
+}
+
+void FuseBlockDataProvider::Close() {
+ fd_.reset();
+}
diff --git a/fuse_sideload/include/fuse_provider.h b/fuse_sideload/include/fuse_provider.h
index 59059cf9b..8d4ea4073 100644
--- a/fuse_sideload/include/fuse_provider.h
+++ b/fuse_sideload/include/fuse_provider.h
@@ -18,10 +18,13 @@
#include <stdint.h>
+#include <memory>
#include <string>
#include <android-base/unique_fd.h>
+#include "otautil/rangeset.h"
+
// This is the base class to read data from source and provide the data to FUSE.
class FuseDataProvider {
public:
@@ -70,3 +73,28 @@ class FuseFileDataProvider : public FuseDataProvider {
// The underlying source to read data from.
android::base::unique_fd fd_;
};
+
+// This class parses a block map and reads data from the underlying block device.
+class FuseBlockDataProvider : public FuseDataProvider {
+ public:
+ // Constructs the fuse provider from the block map.
+ static std::unique_ptr<FuseBlockDataProvider> CreateFromBlockMap(
+ const std::string& block_map_path, uint32_t fuse_block_size);
+
+ RangeSet ranges() const {
+ return ranges_;
+ }
+ bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
+ uint32_t start_block) const override;
+ void Close() override;
+
+ private:
+ FuseBlockDataProvider(uint64_t file_size, uint32_t fuse_block_size, android::base::unique_fd&& fd,
+ uint32_t source_block_size, RangeSet ranges);
+ // The underlying block device to read data from.
+ android::base::unique_fd fd_;
+ // The block size of the source block device.
+ uint32_t source_block_size_;
+ // The block ranges from the source block device that consist of the file
+ RangeSet ranges_;
+};
diff --git a/install/Android.bp b/install/Android.bp
index b18e29021..4696e501e 100644
--- a/install/Android.bp
+++ b/install/Android.bp
@@ -66,6 +66,7 @@ cc_library_static {
"package.cpp",
"verifier.cpp",
"wipe_data.cpp",
+ "wipe_device.cpp",
],
shared_libs: [
diff --git a/install/adb_install.cpp b/install/adb_install.cpp
index 9dfe0407f..2de1075d2 100644
--- a/install/adb_install.cpp
+++ b/install/adb_install.cpp
@@ -43,6 +43,7 @@
#include "fuse_sideload.h"
#include "install/install.h"
+#include "install/wipe_data.h"
#include "minadbd_types.h"
#include "otautil/sysutil.h"
#include "recovery_ui/device.h"
@@ -89,7 +90,7 @@ static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) {
// Installs the package from FUSE. Returns the installation result and whether it should continue
// waiting for new commands.
-static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) {
+static auto AdbInstallPackageHandler(RecoveryUI* ui, InstallResult* result) {
// How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long
// because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME
// will start to exist once the host connects and starts serving a package. Poll for its
@@ -109,7 +110,7 @@ static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) {
break;
}
}
- *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui);
+ *result = InstallPackage(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui);
break;
}
@@ -119,7 +120,7 @@ static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) {
return std::make_pair(*result == INSTALL_SUCCESS, should_continue);
}
-static auto AdbRebootHandler(MinadbdCommand command, int* result,
+static auto AdbRebootHandler(MinadbdCommand command, InstallResult* result,
Device::BuiltinAction* reboot_action) {
// Use Device::REBOOT_{FASTBOOT,RECOVERY,RESCUE}, instead of the ones with ENTER_. This allows
// rebooting back into fastboot/recovery/rescue mode through bootloader, which may use a newly
@@ -330,7 +331,7 @@ static void CreateMinadbdServiceAndExecuteCommands(
signal(SIGPIPE, SIG_DFL);
}
-int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action) {
+InstallResult ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) {
// Save the usb state to restore after the sideload operation.
std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
// Clean up state and stop adbd.
@@ -339,15 +340,9 @@ int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot
return INSTALL_ERROR;
}
- if (!rescue_mode) {
- ui->Print(
- "\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");
- }
+ RecoveryUI* ui = device->GetUI();
- int install_result = INSTALL_ERROR;
+ InstallResult install_result = INSTALL_ERROR;
std::map<MinadbdCommand, CommandFunction> command_map{
{ MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) },
{ MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid,
@@ -363,6 +358,18 @@ int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot
std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) },
};
+ if (!rescue_mode) {
+ ui->Print(
+ "\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);
+ });
+ }
+
CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode);
// Clean up before switching to the older state, for example setting the state
diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_sdcard_install.cpp
index 1aa8768e7..a5caa6e75 100644
--- a/install/fuse_sdcard_install.cpp
+++ b/install/fuse_sdcard_install.cpp
@@ -133,7 +133,7 @@ static bool StartSdcardFuse(const std::string& path) {
return run_fuse_sideload(std::move(file_data_reader)) == 0;
}
-int ApplyFromSdcard(Device* device, RecoveryUI* ui) {
+InstallResult ApplyFromSdcard(Device* device, RecoveryUI* ui) {
if (ensure_path_mounted(SDCARD_ROOT) != 0) {
LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n";
return INSTALL_ERROR;
@@ -159,9 +159,8 @@ int ApplyFromSdcard(Device* device, RecoveryUI* ui) {
_exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
}
- // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child
- // process is ready.
- int result = INSTALL_ERROR;
+ // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child process is ready.
+ InstallResult result = INSTALL_ERROR;
int status;
bool waited = false;
for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
@@ -184,7 +183,7 @@ int ApplyFromSdcard(Device* device, RecoveryUI* ui) {
}
}
- result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui);
+ result = InstallPackage(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /* retry_count */, ui);
break;
}
diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h
index 49b32b54f..880022361 100644
--- a/install/include/install/adb_install.h
+++ b/install/include/install/adb_install.h
@@ -16,10 +16,10 @@
#pragma once
-#include <recovery_ui/device.h>
-#include <recovery_ui/ui.h>
+#include "install/install.h"
+#include "recovery_ui/device.h"
-// Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum
-// InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with
-// the reboot target set in reboot_action.
-int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action);
+// Applies a package via `adb sideload` or `adb rescue`. Returns the install result. When a reboot
+// has been requested, INSTALL_REBOOT will be the return value, with the reboot target set in
+// reboot_action.
+InstallResult ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action);
diff --git a/install/include/install/fuse_sdcard_install.h b/install/include/install/fuse_sdcard_install.h
index d9214ca3b..e5bb01f09 100644
--- a/install/include/install/fuse_sdcard_install.h
+++ b/install/include/install/fuse_sdcard_install.h
@@ -16,7 +16,8 @@
#pragma once
+#include "install/install.h"
#include "recovery_ui/device.h"
#include "recovery_ui/ui.h"
-int ApplyFromSdcard(Device* device, RecoveryUI* ui);
+InstallResult ApplyFromSdcard(Device* device, RecoveryUI* ui);
diff --git a/install/include/install/install.h b/install/include/install/install.h
index c0a8f1f4c..44a5cde91 100644
--- a/install/include/install/install.h
+++ b/install/include/install/install.h
@@ -47,8 +47,8 @@ enum class OtaType {
// Installs the given update package. This function should also wipe the cache partition after a
// successful installation if |should_wipe_cache| is true or an updater command asks to wipe the
// cache.
-int install_package(const std::string& package, bool should_wipe_cache, bool needs_mount,
- int retry_count, RecoveryUI* ui);
+InstallResult InstallPackage(const std::string& package, bool should_wipe_cache, bool needs_mount,
+ int retry_count, RecoveryUI* ui);
// Verifies the package by ota keys. Returns true if the package is verified successfully,
// otherwise returns false.
@@ -58,14 +58,11 @@ bool verify_package(Package* package, RecoveryUI* ui);
// result to |metadata|. Return true if succeed, otherwise return false.
bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata);
-// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe.
-std::vector<std::string> GetWipePartitionList(Package* wipe_package);
-
// Verifies the compatibility info in a Treble-compatible package. Returns true directly if the
// entry doesn't exist.
bool verify_package_compatibility(ZipArchiveHandle package_zip);
-// Checks if the the metadata in the OTA package has expected values. Returns 0 on success.
-// Mandatory checks: ota-type, pre-device and serial number(if presents)
-// AB OTA specific checks: pre-build version, fingerprint, timestamp.
-int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type);
+// Checks if the metadata in the OTA package has expected values. Mandatory checks: ota-type,
+// pre-device and serial number (if presents). A/B OTA specific checks: pre-build version,
+// fingerprint, timestamp.
+bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type);
diff --git a/install/include/install/wipe_device.h b/install/include/install/wipe_device.h
new file mode 100644
index 000000000..c60b99997
--- /dev/null
+++ b/install/include/install/wipe_device.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "install/package.h"
+#include "recovery_ui/device.h"
+
+// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE.
+bool WipeAbDevice(Device* device, size_t wipe_package_size);
+
+// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe.
+std::vector<std::string> GetWipePartitionList(Package* wipe_package);
diff --git a/install/include/private/setup_commands.h b/install/include/private/setup_commands.h
index 7fdc741d6..dcff76112 100644
--- a/install/include/private/setup_commands.h
+++ b/install/include/private/setup_commands.h
@@ -27,13 +27,13 @@
// |zip| located at |package|. Stores the command line that should be called into |cmd|. The
// |status_fd| is the file descriptor the child process should use to report back the progress of
// the update.
-int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
- int status_fd, std::vector<std::string>* cmd);
+bool SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
+ int status_fd, std::vector<std::string>* cmd);
// Sets up the commands for an A/B update. Extracts the needed entries from the open zip archive
// |zip| located at |package|. Stores the command line that should be called into |cmd|. The
// |status_fd| is the file descriptor the child process should use to report back the progress of
// the update. Note that since this applies to the sideloading flow only, it takes one less
// parameter |retry_count| than the non-A/B version.
-int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
- std::vector<std::string>* cmd);
+bool SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
+ std::vector<std::string>* cmd);
diff --git a/install/install.cpp b/install/install.cpp
index e2d470096..8d46641dc 100644
--- a/install/install.cpp
+++ b/install/install.cpp
@@ -73,9 +73,8 @@ bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::st
CHECK(metadata != nullptr);
static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
- ZipString path(METADATA_PATH);
ZipEntry entry;
- if (FindEntry(zip, path, &entry) != 0) {
+ if (FindEntry(zip, METADATA_PATH, &entry) != 0) {
LOG(ERROR) << "Failed to find " << METADATA_PATH;
return false;
}
@@ -139,14 +138,14 @@ static void ReadSourceTargetBuild(const std::map<std::string, std::string>& meta
// Checks the build version, fingerprint and timestamp in the metadata of the A/B package.
// Downgrading is not allowed unless explicitly enabled in the package and only for
// incremental packages.
-static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata) {
+static bool CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata) {
// Incremental updates should match the current build.
auto device_pre_build = android::base::GetProperty("ro.build.version.incremental", "");
auto pkg_pre_build = get_value(metadata, "pre-build-incremental");
if (!pkg_pre_build.empty() && pkg_pre_build != device_pre_build) {
LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected "
<< device_pre_build;
- return INSTALL_ERROR;
+ return false;
}
auto device_fingerprint = android::base::GetProperty("ro.build.fingerprint", "");
@@ -154,7 +153,7 @@ static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& met
if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != device_fingerprint) {
LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected "
<< device_fingerprint;
- return INSTALL_ERROR;
+ return false;
}
// Check for downgrade version.
@@ -172,36 +171,36 @@ static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& met
"newer than timestamp "
<< build_timestamp << " but package has timestamp " << pkg_post_timestamp
<< " and downgrade not allowed.";
- return INSTALL_ERROR;
+ return false;
}
if (pkg_pre_build_fingerprint.empty()) {
LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed.";
- return INSTALL_ERROR;
+ return false;
}
}
- return 0;
+ return true;
}
-int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type) {
+bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type) {
auto package_ota_type = get_value(metadata, "ota-type");
auto expected_ota_type = OtaTypeToString(ota_type);
if (ota_type != OtaType::AB && ota_type != OtaType::BRICK) {
LOG(INFO) << "Skip package metadata check for ota type " << expected_ota_type;
- return 0;
+ return true;
}
if (package_ota_type != expected_ota_type) {
LOG(ERROR) << "Unexpected ota package type, expects " << expected_ota_type << ", actual "
<< package_ota_type;
- return INSTALL_ERROR;
+ return false;
}
auto device = android::base::GetProperty("ro.product.device", "");
auto pkg_device = get_value(metadata, "pre-device");
if (pkg_device != device || pkg_device.empty()) {
LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << device;
- return INSTALL_ERROR;
+ return false;
}
// We allow the package to not have any serialno; and we also allow it to carry multiple serial
@@ -218,7 +217,7 @@ int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, Ota
}
if (!serial_number_match) {
LOG(ERROR) << "Package is for serial " << pkg_serial_no;
- return INSTALL_ERROR;
+ return false;
}
}
@@ -226,21 +225,20 @@ int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, Ota
return CheckAbSpecificMetadata(metadata);
}
- return 0;
+ return true;
}
-int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
- std::vector<std::string>* cmd) {
+bool SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
+ std::vector<std::string>* cmd) {
CHECK(cmd != nullptr);
// For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset
// in the zip file.
static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt";
- ZipString property_name(AB_OTA_PAYLOAD_PROPERTIES);
ZipEntry properties_entry;
- if (FindEntry(zip, property_name, &properties_entry) != 0) {
+ if (FindEntry(zip, AB_OTA_PAYLOAD_PROPERTIES, &properties_entry) != 0) {
LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD_PROPERTIES;
- return INSTALL_CORRUPT;
+ return false;
}
uint32_t properties_entry_length = properties_entry.uncompressed_length;
std::vector<uint8_t> payload_properties(properties_entry_length);
@@ -248,15 +246,14 @@ int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int
ExtractToMemory(zip, &properties_entry, payload_properties.data(), properties_entry_length);
if (err != 0) {
LOG(ERROR) << "Failed to extract " << AB_OTA_PAYLOAD_PROPERTIES << ": " << ErrorCodeString(err);
- return INSTALL_CORRUPT;
+ return false;
}
static constexpr const char* AB_OTA_PAYLOAD = "payload.bin";
- ZipString payload_name(AB_OTA_PAYLOAD);
ZipEntry payload_entry;
- if (FindEntry(zip, payload_name, &payload_entry) != 0) {
+ if (FindEntry(zip, AB_OTA_PAYLOAD, &payload_entry) != 0) {
LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD;
- return INSTALL_CORRUPT;
+ return false;
}
long payload_offset = payload_entry.offset;
*cmd = {
@@ -266,20 +263,19 @@ int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int
"--headers=" + std::string(payload_properties.begin(), payload_properties.end()),
android::base::StringPrintf("--status_fd=%d", status_fd),
};
- return 0;
+ return true;
}
-int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
- int status_fd, std::vector<std::string>* cmd) {
+bool SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
+ int status_fd, std::vector<std::string>* cmd) {
CHECK(cmd != nullptr);
// In non-A/B updates we extract the update binary from the package.
static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
- ZipString binary_name(UPDATE_BINARY_NAME);
ZipEntry binary_entry;
- if (FindEntry(zip, binary_name, &binary_entry) != 0) {
+ if (FindEntry(zip, UPDATE_BINARY_NAME, &binary_entry) != 0) {
LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME;
- return INSTALL_CORRUPT;
+ return false;
}
const std::string binary_path = Paths::Get().temporary_update_binary();
@@ -288,13 +284,12 @@ int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, i
open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755));
if (fd == -1) {
PLOG(ERROR) << "Failed to create " << binary_path;
- return INSTALL_ERROR;
+ return false;
}
- int32_t error = ExtractEntryToFile(zip, &binary_entry, fd);
- if (error != 0) {
+ if (auto error = ExtractEntryToFile(zip, &binary_entry, fd); error != 0) {
LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error);
- return INSTALL_ERROR;
+ return false;
}
// When executing the update binary contained in the package, the arguments passed are:
@@ -311,7 +306,7 @@ int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, i
if (retry_count > 0) {
cmd->push_back("retry");
}
- return 0;
+ return true;
}
static void log_max_temperature(int* max_temperature, const std::atomic<bool>& logger_finished) {
@@ -325,9 +320,9 @@ static void log_max_temperature(int* max_temperature, const std::atomic<bool>& l
}
// If the package contains an update binary, extract it and run it.
-static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
- std::vector<std::string>* log_buffer, int retry_count,
- int* max_temperature, RecoveryUI* ui) {
+static InstallResult TryUpdateBinary(const std::string& package, ZipArchiveHandle zip,
+ bool* wipe_cache, std::vector<std::string>* log_buffer,
+ int retry_count, int* max_temperature, RecoveryUI* ui) {
std::map<std::string, std::string> metadata;
if (!ReadMetadataFromPackage(zip, &metadata)) {
LOG(ERROR) << "Failed to parse metadata in the zip file";
@@ -335,11 +330,10 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b
}
bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
- // Verifies against the metadata in the package first.
- if (int check_status = is_ab ? CheckPackageMetadata(metadata, OtaType::AB) : 0;
- check_status != 0) {
+ // Verify against the metadata in the package first.
+ if (is_ab && !CheckPackageMetadata(metadata, OtaType::AB)) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
- return check_status;
+ return INSTALL_ERROR;
}
ReadSourceTargetBuild(metadata, log_buffer);
@@ -386,12 +380,12 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b
//
std::vector<std::string> args;
- if (int update_status =
+ if (auto setup_result =
is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args)
: SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args);
- update_status != 0) {
+ !setup_result) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
- return update_status;
+ return INSTALL_CORRUPT;
}
pid_t pid = fork();
@@ -510,9 +504,8 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) {
LOG(INFO) << "Verifying package compatibility...";
static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip";
- ZipString compatibility_entry_name(COMPATIBILITY_ZIP_ENTRY);
ZipEntry compatibility_entry;
- if (FindEntry(package_zip, compatibility_entry_name, &compatibility_entry) != 0) {
+ if (FindEntry(package_zip, COMPATIBILITY_ZIP_ENTRY, &compatibility_entry) != 0) {
LOG(INFO) << "Package doesn't contain " << COMPATIBILITY_ZIP_ENTRY << " entry";
return true;
}
@@ -536,7 +529,7 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) {
// Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents.
void* cookie;
- ret = StartIteration(zip_handle, &cookie, nullptr, nullptr);
+ ret = StartIteration(zip_handle, &cookie);
if (ret != 0) {
LOG(ERROR) << "Failed to start iterating zip entries: " << ErrorCodeString(ret);
CloseArchive(zip_handle);
@@ -546,13 +539,13 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) {
std::vector<std::string> compatibility_info;
ZipEntry info_entry;
- ZipString info_name;
+ std::string info_name;
while (Next(cookie, &info_entry, &info_name) == 0) {
std::string content(info_entry.uncompressed_length, '\0');
int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast<uint8_t*>(&content[0]),
info_entry.uncompressed_length);
if (ret != 0) {
- LOG(ERROR) << "Failed to read " << info_name.name << ": " << ErrorCodeString(ret);
+ LOG(ERROR) << "Failed to read " << info_name << ": " << ErrorCodeString(ret);
CloseArchive(zip_handle);
return false;
}
@@ -571,9 +564,10 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) {
return false;
}
-static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount,
- std::vector<std::string>* log_buffer, int retry_count,
- int* max_temperature, RecoveryUI* ui) {
+static InstallResult VerifyAndInstallPackage(const std::string& path, bool* wipe_cache,
+ bool needs_mount, std::vector<std::string>* log_buffer,
+ int retry_count, int* max_temperature,
+ RecoveryUI* ui) {
ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
ui->Print("Finding update package...\n");
// Give verification half the progress bar...
@@ -624,16 +618,16 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo
ui->Print("Retry attempt: %d\n", retry_count);
}
ui->SetEnableReboot(false);
- int result =
- try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui);
+ auto result =
+ TryUpdateBinary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui);
ui->SetEnableReboot(true);
ui->Print("\n");
return result;
}
-int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount,
- int retry_count, RecoveryUI* ui) {
+InstallResult InstallPackage(const std::string& path, bool should_wipe_cache, bool needs_mount,
+ int retry_count, RecoveryUI* ui) {
CHECK(!path.empty());
auto start = std::chrono::system_clock::now();
@@ -641,15 +635,15 @@ int install_package(const std::string& path, bool should_wipe_cache, bool needs_
int start_temperature = GetMaxValueFromThermalZone();
int max_temperature = start_temperature;
- int result;
+ InstallResult result;
std::vector<std::string> log_buffer;
if (setup_install_mounts() != 0) {
LOG(ERROR) << "failed to set up expected mounts for install; aborting";
result = INSTALL_ERROR;
} else {
bool updater_wipe_cache = false;
- result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer,
- retry_count, &max_temperature, ui);
+ result = VerifyAndInstallPackage(path, &updater_wipe_cache, needs_mount, &log_buffer,
+ retry_count, &max_temperature, ui);
should_wipe_cache = should_wipe_cache || updater_wipe_cache;
}
diff --git a/install/verifier.cpp b/install/verifier.cpp
index 6ba1d77c3..02759cdc2 100644
--- a/install/verifier.cpp
+++ b/install/verifier.cpp
@@ -311,8 +311,7 @@ int verify_file(VerifierInterface* package, const std::vector<Certificate>& keys
static std::vector<Certificate> IterateZipEntriesAndSearchForKeys(const ZipArchiveHandle& handle) {
void* cookie;
- ZipString suffix("x509.pem");
- int32_t iter_status = StartIteration(handle, &cookie, nullptr, &suffix);
+ int32_t iter_status = StartIteration(handle, &cookie, "", "x509.pem");
if (iter_status != 0) {
LOG(ERROR) << "Failed to iterate over entries in the certificate zipfile: "
<< ErrorCodeString(iter_status);
@@ -321,22 +320,21 @@ static std::vector<Certificate> IterateZipEntriesAndSearchForKeys(const ZipArchi
std::vector<Certificate> result;
- ZipString name;
+ std::string name;
ZipEntry entry;
while ((iter_status = Next(cookie, &entry, &name)) == 0) {
std::vector<uint8_t> pem_content(entry.uncompressed_length);
if (int32_t extract_status =
ExtractToMemory(handle, &entry, pem_content.data(), pem_content.size());
extract_status != 0) {
- LOG(ERROR) << "Failed to extract " << std::string(name.name, name.name + name.name_length);
+ LOG(ERROR) << "Failed to extract " << name;
return {};
}
Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
// Aborts the parsing if we fail to load one of the key file.
if (!LoadCertificateFromBuffer(pem_content, &cert)) {
- LOG(ERROR) << "Failed to load keys from "
- << std::string(name.name, name.name + name.name_length);
+ LOG(ERROR) << "Failed to load keys from " << name;
return {};
}
diff --git a/install/wipe_device.cpp b/install/wipe_device.cpp
new file mode 100644
index 000000000..89d5d31a3
--- /dev/null
+++ b/install/wipe_device.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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 "install/wipe_device.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ziparchive/zip_archive.h>
+
+#include "bootloader_message/bootloader_message.h"
+#include "install/install.h"
+#include "install/package.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
+
+std::vector<std::string> GetWipePartitionList(Package* wipe_package) {
+ ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle();
+ if (!zip) {
+ LOG(ERROR) << "Failed to get ZipArchiveHandle";
+ return {};
+ }
+
+ constexpr char RECOVERY_WIPE_ENTRY_NAME[] = "recovery.wipe";
+
+ std::string partition_list_content;
+ ZipEntry entry;
+ if (FindEntry(zip, RECOVERY_WIPE_ENTRY_NAME, &entry) == 0) {
+ uint32_t length = entry.uncompressed_length;
+ partition_list_content = std::string(length, '\0');
+ if (auto err = ExtractToMemory(
+ zip, &entry, reinterpret_cast<uint8_t*>(partition_list_content.data()), length);
+ err != 0) {
+ LOG(ERROR) << "Failed to extract " << RECOVERY_WIPE_ENTRY_NAME << ": "
+ << ErrorCodeString(err);
+ return {};
+ }
+ } else {
+ LOG(INFO) << "Failed to find " << RECOVERY_WIPE_ENTRY_NAME
+ << ", falling back to use the partition list on device.";
+
+ constexpr char RECOVERY_WIPE_ON_DEVICE[] = "/etc/recovery.wipe";
+ if (!android::base::ReadFileToString(RECOVERY_WIPE_ON_DEVICE, &partition_list_content)) {
+ PLOG(ERROR) << "failed to read \"" << RECOVERY_WIPE_ON_DEVICE << "\"";
+ return {};
+ }
+ }
+
+ std::vector<std::string> result;
+ auto lines = android::base::Split(partition_list_content, "\n");
+ for (const auto& line : lines) {
+ auto partition = android::base::Trim(line);
+ // Ignore '#' comment or empty lines.
+ if (android::base::StartsWith(partition, "#") || partition.empty()) {
+ continue;
+ }
+ result.push_back(line);
+ }
+
+ return result;
+}
+
+// Secure-wipes a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with
+// BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT.
+static bool SecureWipePartition(const std::string& partition) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open \"" << partition << "\"";
+ return false;
+ }
+
+ uint64_t range[2] = { 0, 0 };
+ if (ioctl(fd, BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) {
+ PLOG(ERROR) << "Failed to get partition size";
+ return false;
+ }
+ LOG(INFO) << "Secure-wiping \"" << partition << "\" from " << range[0] << " to " << range[1];
+
+ LOG(INFO) << " Trying BLKSECDISCARD...";
+ if (ioctl(fd, BLKSECDISCARD, &range) == -1) {
+ PLOG(WARNING) << " Failed";
+
+ // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT.
+ unsigned int zeroes;
+ if (ioctl(fd, BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) {
+ LOG(INFO) << " Trying BLKDISCARD...";
+ if (ioctl(fd, BLKDISCARD, &range) == -1) {
+ PLOG(ERROR) << " Failed";
+ return false;
+ }
+ } else {
+ LOG(INFO) << " Trying BLKZEROOUT...";
+ if (ioctl(fd, BLKZEROOUT, &range) == -1) {
+ PLOG(ERROR) << " Failed";
+ return false;
+ }
+ }
+ }
+
+ LOG(INFO) << " Done";
+ return true;
+}
+
+static std::unique_ptr<Package> ReadWipePackage(size_t wipe_package_size) {
+ if (wipe_package_size == 0) {
+ LOG(ERROR) << "wipe_package_size is zero";
+ return nullptr;
+ }
+
+ std::string wipe_package;
+ if (std::string err_str; !read_wipe_package(&wipe_package, wipe_package_size, &err_str)) {
+ PLOG(ERROR) << "Failed to read wipe package" << err_str;
+ return nullptr;
+ }
+
+ return Package::CreateMemoryPackage(
+ std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr);
+}
+
+// Checks if the wipe package matches expectation. If the check passes, reads the list of
+// partitions to wipe from the package. Checks include
+// 1. verify the package.
+// 2. check metadata (ota-type, pre-device and serial number if having one).
+static bool CheckWipePackage(Package* wipe_package, RecoveryUI* ui) {
+ if (!verify_package(wipe_package, ui)) {
+ LOG(ERROR) << "Failed to verify package";
+ return false;
+ }
+
+ ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle();
+ if (!zip) {
+ LOG(ERROR) << "Failed to get ZipArchiveHandle";
+ return false;
+ }
+
+ std::map<std::string, std::string> metadata;
+ if (!ReadMetadataFromPackage(zip, &metadata)) {
+ LOG(ERROR) << "Failed to parse metadata in the zip file";
+ return false;
+ }
+
+ return CheckPackageMetadata(metadata, OtaType::BRICK);
+}
+
+bool WipeAbDevice(Device* device, size_t wipe_package_size) {
+ auto ui = device->GetUI();
+ ui->SetBackground(RecoveryUI::ERASING);
+ ui->SetProgressType(RecoveryUI::INDETERMINATE);
+
+ auto wipe_package = ReadWipePackage(wipe_package_size);
+ if (!wipe_package) {
+ LOG(ERROR) << "Failed to open wipe package";
+ return false;
+ }
+
+ if (!CheckWipePackage(wipe_package.get(), ui)) {
+ LOG(ERROR) << "Failed to verify wipe package";
+ return false;
+ }
+
+ auto partition_list = GetWipePartitionList(wipe_package.get());
+ if (partition_list.empty()) {
+ LOG(ERROR) << "Empty wipe ab partition list";
+ return false;
+ }
+
+ for (const auto& partition : partition_list) {
+ // Proceed anyway even if it fails to wipe some partition.
+ SecureWipePartition(partition);
+ }
+ return true;
+}
diff --git a/minadbd/Android.bp b/minadbd/Android.bp
index 007e5057b..afd57ad2d 100644
--- a/minadbd/Android.bp
+++ b/minadbd/Android.bp
@@ -43,6 +43,10 @@ cc_library {
"minadbd_services.cpp",
],
+ static_libs: [
+ "libotautil",
+ ],
+
shared_libs: [
"libadbd",
"libbase",
@@ -96,6 +100,7 @@ cc_test {
static_libs: [
"libminadbd_services",
"libfusesideload",
+ "libotautil",
"libadbd",
"libcrypto",
],
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index 9b1999d90..1e6eb3165 100644
--- a/minadbd/minadbd_services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -25,10 +25,10 @@
#include <functional>
#include <memory>
+#include <set>
#include <string>
#include <string_view>
#include <thread>
-#include <unordered_set>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -104,7 +104,7 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args,
if (pieces.size() != 2 || !android::base::ParseInt(pieces[0], &file_size) || file_size <= 0 ||
!android::base::ParseInt(pieces[1], &block_size) || block_size <= 0) {
LOG(ERROR) << "bad sideload-host arguments: " << args;
- return kMinadbdPackageSizeError;
+ return kMinadbdHostCommandArgumentError;
}
LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size;
@@ -124,17 +124,17 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args,
return kMinadbdMessageFormatError;
}
- // Signal host-side adb to stop. For sideload mode, we always send kSideloadServiceExitSuccess
+ // Signal host-side adb to stop. For sideload mode, we always send kMinadbdServicesExitSuccess
// (i.e. "DONEDONE") regardless of the install result. For rescue mode, we send failure message on
// install error.
if (!rescue_mode || *status == MinadbdCommandStatus::kSuccess) {
- if (!android::base::WriteFully(sfd, kSideloadServiceExitSuccess,
- strlen(kSideloadServiceExitSuccess))) {
+ if (!android::base::WriteFully(sfd, kMinadbdServicesExitSuccess,
+ strlen(kMinadbdServicesExitSuccess))) {
return kMinadbdHostSocketIOError;
}
} else {
- if (!android::base::WriteFully(sfd, kSideloadServiceExitFailure,
- strlen(kSideloadServiceExitFailure))) {
+ if (!android::base::WriteFully(sfd, kMinadbdServicesExitFailure,
+ strlen(kMinadbdServicesExitFailure))) {
return kMinadbdHostSocketIOError;
}
}
@@ -156,19 +156,36 @@ static void RescueInstallHostService(unique_fd sfd, const std::string& args) {
}
}
+// Answers the query on a given property |prop|, by writing the result to the given |sfd|. The
+// result will be newline-terminated, so nonexistent or nonallowed query will be answered with "\n".
+// If given an empty string, dumps all the supported properties (analogous to `adb shell getprop`)
+// in lines, e.g. "[prop]: [value]".
static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) {
- static const std::unordered_set<std::string> kGetpropAllowedProps = {
- "ro.build.fingerprint",
+ static const std::set<std::string> kGetpropAllowedProps = {
"ro.build.date.utc",
+ "ro.build.fingerprint",
+ "ro.build.flavor",
+ "ro.build.id",
+ "ro.build.product",
+ "ro.build.tags",
+ "ro.build.version.incremental",
+ "ro.product.device",
+ "ro.product.vendor.device",
};
- auto allowed = kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end();
- if (!allowed) {
- return;
+ std::string result;
+ if (prop.empty()) {
+ for (const auto& key : kGetpropAllowedProps) {
+ auto value = android::base::GetProperty(key, "");
+ if (value.empty()) {
+ continue;
+ }
+ result += "[" + key + "]: [" + value + "]\n";
+ }
+ } else if (kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end()) {
+ result = android::base::GetProperty(prop, "") + "\n";
}
-
- auto result = android::base::GetProperty(prop, "");
if (result.empty()) {
- return;
+ result = "\n";
}
if (!android::base::WriteFully(sfd, result.data(), result.size())) {
exit(kMinadbdHostSocketIOError);
@@ -200,9 +217,37 @@ static void RebootHostService(unique_fd /* sfd */, const std::string& target) {
}
}
+static void WipeDeviceService(unique_fd fd, const std::string& args) {
+ auto pieces = android::base::Split(args, ":");
+ if (pieces.size() != 2 || pieces[0] != "userdata") {
+ LOG(ERROR) << "Failed to parse wipe device command arguments " << args;
+ exit(kMinadbdHostCommandArgumentError);
+ }
+
+ size_t message_size;
+ if (!android::base::ParseUint(pieces[1], &message_size) ||
+ message_size < strlen(kMinadbdServicesExitSuccess)) {
+ LOG(ERROR) << "Failed to parse wipe device message size in " << args;
+ exit(kMinadbdHostCommandArgumentError);
+ }
+
+ WriteCommandToFd(MinadbdCommand::kWipeData, minadbd_socket);
+ MinadbdCommandStatus status;
+ if (!WaitForCommandStatus(minadbd_socket, &status)) {
+ exit(kMinadbdMessageFormatError);
+ }
+
+ std::string response = (status == MinadbdCommandStatus::kSuccess) ? kMinadbdServicesExitSuccess
+ : kMinadbdServicesExitFailure;
+ response += std::string(message_size - response.size(), '\0');
+ if (!android::base::WriteFully(fd, response.c_str(), response.size())) {
+ exit(kMinadbdHostSocketIOError);
+ }
+}
+
unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) {
// Common services that are supported both in sideload and rescue modes.
- if (ConsumePrefix(&name, "reboot:")) {
+ if (android::base::ConsumePrefix(&name, "reboot:")) {
// "reboot:<target>", where target must be one of the following.
std::string args(name);
if (args.empty() || args == "bootloader" || args == "rescue" || args == "recovery" ||
@@ -215,17 +260,23 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport *
// Rescue-specific services.
if (rescue_mode) {
- if (ConsumePrefix(&name, "rescue-install:")) {
+ if (android::base::ConsumePrefix(&name, "rescue-install:")) {
// rescue-install:<file-size>:<block-size>
std::string args(name);
return create_service_thread(
"rescue-install", std::bind(RescueInstallHostService, std::placeholders::_1, args));
- } else if (ConsumePrefix(&name, "rescue-getprop:")) {
+ } else if (android::base::ConsumePrefix(&name, "rescue-getprop:")) {
// rescue-getprop:<prop>
std::string args(name);
return create_service_thread(
"rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args));
+ } else if (android::base::ConsumePrefix(&name, "rescue-wipe:")) {
+ // rescue-wipe:target:<message-size>
+ std::string args(name);
+ return create_service_thread("rescue-wipe",
+ std::bind(WipeDeviceService, std::placeholders::_1, args));
}
+
return unique_fd{};
}
@@ -234,7 +285,7 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport *
// This exit status causes recovery to print a special error message saying to use a newer adb
// (that supports sideload-host).
exit(kMinadbdAdbVersionError);
- } else if (ConsumePrefix(&name, "sideload-host:")) {
+ } else if (android::base::ConsumePrefix(&name, "sideload-host:")) {
// sideload-host:<file-size>:<block-size>
std::string args(name);
return create_service_thread("sideload-host",
diff --git a/minadbd/minadbd_services_test.cpp b/minadbd/minadbd_services_test.cpp
index 593180bb3..f87873792 100644
--- a/minadbd/minadbd_services_test.cpp
+++ b/minadbd/minadbd_services_test.cpp
@@ -122,7 +122,7 @@ class MinadbdServicesTest : public ::testing::Test {
TEST_F(MinadbdServicesTest, SideloadHostService_wrong_size_argument) {
ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:abc:4096"),
- ::testing::ExitedWithCode(kMinadbdPackageSizeError), "");
+ ::testing::ExitedWithCode(kMinadbdHostCommandArgumentError), "");
}
TEST_F(MinadbdServicesTest, SideloadHostService_wrong_block_size) {
diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h
index b370b7952..99fd45e83 100644
--- a/minadbd/minadbd_types.h
+++ b/minadbd/minadbd_types.h
@@ -30,7 +30,7 @@ enum MinadbdErrorCode : int {
kMinadbdSocketIOError = 2,
kMinadbdMessageFormatError = 3,
kMinadbdAdbVersionError = 4,
- kMinadbdPackageSizeError = 5,
+ kMinadbdHostCommandArgumentError = 5,
kMinadbdFuseStartError = 6,
kMinadbdUnsupportedCommandError = 7,
kMinadbdCommandExecutionError = 8,
@@ -51,6 +51,8 @@ enum class MinadbdCommand : uint32_t {
kRebootFastboot = 4,
kRebootRecovery = 5,
kRebootRescue = 6,
+ kWipeCache = 7,
+ kWipeData = 8,
// Last but invalid command.
kError,
diff --git a/misc_writer/Android.bp b/misc_writer/Android.bp
new file mode 100644
index 000000000..567143c79
--- /dev/null
+++ b/misc_writer/Android.bp
@@ -0,0 +1,40 @@
+//
+// 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.
+//
+
+cc_binary {
+ name: "misc_writer",
+ vendor: true,
+
+ srcs: [
+ "misc_writer.cpp",
+ ],
+
+ cpp_std: "experimental",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libbase",
+ ],
+
+ static_libs: [
+ "libbootloader_message_vendor",
+ "libfstab",
+ ],
+}
diff --git a/misc_writer/misc_writer.cpp b/misc_writer/misc_writer.cpp
new file mode 100644
index 000000000..1d9702ebf
--- /dev/null
+++ b/misc_writer/misc_writer.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <bootloader_message/bootloader_message.h>
+
+using namespace std::string_literals;
+
+static std::vector<uint8_t> ParseHexString(std::string_view hex_string) {
+ auto length = hex_string.size();
+ if (length % 2 != 0 || length == 0) {
+ return {};
+ }
+
+ std::vector<uint8_t> result(length / 2);
+ for (size_t i = 0; i < length / 2; i++) {
+ auto sub = "0x" + std::string(hex_string.substr(i * 2, 2));
+ if (!android::base::ParseUint(sub, &result[i])) {
+ return {};
+ }
+ }
+ return result;
+}
+
+static int Usage(std::string_view name) {
+ std::cerr << name << " usage:\n";
+ std::cerr << name << " [--vendor-space-offset <offset>] --hex-string 0xABCDEF\n";
+ std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc "
+ "partition. Offset defaults to 0 if unspecified.\n";
+ return EXIT_FAILURE;
+}
+
+// misc_writer is a vendor tool that writes data to the vendor space in /misc.
+int main(int argc, char** argv) {
+ constexpr struct option OPTIONS[] = {
+ { "vendor-space-offset", required_argument, nullptr, 0 },
+ { "hex-string", required_argument, nullptr, 0 },
+ { nullptr, 0, nullptr, 0 },
+ };
+
+ // Offset defaults to 0 if unspecified.
+ size_t offset = 0;
+ std::string_view hex_string;
+
+ int arg;
+ int option_index;
+ while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
+ if (arg != 0) {
+ LOG(ERROR) << "Invalid command argument";
+ return Usage(argv[0]);
+ }
+ auto option_name = OPTIONS[option_index].name;
+ if (option_name == "vendor-space-offset"s) {
+ if (!android::base::ParseUint(optarg, &offset)) {
+ LOG(ERROR) << "Failed to parse the offset: " << optarg;
+ return Usage(argv[0]);
+ }
+ } else if (option_name == "hex-string"s) {
+ hex_string = optarg;
+ }
+ }
+
+ if (hex_string.starts_with("0x") || hex_string.starts_with("0X")) {
+ hex_string = hex_string.substr(2);
+ }
+ if (hex_string.empty()) {
+ LOG(ERROR) << "Invalid input hex string: " << hex_string;
+ return Usage(argv[0]);
+ }
+
+ auto data = ParseHexString(hex_string);
+ if (data.empty()) {
+ LOG(ERROR) << "Failed to parse the input hex string: " << hex_string;
+ return EXIT_FAILURE;
+ }
+ if (std::string err; !WriteMiscPartitionVendorSpace(data.data(), data.size(), offset, &err)) {
+ LOG(ERROR) << "Failed to write to misc partition: " << err;
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/otautil/Android.bp b/otautil/Android.bp
index 73398c3aa..871dcae9a 100644
--- a/otautil/Android.bp
+++ b/otautil/Android.bp
@@ -24,12 +24,16 @@ cc_library_static {
// Minimal set of files to support host build.
srcs: [
+ "dirutil.cpp",
"paths.cpp",
"rangeset.cpp",
+ "sysutil.cpp",
],
shared_libs: [
"libbase",
+ "libcutils",
+ "libselinux",
],
export_include_dirs: [
@@ -39,12 +43,10 @@ cc_library_static {
target: {
android: {
srcs: [
- "dirutil.cpp",
"logging.cpp",
"mounts.cpp",
"parse_install_logs.cpp",
"roots.cpp",
- "sysutil.cpp",
"thermalutil.cpp",
],
@@ -57,10 +59,8 @@ cc_library_static {
],
shared_libs: [
- "libcutils",
"libext4_utils",
"libfs_mgr",
- "libselinux",
],
export_static_lib_headers: [
diff --git a/otautil/include/otautil/rangeset.h b/otautil/include/otautil/rangeset.h
index e91d02ca6..a18c30e29 100644
--- a/otautil/include/otautil/rangeset.h
+++ b/otautil/include/otautil/rangeset.h
@@ -18,6 +18,7 @@
#include <stddef.h>
+#include <optional>
#include <string>
#include <utility>
#include <vector>
@@ -49,6 +50,12 @@ class RangeSet {
// bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" and "5,7" are not overlapped.
bool Overlaps(const RangeSet& other) const;
+ // Returns a subset of ranges starting from |start_index| with respect to the original range. The
+ // output range will have |num_of_blocks| blocks in size. Returns std::nullopt if the input is
+ // invalid. e.g. RangeSet({{0, 5}, {10, 15}}).GetSubRanges(1, 5) returns
+ // RangeSet({{1, 5}, {10, 11}}).
+ std::optional<RangeSet> GetSubRanges(size_t start_index, size_t num_of_blocks) const;
+
// Returns a vector of RangeSets that contain the same set of blocks represented by the current
// RangeSet. The RangeSets in the vector contain similar number of blocks, with a maximum delta
// of 1-block between any two of them. For example, 14 blocks would be split into 4 + 4 + 3 + 3,
diff --git a/otautil/include/otautil/roots.h b/otautil/include/otautil/roots.h
index 482f3d050..2ab3f4549 100644
--- a/otautil/include/otautil/roots.h
+++ b/otautil/include/otautil/roots.h
@@ -53,7 +53,3 @@ int format_volume(const std::string& volume, const std::string& directory);
// Ensure that all and only the volumes that packages expect to find
// mounted (/tmp and /cache) are mounted. Returns 0 on success.
int setup_install_mounts();
-
-bool logical_partitions_mapped();
-
-std::string get_system_root();
diff --git a/otautil/include/otautil/sysutil.h b/otautil/include/otautil/sysutil.h
index 692a99e9d..326db8644 100644
--- a/otautil/include/otautil/sysutil.h
+++ b/otautil/include/otautil/sysutil.h
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#ifndef _OTAUTIL_SYSUTIL
-#define _OTAUTIL_SYSUTIL
+#pragma once
#include <sys/types.h>
#include <string>
+#include <string_view>
#include <vector>
#include "rangeset.h"
@@ -101,13 +101,14 @@ class MemMapping {
std::vector<MappedRange> ranges_;
};
-// Wrapper function to trigger a reboot, by additionally handling quiescent reboot mode. The
-// command should start with "reboot," (e.g. "reboot,bootloader" or "reboot,").
-bool reboot(const std::string& command);
+// Reboots the device into the specified target, by additionally handling quiescent reboot mode.
+// All unknown targets reboot into Android.
+bool Reboot(std::string_view target);
+
+// Triggers a shutdown.
+bool Shutdown(std::string_view target);
// Returns a null-terminated char* array, where the elements point to the C-strings in the given
// vector, plus an additional nullptr at the end. This is a helper function that facilitates
// calling C functions (such as getopt(3)) that expect an array of C-strings.
std::vector<char*> StringVectorToNullTerminatedArray(const std::vector<std::string>& args);
-
-#endif // _OTAUTIL_SYSUTIL
diff --git a/otautil/rangeset.cpp b/otautil/rangeset.cpp
index 5ab8e08fe..8ee99dd7a 100644
--- a/otautil/rangeset.cpp
+++ b/otautil/rangeset.cpp
@@ -184,6 +184,58 @@ bool RangeSet::Overlaps(const RangeSet& other) const {
return false;
}
+std::optional<RangeSet> RangeSet::GetSubRanges(size_t start_index, size_t num_of_blocks) const {
+ size_t end_index = start_index + num_of_blocks; // The index of final block to read plus one
+ if (start_index > end_index || end_index > blocks_) {
+ LOG(ERROR) << "Failed to get the sub ranges for start_index " << start_index
+ << " num_of_blocks " << num_of_blocks
+ << " total number of blocks the range contains is " << blocks_;
+ return std::nullopt;
+ }
+
+ if (num_of_blocks == 0) {
+ LOG(WARNING) << "num_of_blocks is zero when calling GetSubRanges()";
+ return RangeSet();
+ }
+
+ RangeSet result;
+ size_t current_index = 0;
+ for (const auto& [range_start, range_end] : ranges_) {
+ CHECK_LT(range_start, range_end);
+ size_t blocks_in_range = range_end - range_start;
+ // Linear search to skip the ranges until we reach start_block.
+ if (current_index + blocks_in_range <= start_index) {
+ current_index += blocks_in_range;
+ continue;
+ }
+
+ size_t trimmed_range_start = range_start;
+ // We have found the first block range to read, trim the heading blocks.
+ if (current_index < start_index) {
+ trimmed_range_start += start_index - current_index;
+ }
+ // Trim the trailing blocks if the last range has more blocks than desired; also return the
+ // result.
+ if (current_index + blocks_in_range >= end_index) {
+ size_t trimmed_range_end = range_end - (current_index + blocks_in_range - end_index);
+ if (!result.PushBack({ trimmed_range_start, trimmed_range_end })) {
+ return std::nullopt;
+ }
+
+ return result;
+ }
+
+ if (!result.PushBack({ trimmed_range_start, range_end })) {
+ return std::nullopt;
+ }
+ current_index += blocks_in_range;
+ }
+
+ LOG(ERROR) << "Failed to construct byte ranges to read, start_block: " << start_index
+ << ", num_of_blocks: " << num_of_blocks << " total number of blocks: " << blocks_;
+ return std::nullopt;
+}
+
// Ranges in the the set should be mutually exclusive; and they're sorted by the start block.
SortedRangeSet::SortedRangeSet(std::vector<Range>&& pairs) : RangeSet(std::move(pairs)) {
std::sort(ranges_.begin(), ranges_.end());
diff --git a/otautil/roots.cpp b/otautil/roots.cpp
index 815d644e5..a778e05ff 100644
--- a/otautil/roots.cpp
+++ b/otautil/roots.cpp
@@ -275,11 +275,3 @@ int setup_install_mounts() {
}
return 0;
}
-
-bool logical_partitions_mapped() {
- return android::fs_mgr::LogicalPartitionsMapped();
-}
-
-std::string get_system_root() {
- return android::fs_mgr::GetSystemRoot();
-}
diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp
index 8366fa0ac..a8829858d 100644
--- a/otautil/sysutil.cpp
+++ b/otautil/sysutil.cpp
@@ -94,6 +94,11 @@ BlockMapData BlockMapData::ParseBlockMapFile(const std::string& block_map_path)
remaining_blocks -= range_blocks;
}
+ if (remaining_blocks != 0) {
+ LOG(ERROR) << "Invalid ranges: remaining blocks " << remaining_blocks;
+ return {};
+ }
+
return BlockMapData(block_dev, file_size, blksize, std::move(ranges));
}
@@ -214,14 +219,21 @@ MemMapping::~MemMapping() {
ranges_.clear();
}
-bool reboot(const std::string& command) {
- std::string cmd = command;
- if (android::base::GetBoolProperty("ro.boot.quiescent", false)) {
+bool Reboot(std::string_view target) {
+ std::string cmd = "reboot," + std::string(target);
+ // Honor the quiescent mode if applicable.
+ if (target != "bootloader" && target != "fastboot" &&
+ android::base::GetBoolProperty("ro.boot.quiescent", false)) {
cmd += ",quiescent";
}
return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd);
}
+bool Shutdown(std::string_view target) {
+ std::string cmd = "shutdown," + std::string(target);
+ return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd);
+}
+
std::vector<char*> StringVectorToNullTerminatedArray(const std::vector<std::string>& args) {
std::vector<char*> result(args.size());
std::transform(args.cbegin(), args.cend(), result.begin(),
diff --git a/recovery.cpp b/recovery.cpp
index f9b3bfc0b..eb0c2b216 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -18,11 +18,9 @@
#include <ctype.h>
#include <errno.h>
-#include <fcntl.h>
#include <getopt.h>
#include <inttypes.h>
#include <limits.h>
-#include <linux/fs.h>
#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
@@ -30,8 +28,8 @@
#include <sys/types.h>
#include <unistd.h>
-#include <algorithm>
#include <functional>
+#include <iterator>
#include <memory>
#include <string>
#include <vector>
@@ -42,12 +40,12 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <bootloader_message/bootloader_message.h>
#include <cutils/properties.h> /* for property_list */
+#include <fs_mgr/roots.h>
#include <healthhalutils/HealthHalUtils.h>
#include <ziparchive/zip_archive.h>
+#include "bootloader_message/bootloader_message.h"
#include "common.h"
#include "fsck_unshare_blocks.h"
#include "install/adb_install.h"
@@ -55,6 +53,7 @@
#include "install/install.h"
#include "install/package.h"
#include "install/wipe_data.h"
+#include "install/wipe_device.h"
#include "otautil/error_code.h"
#include "otautil/logging.h"
#include "otautil/paths.h"
@@ -115,12 +114,12 @@ const char* reason = nullptr;
* 3. main system reboots into recovery
* 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
* -- after this, rebooting will attempt to reinstall the update --
- * 5. install_package() attempts to install the update
+ * 5. InstallPackage() attempts to install the update
* NOTE: the package install must itself be restartable from any point
* 6. finish_recovery() erases BCB
* -- after this, rebooting will (try to) restart the main system --
* 7. ** if install failed **
- * 7a. prompt_and_wait() shows an error icon and waits for the user
+ * 7a. PromptAndWait() shows an error icon and waits for the user
* 7b. the user reboots (pulling the battery, etc) into the main system
*/
@@ -221,165 +220,6 @@ static InstallResult prompt_and_wipe_data(Device* device) {
}
}
-// Secure-wipe a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with
-// BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT.
-static bool secure_wipe_partition(const std::string& partition) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY)));
- if (fd == -1) {
- PLOG(ERROR) << "Failed to open \"" << partition << "\"";
- return false;
- }
-
- uint64_t range[2] = { 0, 0 };
- if (ioctl(fd, BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) {
- PLOG(ERROR) << "Failed to get partition size";
- return false;
- }
- LOG(INFO) << "Secure-wiping \"" << partition << "\" from " << range[0] << " to " << range[1];
-
- LOG(INFO) << " Trying BLKSECDISCARD...";
- if (ioctl(fd, BLKSECDISCARD, &range) == -1) {
- PLOG(WARNING) << " Failed";
-
- // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT.
- unsigned int zeroes;
- if (ioctl(fd, BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) {
- LOG(INFO) << " Trying BLKDISCARD...";
- if (ioctl(fd, BLKDISCARD, &range) == -1) {
- PLOG(ERROR) << " Failed";
- return false;
- }
- } else {
- LOG(INFO) << " Trying BLKZEROOUT...";
- if (ioctl(fd, BLKZEROOUT, &range) == -1) {
- PLOG(ERROR) << " Failed";
- return false;
- }
- }
- }
-
- LOG(INFO) << " Done";
- return true;
-}
-
-static std::unique_ptr<Package> ReadWipePackage(size_t wipe_package_size) {
- if (wipe_package_size == 0) {
- LOG(ERROR) << "wipe_package_size is zero";
- return nullptr;
- }
-
- std::string wipe_package;
- std::string err_str;
- if (!read_wipe_package(&wipe_package, wipe_package_size, &err_str)) {
- PLOG(ERROR) << "Failed to read wipe package" << err_str;
- return nullptr;
- }
-
- return Package::CreateMemoryPackage(
- std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr);
-}
-
-// Checks if the wipe package matches expectation. If the check passes, reads the list of
-// partitions to wipe from the package. Checks include
-// 1. verify the package.
-// 2. check metadata (ota-type, pre-device and serial number if having one).
-static bool CheckWipePackage(Package* wipe_package) {
- if (!verify_package(wipe_package, ui)) {
- LOG(ERROR) << "Failed to verify package";
- return false;
- }
-
- ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle();
- if (!zip) {
- LOG(ERROR) << "Failed to get ZipArchiveHandle";
- return false;
- }
-
- std::map<std::string, std::string> metadata;
- if (!ReadMetadataFromPackage(zip, &metadata)) {
- LOG(ERROR) << "Failed to parse metadata in the zip file";
- return false;
- }
-
- return CheckPackageMetadata(metadata, OtaType::BRICK) == 0;
-}
-
-std::vector<std::string> GetWipePartitionList(Package* wipe_package) {
- ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle();
- if (!zip) {
- LOG(ERROR) << "Failed to get ZipArchiveHandle";
- return {};
- }
-
- static constexpr const char* RECOVERY_WIPE_ENTRY_NAME = "recovery.wipe";
-
- std::string partition_list_content;
- ZipString path(RECOVERY_WIPE_ENTRY_NAME);
- ZipEntry entry;
- if (FindEntry(zip, path, &entry) == 0) {
- uint32_t length = entry.uncompressed_length;
- partition_list_content = std::string(length, '\0');
- if (auto err = ExtractToMemory(
- zip, &entry, reinterpret_cast<uint8_t*>(partition_list_content.data()), length);
- err != 0) {
- LOG(ERROR) << "Failed to extract " << RECOVERY_WIPE_ENTRY_NAME << ": "
- << ErrorCodeString(err);
- return {};
- }
- } else {
- LOG(INFO) << "Failed to find " << RECOVERY_WIPE_ENTRY_NAME
- << ", falling back to use the partition list on device.";
-
- static constexpr const char* RECOVERY_WIPE_ON_DEVICE = "/etc/recovery.wipe";
- if (!android::base::ReadFileToString(RECOVERY_WIPE_ON_DEVICE, &partition_list_content)) {
- PLOG(ERROR) << "failed to read \"" << RECOVERY_WIPE_ON_DEVICE << "\"";
- return {};
- }
- }
-
- std::vector<std::string> result;
- std::vector<std::string> lines = android::base::Split(partition_list_content, "\n");
- for (const std::string& line : lines) {
- std::string partition = android::base::Trim(line);
- // Ignore '#' comment or empty lines.
- if (android::base::StartsWith(partition, "#") || partition.empty()) {
- continue;
- }
- result.push_back(line);
- }
-
- return result;
-}
-
-// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE.
-static bool wipe_ab_device(size_t wipe_package_size) {
- ui->SetBackground(RecoveryUI::ERASING);
- ui->SetProgressType(RecoveryUI::INDETERMINATE);
-
- auto wipe_package = ReadWipePackage(wipe_package_size);
- if (!wipe_package) {
- LOG(ERROR) << "Failed to open wipe package";
- return false;
- }
-
- if (!CheckWipePackage(wipe_package.get())) {
- LOG(ERROR) << "Failed to verify wipe package";
- return false;
- }
-
- auto partition_list = GetWipePartitionList(wipe_package.get());
- if (partition_list.empty()) {
- LOG(ERROR) << "Empty wipe ab partition list";
- return false;
- }
-
- for (const auto& partition : partition_list) {
- // Proceed anyway even if it fails to wipe some partition.
- secure_wipe_partition(partition);
- }
- return true;
-}
-
static void choose_recovery_file(Device* device) {
std::vector<std::string> entries;
if (has_cache) {
@@ -473,14 +313,18 @@ static void run_graphics_test() {
ui->ShowText(true);
}
-// Returns REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default,
-// which is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery.
-static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
+// Shows the recovery UI and waits for user input. Returns one of the device builtin actions, such
+// as REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default, which
+// is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery.
+static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) {
for (;;) {
finish_recovery();
switch (status) {
case INSTALL_SUCCESS:
case INSTALL_NONE:
+ case INSTALL_SKIPPED:
+ case INSTALL_RETRY:
+ case INSTALL_KEY_INTERRUPTED:
ui->SetBackground(RecoveryUI::NO_COMMAND);
break;
@@ -488,6 +332,12 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
case INSTALL_CORRUPT:
ui->SetBackground(RecoveryUI::ERROR);
break;
+
+ case INSTALL_REBOOT:
+ // All the reboots should have been handled prior to entering PromptAndWait() or immediately
+ // after installing a package.
+ LOG(FATAL) << "Invalid status code of INSTALL_REBOOT";
+ break;
}
ui->SetProgressType(RecoveryUI::EMPTY);
@@ -506,6 +356,8 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
: device->InvokeMenuItem(chosen_item);
switch (chosen_action) {
+ case Device::REBOOT_FROM_FASTBOOT: // Can not happen
+ case Device::SHUTDOWN_FROM_FASTBOOT: // Can not happen
case Device::NO_ACTION:
break;
@@ -551,9 +403,9 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
if (chosen_action == Device::ENTER_RESCUE) {
// Switch to graphics screen.
ui->ShowText(false);
- status = ApplyFromAdb(ui, true /* rescue_mode */, &reboot_action);
+ status = ApplyFromAdb(device, true /* rescue_mode */, &reboot_action);
} else if (chosen_action == Device::APPLY_ADB_SIDELOAD) {
- status = ApplyFromAdb(ui, false /* rescue_mode */, &reboot_action);
+ status = ApplyFromAdb(device, false /* rescue_mode */, &reboot_action);
} else {
adb = false;
status = ApplyFromSdcard(device, ui);
@@ -588,8 +440,7 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
break;
}
case Device::MOUNT_SYSTEM:
- // the system partition is mounted at /mnt/system
- if (ensure_path_mounted_at(get_system_root(), "/mnt/system") != -1) {
+ if (ensure_path_mounted_at(android::fs_mgr::GetSystemRoot(), "/mnt/system") != -1) {
ui->Print("Mounted /system.\n");
}
break;
@@ -851,7 +702,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
ui->Print("Supported API: %d\n", kRecoveryApiVersion);
- int status = INSTALL_SUCCESS;
+ InstallResult status = INSTALL_SUCCESS;
// next_action indicates the next target to reboot into upon finishing the install. It could be
// overridden to a different reboot target per user request.
Device::BuiltinAction next_action = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
@@ -881,7 +732,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
set_retry_bootloader_message(retry_count + 1, args);
}
- status = install_package(update_package, should_wipe_cache, true, retry_count, ui);
+ status = InstallPackage(update_package, should_wipe_cache, true, retry_count, ui);
if (status != INSTALL_SUCCESS) {
ui->Print("Installation aborted.\n");
@@ -895,8 +746,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
// Print retry count on screen.
ui->Print("Retry attempt %d\n", retry_count);
- // Reboot and retry the update
- if (!reboot("reboot,recovery")) {
+ // Reboot back into recovery to retry the update.
+ if (!Reboot("recovery")) {
ui->Print("Reboot failed\n");
} else {
while (true) {
@@ -934,7 +785,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
status = INSTALL_ERROR;
}
} else if (should_wipe_ab) {
- if (!wipe_ab_device(wipe_package_size)) {
+ if (!WipeAbDevice(device, wipe_package_size)) {
status = INSTALL_ERROR;
}
} else if (sideload) {
@@ -946,7 +797,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
if (!sideload_auto_reboot) {
ui->ShowText(true);
}
- status = ApplyFromAdb(ui, false /* rescue_mode */, &next_action);
+ status = ApplyFromAdb(device, false /* rescue_mode */, &next_action);
ui->Print("\nInstall from ADB complete (status: %d).\n", status);
if (sideload_auto_reboot) {
status = INSTALL_REBOOT;
@@ -954,7 +805,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
}
} else if (rescue) {
save_current_log = true;
- status = ApplyFromAdb(ui, true /* rescue_mode */, &next_action);
+ status = ApplyFromAdb(device, true /* rescue_mode */, &next_action);
ui->Print("\nInstall from ADB complete (status: %d).\n", status);
} else if (fsck_unshare_blocks) {
if (!do_fsck_unshare_blocks()) {
@@ -989,7 +840,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
// for 5s followed by an automatic reboot.
if (status != INSTALL_REBOOT) {
if (status == INSTALL_NONE || ui->IsTextVisible()) {
- Device::BuiltinAction temp = prompt_and_wait(device, status);
+ auto temp = PromptAndWait(device, status);
if (temp != Device::NO_ACTION) {
next_action = temp;
}
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 6e69b7009..7fbdf9a08 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -41,8 +41,8 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
-#include <cutils/android_reboot.h>
#include <cutils/sockets.h>
+#include <fs_mgr/roots.h>
#include <private/android_logger.h> /* private pmsg functions */
#include <selinux/android.h>
#include <selinux/label.h>
@@ -471,27 +471,31 @@ int main(int argc, char** argv) {
switch (ret) {
case Device::SHUTDOWN:
ui->Print("Shutting down...\n");
- // TODO: Move all the reboots to reboot(), which should conditionally set quiescent flag.
- android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,");
+ Shutdown("userrequested,recovery");
+ break;
+
+ case Device::SHUTDOWN_FROM_FASTBOOT:
+ ui->Print("Shutting down...\n");
+ Shutdown("userrequested,fastboot");
break;
case Device::REBOOT_BOOTLOADER:
ui->Print("Rebooting to bootloader...\n");
- android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
+ Reboot("bootloader");
break;
case Device::REBOOT_FASTBOOT:
ui->Print("Rebooting to recovery/fastboot...\n");
- android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot");
+ Reboot("fastboot");
break;
case Device::REBOOT_RECOVERY:
ui->Print("Rebooting to recovery...\n");
- reboot("reboot,recovery");
+ Reboot("recovery");
break;
case Device::REBOOT_RESCUE: {
- // Not using `reboot("reboot,rescue")`, as it requires matching support in kernel and/or
+ // Not using `Reboot("rescue")`, as it requires matching support in kernel and/or
// bootloader.
bootloader_message boot = {};
strlcpy(boot.command, "boot-rescue", sizeof(boot.command));
@@ -502,14 +506,14 @@ int main(int argc, char** argv) {
continue;
}
ui->Print("Rebooting to recovery/rescue...\n");
- reboot("reboot,recovery");
+ Reboot("recovery");
break;
}
case Device::ENTER_FASTBOOT:
- if (logical_partitions_mapped()) {
+ if (android::fs_mgr::LogicalPartitionsMapped()) {
ui->Print("Partitions may be mounted - rebooting to enter fastboot.");
- android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot");
+ Reboot("fastboot");
} else {
LOG(INFO) << "Entering fastboot";
fastboot = true;
@@ -521,9 +525,19 @@ int main(int argc, char** argv) {
fastboot = false;
break;
+ case Device::REBOOT:
+ ui->Print("Rebooting...\n");
+ Reboot("userrequested,recovery");
+ break;
+
+ case Device::REBOOT_FROM_FASTBOOT:
+ ui->Print("Rebooting...\n");
+ Reboot("userrequested,fastboot");
+ break;
+
default:
ui->Print("Rebooting...\n");
- reboot("reboot,");
+ Reboot("unknown" + std::to_string(ret));
break;
}
}
diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h
index 7c76cdb0a..9a4edf261 100644
--- a/recovery_ui/include/recovery_ui/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -58,6 +58,8 @@ class Device {
REBOOT_FASTBOOT = 17,
REBOOT_RECOVERY = 18,
REBOOT_RESCUE = 19,
+ REBOOT_FROM_FASTBOOT = 20,
+ SHUTDOWN_FROM_FASTBOOT = 21,
};
explicit Device(RecoveryUI* ui);
diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp
index f1a79dcfd..cf0d3b563 100644
--- a/recovery_ui/ui.cpp
+++ b/recovery_ui/ui.cpp
@@ -375,7 +375,7 @@ void RecoveryUI::ProcessKey(int key_code, int updown) {
case RecoveryUI::REBOOT:
if (reboot_enabled) {
- reboot("reboot,");
+ Reboot("userrequested,recovery,ui");
while (true) {
pause();
}
diff --git a/tests/Android.bp b/tests/Android.bp
index 2e5334d9e..4969c087b 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -79,6 +79,8 @@ librecovery_static_libs = [
"libinstall",
"librecovery_ui",
"libminui",
+ "libfusesideload",
+ "libbootloader_message",
"libotautil",
"libhealthhalutils",
@@ -87,10 +89,8 @@ librecovery_static_libs = [
"android.hardware.health@2.0",
"android.hardware.health@1.0",
- "libbootloader_message",
"libext4_utils",
"libfs_mgr",
- "libfusesideload",
"libhidl-gen-utils",
"libhidlbase",
"libhidltransport",
@@ -107,6 +107,8 @@ cc_test {
defaults: [
"recovery_test_defaults",
+ "libupdater_defaults",
+ "libupdater_device_defaults",
],
test_suites: ["device-tests"],
@@ -115,16 +117,23 @@ cc_test {
"unit/*.cpp",
],
- static_libs: libapplypatch_static_libs + [
- "libinstall",
+ static_libs: libapplypatch_static_libs + librecovery_static_libs + [
"librecovery_ui",
+ "libfusesideload",
"libminui",
"libotautil",
- "libupdater",
+ "libupdater_device",
+ "libupdater_core",
+ "libupdate_verifier",
+
"libgtest_prod",
+ "libprotobuf-cpp-lite",
],
- data: ["testdata/*"],
+ data: [
+ "testdata/*",
+ ":res-testdata",
+ ],
}
cc_test {
@@ -142,33 +151,6 @@ cc_test {
],
}
-cc_test {
- name: "recovery_component_test",
- isolated: true,
-
- defaults: [
- "recovery_test_defaults",
- "libupdater_defaults",
- ],
-
- test_suites: ["device-tests"],
-
- srcs: [
- "component/*.cpp",
- ],
-
- static_libs: libapplypatch_static_libs + librecovery_static_libs + [
- "libupdater",
- "libupdate_verifier",
- "libprotobuf-cpp-lite",
- ],
-
- data: [
- "testdata/*",
- ":res-testdata",
- ],
-}
-
cc_test_host {
name: "recovery_host_test",
isolated: true,
@@ -178,7 +160,7 @@ cc_test_host {
],
srcs: [
- "component/imgdiff_test.cpp",
+ "unit/imgdiff_test.cpp",
],
static_libs: [
diff --git a/tests/component/resources_test.cpp b/tests/component/resources_test.cpp
deleted file mode 100644
index d7fdb8fa0..000000000
--- a/tests/component/resources_test.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2018 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 <dirent.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/strings.h>
-#include <gtest/gtest.h>
-#include <png.h>
-
-#include "minui/minui.h"
-#include "private/resources.h"
-
-static const std::string kLocale = "zu";
-
-static const std::vector<std::string> kResourceImagesDirs{
- "res-mdpi/images/", "res-hdpi/images/", "res-xhdpi/images/",
- "res-xxhdpi/images/", "res-xxxhdpi/images/",
-};
-
-static int png_filter(const dirent* de) {
- if (de->d_type != DT_REG || !android::base::EndsWith(de->d_name, "_text.png")) {
- return 0;
- }
- return 1;
-}
-
-// Finds out all the PNG files to test, which stay under the same dir with the executabl..
-static std::vector<std::string> add_files() {
- std::vector<std::string> files;
- for (const std::string& images_dir : kResourceImagesDirs) {
- static std::string exec_dir = android::base::GetExecutableDirectory();
- std::string dir_path = exec_dir + "/" + images_dir;
- dirent** namelist;
- int n = scandir(dir_path.c_str(), &namelist, png_filter, alphasort);
- if (n == -1) {
- printf("Failed to scandir %s: %s\n", dir_path.c_str(), strerror(errno));
- continue;
- }
- if (n == 0) {
- printf("No file is added for test in %s\n", dir_path.c_str());
- }
-
- while (n--) {
- std::string file_path = dir_path + namelist[n]->d_name;
- files.push_back(file_path);
- free(namelist[n]);
- }
- free(namelist);
- }
- return files;
-}
-
-class ResourcesTest : public testing::TestWithParam<std::string> {
- public:
- static std::vector<std::string> png_list;
-
- protected:
- void SetUp() override {
- png_ = std::make_unique<PngHandler>(GetParam());
- ASSERT_TRUE(png_);
-
- ASSERT_EQ(PNG_COLOR_TYPE_GRAY, png_->color_type()) << "Recovery expects grayscale PNG file.";
- ASSERT_LT(static_cast<png_uint_32>(5), png_->width());
- ASSERT_LT(static_cast<png_uint_32>(0), png_->height());
- ASSERT_EQ(1, png_->channels()) << "Recovery background text images expects 1-channel PNG file.";
- }
-
- std::unique_ptr<PngHandler> png_{ nullptr };
-};
-
-// Parses a png file and tests if it's qualified for the background text image under recovery.
-TEST_P(ResourcesTest, ValidateLocale) {
- std::vector<unsigned char> row(png_->width());
- for (png_uint_32 y = 0; y < png_->height(); ++y) {
- png_read_row(png_->png_ptr(), row.data(), nullptr);
- int w = (row[1] << 8) | row[0];
- int h = (row[3] << 8) | row[2];
- int len = row[4];
- EXPECT_LT(0, w);
- EXPECT_LT(0, h);
- EXPECT_LT(0, len) << "Locale string should be non-empty.";
- EXPECT_NE(0, row[5]) << "Locale string is missing.";
-
- ASSERT_GE(png_->height(), y + 1 + h) << "Locale: " << kLocale << " is not found in the file.";
- char* loc = reinterpret_cast<char*>(&row[5]);
- if (matches_locale(loc, kLocale.c_str())) {
- EXPECT_TRUE(android::base::StartsWith(loc, kLocale));
- break;
- }
- for (int i = 0; i < h; ++i, ++y) {
- png_read_row(png_->png_ptr(), row.data(), nullptr);
- }
- }
-}
-
-std::vector<std::string> ResourcesTest::png_list = add_files();
-
-INSTANTIATE_TEST_CASE_P(BackgroundTextValidation, ResourcesTest,
- ::testing::ValuesIn(ResourcesTest::png_list.cbegin(),
- ResourcesTest::png_list.cend()));
diff --git a/tests/component/applypatch_modes_test.cpp b/tests/unit/applypatch_modes_test.cpp
index 08414b796..08414b796 100644
--- a/tests/component/applypatch_modes_test.cpp
+++ b/tests/unit/applypatch_modes_test.cpp
diff --git a/tests/component/bootloader_message_test.cpp b/tests/unit/bootloader_message_test.cpp
index b005d199c..95d875e69 100644
--- a/tests/component/bootloader_message_test.cpp
+++ b/tests/unit/bootloader_message_test.cpp
@@ -15,6 +15,7 @@
*/
#include <string>
+#include <string_view>
#include <vector>
#include <android-base/file.h>
@@ -22,6 +23,10 @@
#include <bootloader_message/bootloader_message.h>
#include <gtest/gtest.h>
+using namespace std::string_literals;
+
+extern void SetMiscBlockDeviceForTest(std::string_view misc_device);
+
TEST(BootloaderMessageTest, read_and_write_bootloader_message) {
TemporaryFile temp_misc;
@@ -114,3 +119,36 @@ TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_long) {
std::string(boot.reserved, sizeof(boot.reserved)));
}
+TEST(BootloaderMessageTest, WriteMiscPartitionVendorSpace) {
+ TemporaryFile temp_misc;
+ ASSERT_TRUE(android::base::WriteStringToFile(std::string(4096, '\x00'), temp_misc.path));
+ SetMiscBlockDeviceForTest(temp_misc.path);
+
+ constexpr std::string_view kTestMessage = "kTestMessage";
+ std::string err;
+ ASSERT_TRUE(WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), 0, &err));
+
+ std::string message;
+ message.resize(kTestMessage.size());
+ ASSERT_TRUE(ReadMiscPartitionVendorSpace(message.data(), message.size(), 0, &err));
+ ASSERT_EQ(kTestMessage, message);
+
+ // Write with an offset.
+ ASSERT_TRUE(WriteMiscPartitionVendorSpace("\x00\x00", 2, 5, &err));
+ ASSERT_TRUE(ReadMiscPartitionVendorSpace(message.data(), message.size(), 0, &err));
+ ASSERT_EQ("kTest\x00\x00ssage"s, message);
+
+ // Write with the right size.
+ auto start_offset =
+ WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC - kTestMessage.size();
+ ASSERT_TRUE(
+ WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), start_offset, &err));
+
+ // Out-of-bound write.
+ ASSERT_FALSE(WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(),
+ start_offset + 1, &err));
+
+ // Message won't fit.
+ std::string long_message(WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC + 1, 'a');
+ ASSERT_FALSE(WriteMiscPartitionVendorSpace(long_message.data(), long_message.size(), 0, &err));
+}
diff --git a/tests/component/edify_test.cpp b/tests/unit/edify_test.cpp
index 8397bd38e..8397bd38e 100644
--- a/tests/component/edify_test.cpp
+++ b/tests/unit/edify_test.cpp
diff --git a/tests/unit/fuse_provider_test.cpp b/tests/unit/fuse_provider_test.cpp
new file mode 100644
index 000000000..c5995dd7d
--- /dev/null
+++ b/tests/unit/fuse_provider_test.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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 <unistd.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#include "fuse_provider.h"
+#include "fuse_sideload.h"
+#include "install/install.h"
+
+TEST(FuseBlockMapTest, CreateFromBlockMap_smoke) {
+ TemporaryFile fake_block_device;
+ std::vector<std::string> lines = {
+ fake_block_device.path, "10000 4096", "3", "10 11", "20 21", "22 23",
+ };
+
+ TemporaryFile temp_file;
+ android::base::WriteStringToFile(android::base::Join(lines, '\n'), temp_file.path);
+ auto block_map_data = FuseBlockDataProvider::CreateFromBlockMap(temp_file.path, 4096);
+
+ ASSERT_TRUE(block_map_data);
+ ASSERT_EQ(10000, block_map_data->file_size());
+ ASSERT_EQ(4096, block_map_data->fuse_block_size());
+ ASSERT_EQ(RangeSet({ { 10, 11 }, { 20, 21 }, { 22, 23 } }), block_map_data->ranges());
+}
+
+TEST(FuseBlockMapTest, ReadBlockAlignedData_smoke) {
+ std::string content;
+ content.reserve(40960);
+ for (char c = 0; c < 10; c++) {
+ content += std::string(4096, c);
+ }
+ TemporaryFile fake_block_device;
+ ASSERT_TRUE(android::base::WriteStringToFile(content, fake_block_device.path));
+
+ std::vector<std::string> lines = {
+ fake_block_device.path,
+ "20000 4096",
+ "1",
+ "0 5",
+ };
+ TemporaryFile temp_file;
+ android::base::WriteStringToFile(android::base::Join(lines, '\n'), temp_file.path);
+ auto block_map_data = FuseBlockDataProvider::CreateFromBlockMap(temp_file.path, 4096);
+
+ std::vector<uint8_t> result(2000);
+ ASSERT_TRUE(block_map_data->ReadBlockAlignedData(result.data(), 2000, 1));
+ ASSERT_EQ(std::vector<uint8_t>(content.begin() + 4096, content.begin() + 6096), result);
+
+ result.resize(20000);
+ ASSERT_TRUE(block_map_data->ReadBlockAlignedData(result.data(), 20000, 0));
+ ASSERT_EQ(std::vector<uint8_t>(content.begin(), content.begin() + 20000), result);
+}
+
+TEST(FuseBlockMapTest, ReadBlockAlignedData_large_fuse_block) {
+ std::string content;
+ for (char c = 0; c < 10; c++) {
+ content += std::string(4096, c);
+ }
+
+ TemporaryFile temp_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
+
+ std::vector<std::string> lines = {
+ temp_file.path, "36384 4096", "2", "0 5", "6 10",
+ };
+ TemporaryFile block_map;
+ ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(lines, '\n'), block_map.path));
+
+ auto block_map_data = FuseBlockDataProvider::CreateFromBlockMap(block_map.path, 16384);
+ ASSERT_TRUE(block_map_data);
+
+ std::vector<uint8_t> result(20000);
+ // Out of bound read
+ ASSERT_FALSE(block_map_data->ReadBlockAlignedData(result.data(), 20000, 2));
+ ASSERT_TRUE(block_map_data->ReadBlockAlignedData(result.data(), 20000, 1));
+ // expected source block contains: 4, 6-9
+ std::string expected = content.substr(16384, 4096) + content.substr(24576, 15904);
+ ASSERT_EQ(std::vector<uint8_t>(expected.begin(), expected.end()), result);
+}
diff --git a/tests/component/sideload_test.cpp b/tests/unit/fuse_sideload_test.cpp
index 6add99f41..6add99f41 100644
--- a/tests/component/sideload_test.cpp
+++ b/tests/unit/fuse_sideload_test.cpp
diff --git a/tests/component/imgdiff_test.cpp b/tests/unit/imgdiff_test.cpp
index e76ccbdfb..e76ccbdfb 100644
--- a/tests/component/imgdiff_test.cpp
+++ b/tests/unit/imgdiff_test.cpp
diff --git a/tests/component/install_test.cpp b/tests/unit/install_test.cpp
index 385132939..4ec409908 100644
--- a/tests/component/install_test.cpp
+++ b/tests/unit/install_test.cpp
@@ -33,6 +33,7 @@
#include <ziparchive/zip_writer.h>
#include "install/install.h"
+#include "install/wipe_device.h"
#include "otautil/paths.h"
#include "private/setup_commands.h"
@@ -204,7 +205,7 @@ TEST(InstallTest, SetUpNonAbUpdateCommands) {
std::string binary_path = std::string(td.path) + "/update_binary";
Paths::Get().set_temporary_update_binary(binary_path);
std::vector<std::string> cmd;
- ASSERT_EQ(0, SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd));
+ ASSERT_TRUE(SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd));
ASSERT_EQ(4U, cmd.size());
ASSERT_EQ(binary_path, cmd[0]);
ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION
@@ -216,7 +217,7 @@ TEST(InstallTest, SetUpNonAbUpdateCommands) {
// With non-zero retry count. update_binary will be removed automatically.
cmd.clear();
- ASSERT_EQ(0, SetUpNonAbUpdateCommands(package, zip, 2, status_fd, &cmd));
+ ASSERT_TRUE(SetUpNonAbUpdateCommands(package, zip, 2, status_fd, &cmd));
ASSERT_EQ(5U, cmd.size());
ASSERT_EQ(binary_path, cmd[0]);
ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION
@@ -243,7 +244,7 @@ TEST(InstallTest, SetUpNonAbUpdateCommands_MissingUpdateBinary) {
TemporaryDir td;
Paths::Get().set_temporary_update_binary(std::string(td.path) + "/update_binary");
std::vector<std::string> cmd;
- ASSERT_EQ(INSTALL_CORRUPT, SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd));
+ ASSERT_FALSE(SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd));
CloseArchive(zip);
}
@@ -270,19 +271,18 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t
ZipArchiveHandle zip;
ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
- ZipString payload_name("payload.bin");
ZipEntry payload_entry;
- ASSERT_EQ(0, FindEntry(zip, payload_name, &payload_entry));
+ ASSERT_EQ(0, FindEntry(zip, "payload.bin", &payload_entry));
std::map<std::string, std::string> metadata;
ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata));
if (success) {
- ASSERT_EQ(0, CheckPackageMetadata(metadata, OtaType::AB));
+ ASSERT_TRUE(CheckPackageMetadata(metadata, OtaType::AB));
int status_fd = 10;
std::string package = "/path/to/update.zip";
std::vector<std::string> cmd;
- ASSERT_EQ(0, SetUpAbUpdateCommands(package, zip, status_fd, &cmd));
+ ASSERT_TRUE(SetUpAbUpdateCommands(package, zip, status_fd, &cmd));
ASSERT_EQ(5U, cmd.size());
ASSERT_EQ("/system/bin/update_engine_sideload", cmd[0]);
ASSERT_EQ("--payload=file://" + package, cmd[1]);
@@ -290,7 +290,7 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t
ASSERT_EQ("--headers=" + properties, cmd[3]);
ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]);
} else {
- ASSERT_EQ(INSTALL_ERROR, CheckPackageMetadata(metadata, OtaType::AB));
+ ASSERT_FALSE(CheckPackageMetadata(metadata, OtaType::AB));
}
CloseArchive(zip);
}
@@ -325,7 +325,7 @@ TEST(InstallTest, SetUpAbUpdateCommands_MissingPayloadPropertiesTxt) {
int status_fd = 10;
std::string package = "/path/to/update.zip";
std::vector<std::string> cmd;
- ASSERT_EQ(INSTALL_CORRUPT, SetUpAbUpdateCommands(package, zip, status_fd, &cmd));
+ ASSERT_FALSE(SetUpAbUpdateCommands(package, zip, status_fd, &cmd));
CloseArchive(zip);
}
@@ -358,8 +358,8 @@ TEST(InstallTest, SetUpAbUpdateCommands_MultipleSerialnos) {
VerifyAbUpdateCommands(long_serialno);
}
-static void test_check_package_metadata(const std::string& metadata_string, OtaType ota_type,
- int exptected_result) {
+static void TestCheckPackageMetadata(const std::string& metadata_string, OtaType ota_type,
+ bool exptected_result) {
TemporaryFile temp_file;
BuildZipArchive(
{
@@ -387,7 +387,7 @@ TEST(InstallTest, CheckPackageMetadata_ota_type) {
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
},
"\n");
- test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
+ TestCheckPackageMetadata(metadata, OtaType::AB, false);
// Checks if ota-type matches
metadata = android::base::Join(
@@ -397,9 +397,9 @@ TEST(InstallTest, CheckPackageMetadata_ota_type) {
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
},
"\n");
- test_check_package_metadata(metadata, OtaType::AB, 0);
+ TestCheckPackageMetadata(metadata, OtaType::AB, true);
- test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR);
+ TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
}
TEST(InstallTest, CheckPackageMetadata_device_type) {
@@ -409,7 +409,7 @@ TEST(InstallTest, CheckPackageMetadata_device_type) {
"ota-type=BRICK",
},
"\n");
- test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR);
+ TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
// device type mismatches
metadata = android::base::Join(
@@ -418,7 +418,7 @@ TEST(InstallTest, CheckPackageMetadata_device_type) {
"pre-device=dummy_device_type",
},
"\n");
- test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR);
+ TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
}
TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) {
@@ -432,7 +432,7 @@ TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) {
"pre-device=" + device,
},
"\n");
- test_check_package_metadata(metadata, OtaType::BRICK, 0);
+ TestCheckPackageMetadata(metadata, OtaType::BRICK, true);
// Serial number mismatches
metadata = android::base::Join(
@@ -442,7 +442,7 @@ TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) {
"serialno=dummy_serial",
},
"\n");
- test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR);
+ TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
std::string serialno = android::base::GetProperty("ro.serialno", "");
ASSERT_NE("", serialno);
@@ -453,7 +453,7 @@ TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) {
"serialno=" + serialno,
},
"\n");
- test_check_package_metadata(metadata, OtaType::BRICK, 0);
+ TestCheckPackageMetadata(metadata, OtaType::BRICK, true);
}
TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) {
@@ -477,7 +477,7 @@ TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) {
"serialno=" + android::base::Join(serial_numbers, '|'),
},
"\n");
- test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR);
+ TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
serial_numbers.emplace_back(serialno);
std::shuffle(serial_numbers.begin(), serial_numbers.end(), std::default_random_engine());
@@ -488,7 +488,7 @@ TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) {
"serialno=" + android::base::Join(serial_numbers, '|'),
},
"\n");
- test_check_package_metadata(metadata, OtaType::BRICK, 0);
+ TestCheckPackageMetadata(metadata, OtaType::BRICK, true);
}
TEST(InstallTest, CheckPackageMetadata_ab_build_version) {
@@ -506,7 +506,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_build_version) {
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
},
"\n");
- test_check_package_metadata(metadata, OtaType::AB, 0);
+ TestCheckPackageMetadata(metadata, OtaType::AB, true);
metadata = android::base::Join(
std::vector<std::string>{
@@ -516,7 +516,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_build_version) {
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
},
"\n");
- test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
+ TestCheckPackageMetadata(metadata, OtaType::AB, false);
}
TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) {
@@ -534,7 +534,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) {
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
},
"\n");
- test_check_package_metadata(metadata, OtaType::AB, 0);
+ TestCheckPackageMetadata(metadata, OtaType::AB, true);
metadata = android::base::Join(
std::vector<std::string>{
@@ -544,7 +544,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) {
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
},
"\n");
- test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
+ TestCheckPackageMetadata(metadata, OtaType::AB, false);
}
TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) {
@@ -558,7 +558,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) {
"pre-device=" + device,
},
"\n");
- test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
+ TestCheckPackageMetadata(metadata, OtaType::AB, false);
// post timestamp should be larger than the timestamp on device.
metadata = android::base::Join(
@@ -568,7 +568,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) {
"post-timestamp=0",
},
"\n");
- test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
+ TestCheckPackageMetadata(metadata, OtaType::AB, false);
// fingerprint is required for downgrade
metadata = android::base::Join(
@@ -579,7 +579,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) {
"ota-downgrade=yes",
},
"\n");
- test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
+ TestCheckPackageMetadata(metadata, OtaType::AB, false);
std::string finger_print = android::base::GetProperty("ro.build.fingerprint", "");
ASSERT_NE("", finger_print);
@@ -593,5 +593,5 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) {
"ota-downgrade=yes",
},
"\n");
- test_check_package_metadata(metadata, OtaType::AB, 0);
+ TestCheckPackageMetadata(metadata, OtaType::AB, true);
}
diff --git a/tests/unit/package_test.cpp b/tests/unit/package_test.cpp
index a735a699e..5e31f7fa5 100644
--- a/tests/unit/package_test.cpp
+++ b/tests/unit/package_test.cpp
@@ -105,10 +105,9 @@ TEST_F(PackageTest, GetZipArchiveHandle_extract_entry) {
ASSERT_TRUE(zip);
// Check that we can extract one zip entry.
- std::string entry_name = "dir1/file3.txt";
- ZipString path(entry_name.c_str());
+ std::string_view entry_name = "dir1/file3.txt";
ZipEntry entry;
- ASSERT_EQ(0, FindEntry(zip, path, &entry));
+ ASSERT_EQ(0, FindEntry(zip, entry_name, &entry));
std::vector<uint8_t> extracted(entry_name.size());
ASSERT_EQ(0, ExtractToMemory(zip, &entry, extracted.data(), extracted.size()));
diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp
index fc72f2f6d..699f933a0 100644
--- a/tests/unit/rangeset_test.cpp
+++ b/tests/unit/rangeset_test.cpp
@@ -18,6 +18,7 @@
#include <sys/types.h>
#include <limits>
+#include <optional>
#include <vector>
#include <gtest/gtest.h>
@@ -248,6 +249,29 @@ TEST(RangeSetTest, ToString) {
ASSERT_EQ("6,1,3,4,6,15,22", RangeSet::Parse("6,1,3,4,6,15,22").ToString());
}
+TEST(RangeSetTest, GetSubRanges_invalid) {
+ RangeSet range0({ { 1, 11 }, { 20, 30 } });
+ ASSERT_FALSE(range0.GetSubRanges(0, 21)); // too many blocks
+ ASSERT_FALSE(range0.GetSubRanges(21, 1)); // start block OOB
+}
+
+TEST(RangeSetTest, GetSubRanges_empty) {
+ RangeSet range0({ { 1, 11 }, { 20, 30 } });
+ ASSERT_EQ(RangeSet{}, range0.GetSubRanges(1, 0)); // empty num_of_blocks
+}
+
+TEST(RangeSetTest, GetSubRanges_smoke) {
+ RangeSet range0({ { 10, 11 } });
+ ASSERT_EQ(RangeSet({ { 10, 11 } }), range0.GetSubRanges(0, 1));
+
+ RangeSet range1({ { 10, 11 }, { 20, 21 }, { 30, 31 } });
+ ASSERT_EQ(range1, range1.GetSubRanges(0, 3));
+ ASSERT_EQ(RangeSet({ { 20, 21 } }), range1.GetSubRanges(1, 1));
+
+ RangeSet range2({ { 1, 11 }, { 20, 25 }, { 30, 35 } });
+ ASSERT_EQ(RangeSet({ { 10, 11 }, { 20, 25 }, { 30, 31 } }), range2.GetSubRanges(9, 7));
+}
+
TEST(SortedRangeSetTest, Insert) {
SortedRangeSet rs({ { 2, 3 }, { 4, 6 }, { 8, 14 } });
rs.Insert({ 1, 2 });
diff --git a/tests/unit/resources_test.cpp b/tests/unit/resources_test.cpp
index c3f72718f..302744308 100644
--- a/tests/unit/resources_test.cpp
+++ b/tests/unit/resources_test.cpp
@@ -14,12 +14,62 @@
* limitations under the License.
*/
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <memory>
#include <string>
+#include <vector>
+#include <android-base/file.h>
+#include <android-base/strings.h>
#include <gtest/gtest.h>
+#include <png.h>
#include "common/test_constants.h"
#include "minui/minui.h"
+#include "private/resources.h"
+
+static const std::string kLocale = "zu";
+
+static const std::vector<std::string> kResourceImagesDirs{
+ "res-mdpi/images/", "res-hdpi/images/", "res-xhdpi/images/",
+ "res-xxhdpi/images/", "res-xxxhdpi/images/",
+};
+
+static int png_filter(const dirent* de) {
+ if (de->d_type != DT_REG || !android::base::EndsWith(de->d_name, "_text.png")) {
+ return 0;
+ }
+ return 1;
+}
+
+// Finds out all the PNG files to test, which stay under the same dir with the executabl..
+static std::vector<std::string> add_files() {
+ std::vector<std::string> files;
+ for (const std::string& images_dir : kResourceImagesDirs) {
+ static std::string exec_dir = android::base::GetExecutableDirectory();
+ std::string dir_path = exec_dir + "/" + images_dir;
+ dirent** namelist;
+ int n = scandir(dir_path.c_str(), &namelist, png_filter, alphasort);
+ if (n == -1) {
+ printf("Failed to scandir %s: %s\n", dir_path.c_str(), strerror(errno));
+ continue;
+ }
+ if (n == 0) {
+ printf("No file is added for test in %s\n", dir_path.c_str());
+ }
+
+ while (n--) {
+ std::string file_path = dir_path + namelist[n]->d_name;
+ files.push_back(file_path);
+ free(namelist[n]);
+ }
+ free(namelist);
+ }
+ return files;
+}
TEST(ResourcesTest, res_create_multi_display_surface) {
GRSurface** frames;
@@ -35,3 +85,52 @@ TEST(ResourcesTest, res_create_multi_display_surface) {
}
free(frames);
}
+
+class ResourcesTest : public testing::TestWithParam<std::string> {
+ public:
+ static std::vector<std::string> png_list;
+
+ protected:
+ void SetUp() override {
+ png_ = std::make_unique<PngHandler>(GetParam());
+ ASSERT_TRUE(png_);
+
+ ASSERT_EQ(PNG_COLOR_TYPE_GRAY, png_->color_type()) << "Recovery expects grayscale PNG file.";
+ ASSERT_LT(static_cast<png_uint_32>(5), png_->width());
+ ASSERT_LT(static_cast<png_uint_32>(0), png_->height());
+ ASSERT_EQ(1, png_->channels()) << "Recovery background text images expects 1-channel PNG file.";
+ }
+
+ std::unique_ptr<PngHandler> png_{ nullptr };
+};
+
+// Parses a png file and tests if it's qualified for the background text image under recovery.
+TEST_P(ResourcesTest, ValidateLocale) {
+ std::vector<unsigned char> row(png_->width());
+ for (png_uint_32 y = 0; y < png_->height(); ++y) {
+ png_read_row(png_->png_ptr(), row.data(), nullptr);
+ int w = (row[1] << 8) | row[0];
+ int h = (row[3] << 8) | row[2];
+ int len = row[4];
+ EXPECT_LT(0, w);
+ EXPECT_LT(0, h);
+ EXPECT_LT(0, len) << "Locale string should be non-empty.";
+ EXPECT_NE(0, row[5]) << "Locale string is missing.";
+
+ ASSERT_GE(png_->height(), y + 1 + h) << "Locale: " << kLocale << " is not found in the file.";
+ char* loc = reinterpret_cast<char*>(&row[5]);
+ if (matches_locale(loc, kLocale.c_str())) {
+ EXPECT_TRUE(android::base::StartsWith(loc, kLocale));
+ break;
+ }
+ for (int i = 0; i < h; ++i, ++y) {
+ png_read_row(png_->png_ptr(), row.data(), nullptr);
+ }
+ }
+}
+
+std::vector<std::string> ResourcesTest::png_list = add_files();
+
+INSTANTIATE_TEST_CASE_P(BackgroundTextValidation, ResourcesTest,
+ ::testing::ValuesIn(ResourcesTest::png_list.cbegin(),
+ ResourcesTest::png_list.cend()));
diff --git a/tests/unit/sysutil_test.cpp b/tests/unit/sysutil_test.cpp
index 3466e8eec..64b8956f7 100644
--- a/tests/unit/sysutil_test.cpp
+++ b/tests/unit/sysutil_test.cpp
@@ -67,7 +67,7 @@ TEST(SysUtilTest, ParseBlockMapFile_invalid_size) {
"/dev/abc",
"42949672950 4294967295",
"1",
- "0 9",
+ "0 10",
};
TemporaryFile temp_file;
diff --git a/tests/component/uncrypt_test.cpp b/tests/unit/uncrypt_test.cpp
index e97d589a6..e97d589a6 100644
--- a/tests/component/uncrypt_test.cpp
+++ b/tests/unit/uncrypt_test.cpp
diff --git a/tests/component/update_verifier_test.cpp b/tests/unit/update_verifier_test.cpp
index e27e58c22..e27e58c22 100644
--- a/tests/component/update_verifier_test.cpp
+++ b/tests/unit/update_verifier_test.cpp
diff --git a/tests/component/updater_test.cpp b/tests/unit/updater_test.cpp
index a0a7b66ab..8993dd8b7 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/unit/updater_test.cpp
@@ -52,21 +52,20 @@
#include "updater/blockimg.h"
#include "updater/install.h"
#include "updater/updater.h"
+#include "updater/updater_runtime.h"
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) {
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);
@@ -85,6 +84,11 @@ static void expect(const char* expected, const std::string& expr_str, CauseCode
ASSERT_EQ(cause_code, state.cause_code);
}
+static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code) {
+ Updater updater(std::make_unique<UpdaterRuntime>(nullptr));
+ expect(expected, expr_str, cause_code, &updater);
+}
+
static void BuildUpdatePackage(const PackageEntries& entries, int fd) {
FILE* zip_file_ptr = fdopen(fd, "wb");
ZipWriter zip_writer(zip_file_ptr);
@@ -102,38 +106,6 @@ static void BuildUpdatePackage(const PackageEntries& entries, int fd) {
ASSERT_EQ(0, fclose(zip_file_ptr));
}
-static void RunBlockImageUpdate(bool is_verify, const PackageEntries& entries,
- const std::string& image_file, const std::string& result,
- CauseCode cause_code = kNoCause) {
- CHECK(entries.find("transfer_list") != entries.end());
-
- // Build the update package.
- TemporaryFile zip_file;
- BuildUpdatePackage(entries, zip_file.release());
-
- MemMapping map;
- ASSERT_TRUE(map.MapFile(zip_file.path));
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle));
-
- // Set up the handler, command_pipe, patch offset & length.
- UpdaterInfo updater_info;
- updater_info.package_zip = handle;
- TemporaryFile temp_pipe;
- updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe");
- updater_info.package_zip_addr = map.addr;
- updater_info.package_zip_len = map.length;
-
- std::string new_data = entries.find("new_data.br") != entries.end() ? "new_data.br" : "new_data";
- std::string script = is_verify ? "block_image_verify" : "block_image_update";
- script += R"((")" + image_file + R"(", package_extract_file("transfer_list"), ")" + new_data +
- R"(", "patch_data"))";
- expect(result.c_str(), script, cause_code, &updater_info);
-
- ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
- CloseArchive(handle);
-}
-
static std::string GetSha1(std::string_view content) {
uint8_t digest[SHA_DIGEST_LENGTH];
SHA1(reinterpret_cast<const uint8_t*>(content.data()), content.size(), digest);
@@ -159,29 +131,26 @@ static Value* BlobToString(const char* name, State* state,
return args[0].release();
}
-class UpdaterTest : public ::testing::Test {
+class UpdaterTestBase {
protected:
- void SetUp() override {
+ UpdaterTestBase() : updater_(std::make_unique<UpdaterRuntime>(nullptr)) {}
+
+ void SetUp() {
RegisterBuiltins();
RegisterInstallFunctions();
RegisterBlockImageFunctions();
- RegisterFunction("blob_to_string", BlobToString);
-
// Each test is run in a separate process (isolated mode). Shared temporary files won't cause
// conflicts.
Paths::Get().set_cache_temp_source(temp_saved_source_.path);
Paths::Get().set_last_command_file(temp_last_command_.path);
Paths::Get().set_stash_directory_base(temp_stash_base_.path);
- // Enable a special command "abort" to simulate interruption.
- Command::abort_allowed_ = true;
-
last_command_file_ = temp_last_command_.path;
image_file_ = image_temp_file_.path;
}
- void TearDown() override {
+ void TearDown() {
// Clean up the last_command_file if any.
ASSERT_TRUE(android::base::RemoveFileIfExists(last_command_file_));
@@ -191,16 +160,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));
+ ASSERT_TRUE(updater_.RunUpdate());
+ ASSERT_EQ(result, updater_.GetResult());
+
+ // Parse the cause code written to the command pipe.
+ int received_cause_code = kNoCause;
+ std::string pipe_content;
+ ASSERT_TRUE(android::base::ReadFileToString(temp_pipe.path, &pipe_content));
+ auto lines = android::base::Split(pipe_content, "\n");
+ for (std::string_view line : lines) {
+ if (android::base::ConsumePrefix(&line, "log cause: ")) {
+ ASSERT_TRUE(android::base::ParseInt(line.data(), &received_cause_code));
+ }
+ }
+ ASSERT_EQ(cause_code, received_cause_code);
+ }
+
TemporaryFile temp_saved_source_;
TemporaryDir temp_stash_base_;
std::string last_command_file_;
std::string image_file_;
+ Updater updater_;
+
private:
TemporaryFile temp_last_command_;
TemporaryFile image_temp_file_;
};
+class UpdaterTest : public UpdaterTestBase, public ::testing::Test {
+ protected:
+ void SetUp() override {
+ UpdaterTestBase::SetUp();
+
+ RegisterFunction("blob_to_string", BlobToString);
+ // Enable a special command "abort" to simulate interruption.
+ Command::abort_allowed_ = true;
+ }
+
+ void TearDown() override {
+ UpdaterTestBase::TearDown();
+ }
+
+ void SetUpdaterCmdPipe(int fd) {
+ FILE* cmd_pipe = fdopen(fd, "w");
+ ASSERT_NE(nullptr, cmd_pipe);
+ updater_.cmd_pipe_.reset(cmd_pipe);
+ }
+
+ void SetUpdaterOtaPackageHandle(ZipArchiveHandle handle) {
+ updater_.package_handle_ = handle;
+ }
+
+ void FlushUpdaterCommandPipe() const {
+ fflush(updater_.cmd_pipe_.get());
+ }
+};
+
TEST_F(UpdaterTest, getprop) {
expect(android::base::GetProperty("ro.product.device", "").c_str(),
"getprop(\"ro.product.device\")",
@@ -317,13 +350,12 @@ TEST_F(UpdaterTest, package_extract_file) {
ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
// Need to set up the ziphandle.
- UpdaterInfo updater_info;
- updater_info.package_zip = handle;
+ SetUpdaterOtaPackageHandle(handle);
// Two-argument version.
TemporaryFile temp_file1;
std::string script("package_extract_file(\"a.txt\", \"" + std::string(temp_file1.path) + "\")");
- expect("t", script, kNoCause, &updater_info);
+ expect("t", script, kNoCause, &updater_);
// Verify the extracted entry.
std::string data;
@@ -332,32 +364,30 @@ TEST_F(UpdaterTest, package_extract_file) {
// Now extract another entry to the same location, which should overwrite.
script = "package_extract_file(\"b.txt\", \"" + std::string(temp_file1.path) + "\")";
- expect("t", script, kNoCause, &updater_info);
+ expect("t", script, kNoCause, &updater_);
ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data));
ASSERT_EQ(kBTxtContents, data);
// Missing zip entry. The two-argument version doesn't abort.
script = "package_extract_file(\"doesntexist\", \"" + std::string(temp_file1.path) + "\")";
- expect("", script, kNoCause, &updater_info);
+ expect("", script, kNoCause, &updater_);
// Extract to /dev/full should fail.
script = "package_extract_file(\"a.txt\", \"/dev/full\")";
- expect("", script, kNoCause, &updater_info);
+ expect("", script, kNoCause, &updater_);
// One-argument version. package_extract_file() gives a VAL_BLOB, which needs to be converted to
// VAL_STRING for equality test.
script = "blob_to_string(package_extract_file(\"a.txt\")) == \"" + kATxtContents + "\"";
- expect("t", script, kNoCause, &updater_info);
+ expect("t", script, kNoCause, &updater_);
script = "blob_to_string(package_extract_file(\"b.txt\")) == \"" + kBTxtContents + "\"";
- expect("t", script, kNoCause, &updater_info);
+ expect("t", script, kNoCause, &updater_);
// Missing entry. The one-argument version aborts the evaluation.
script = "package_extract_file(\"doesntexist\")";
- expect(nullptr, script, kPackageExtractFileFailure, &updater_info);
-
- CloseArchive(handle);
+ expect(nullptr, script, kPackageExtractFileFailure, &updater_);
}
TEST_F(UpdaterTest, read_file) {
@@ -563,17 +593,15 @@ TEST_F(UpdaterTest, set_progress) {
expect(nullptr, "set_progress(\".3.5\")", kArgsParsingFailure);
TemporaryFile tf;
- UpdaterInfo updater_info;
- updater_info.cmd_pipe = fdopen(tf.release(), "w");
- expect(".52", "set_progress(\".52\")", kNoCause, &updater_info);
- fflush(updater_info.cmd_pipe);
+ SetUpdaterCmdPipe(tf.release());
+ expect(".52", "set_progress(\".52\")", kNoCause, &updater_);
+ FlushUpdaterCommandPipe();
std::string cmd;
ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd));
ASSERT_EQ(android::base::StringPrintf("set_progress %f\n", .52), cmd);
// recovery-updater protocol expects 2 tokens ("set_progress <frac>").
ASSERT_EQ(2U, android::base::Split(cmd, " ").size());
- ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
}
TEST_F(UpdaterTest, show_progress) {
@@ -588,17 +616,15 @@ TEST_F(UpdaterTest, show_progress) {
expect(nullptr, "show_progress(\".3\", \"5a\")", kArgsParsingFailure);
TemporaryFile tf;
- UpdaterInfo updater_info;
- updater_info.cmd_pipe = fdopen(tf.release(), "w");
- expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_info);
- fflush(updater_info.cmd_pipe);
+ SetUpdaterCmdPipe(tf.release());
+ expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_);
+ FlushUpdaterCommandPipe();
std::string cmd;
ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd));
ASSERT_EQ(android::base::StringPrintf("progress %f %d\n", .52, 10), cmd);
// recovery-updater protocol expects 3 tokens ("progress <frac> <secs>").
ASSERT_EQ(3U, android::base::Split(cmd, " ").size());
- ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
}
TEST_F(UpdaterTest, block_image_update_parsing_error) {
@@ -993,44 +1019,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;
diff --git a/tests/component/verifier_test.cpp b/tests/unit/verifier_test.cpp
index ded23c52f..ded23c52f 100644
--- a/tests/component/verifier_test.cpp
+++ b/tests/unit/verifier_test.cpp
diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp
index dfe617ebe..0753d64e1 100644
--- a/tests/unit/zip_test.cpp
+++ b/tests/unit/zip_test.cpp
@@ -37,10 +37,9 @@ TEST(ZipTest, OpenFromMemory) {
ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_path.c_str(), &handle));
static constexpr const char* BINARY_PATH = "META-INF/com/google/android/update-binary";
- ZipString binary_path(BINARY_PATH);
ZipEntry binary_entry;
// Make sure the package opens correctly and its entry can be read.
- ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
+ ASSERT_EQ(0, FindEntry(handle, BINARY_PATH, &binary_entry));
TemporaryFile tmp_binary;
ASSERT_NE(-1, tmp_binary.fd);
diff --git a/tools/image_generator/Android.bp b/tools/image_generator/Android.bp
index 2afdd5a84..83000407c 100644
--- a/tools/image_generator/Android.bp
+++ b/tools/image_generator/Android.bp
@@ -19,7 +19,7 @@ java_library_host {
static_libs: [
"commons-cli-1.2",
- "icu4j-host",
+ "icu4j",
],
srcs: [
diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml
index e6f2ffd84..e51b36dfb 100644
--- a/tools/recovery_l10n/res/values-gl/strings.xml
+++ b/tools/recovery_l10n/res/values-gl/strings.xml
@@ -6,9 +6,9 @@
<string name="recovery_no_command" msgid="4465476568623024327">"Non hai ningún comando"</string>
<string name="recovery_error" msgid="5748178989622716736">"Erro"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"Instalando actualización de seguranza"</string>
- <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos do usuario almacenados neste dispositivo."</string>
+ <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos de usuario almacenados neste dispositivo."</string>
<string name="recovery_try_again" msgid="7168248750158873496">"Tentar de novo"</string>
<string name="recovery_factory_data_reset" msgid="7321351565602894783">"Restablecemento dos datos de fábrica"</string>
- <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Queres borrar todos os datos do usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER."</string>
+ <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Queres borrar todos os datos de usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER."</string>
<string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Cancelar"</string>
</resources>
diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml
index 2d6c0abc4..3d6637278 100644
--- a/tools/recovery_l10n/res/values-ja/strings.xml
+++ b/tools/recovery_l10n/res/values-ja/strings.xml
@@ -6,7 +6,7 @@
<string name="recovery_no_command" msgid="4465476568623024327">"コマンドが指定されていません"</string>
<string name="recovery_error" msgid="5748178989622716736">"エラーが発生しました。"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"セキュリティ アップデートをインストールしています"</string>
- <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、このデバイスに保存されているすべてのユーザー データを消去することが必要な場合があります。"</string>
+ <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、この端末に保存されているすべてのユーザー データを消去することが必要な場合があります。"</string>
<string name="recovery_try_again" msgid="7168248750158873496">"再試行"</string>
<string name="recovery_factory_data_reset" msgid="7321351565602894783">"データの初期化"</string>
<string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"すべてのユーザー データをワイプしますか?\n\nこの操作は元に戻せません。"</string>
diff --git a/updater/Android.bp b/updater/Android.bp
index b80cdb3a0..b279068a8 100644
--- a/updater/Android.bp
+++ b/updater/Android.bp
@@ -30,7 +30,6 @@ cc_defaults {
"libfec",
"libfec_rs",
"libverity_tree",
- "libfs_mgr",
"libgtest_prod",
"liblog",
"liblp",
@@ -46,6 +45,14 @@ cc_defaults {
"libcrypto_utils",
"libcutils",
"libutils",
+ ],
+}
+
+cc_defaults {
+ name: "libupdater_device_defaults",
+
+ static_libs: [
+ "libfs_mgr",
"libtune2fs",
"libext2_com_err",
@@ -54,11 +61,13 @@ cc_defaults {
"libext2_uuid",
"libext2_e2p",
"libext2fs",
- ],
+ ]
}
cc_library_static {
- name: "libupdater",
+ name: "libupdater_core",
+
+ host_supported: true,
defaults: [
"recovery_defaults",
@@ -68,8 +77,37 @@ cc_library_static {
srcs: [
"blockimg.cpp",
"commands.cpp",
- "dynamic_partitions.cpp",
"install.cpp",
+ "updater.cpp",
+ ],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+
+ export_include_dirs: [
+ "include",
+ ],
+}
+
+cc_library_static {
+ name: "libupdater_device",
+
+ defaults: [
+ "recovery_defaults",
+ "libupdater_defaults",
+ "libupdater_device_defaults",
+ ],
+
+ srcs: [
+ "dynamic_partitions.cpp",
+ "updater_runtime.cpp",
+ ],
+
+ static_libs: [
+ "libupdater_core",
],
include_dirs: [
@@ -80,3 +118,32 @@ cc_library_static {
"include",
],
}
+
+cc_library_host_static {
+ name: "libupdater_host",
+
+ defaults: [
+ "recovery_defaults",
+ "libupdater_defaults",
+ ],
+
+ srcs: [
+ "simulator_runtime.cpp",
+ "target_files.cpp",
+ ],
+
+ static_libs: [
+ "libupdater_core",
+ "libfstab",
+ ],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+
+ export_include_dirs: [
+ "include",
+ ],
+}
diff --git a/updater/Android.mk b/updater/Android.mk
index c7a6ba989..e969d1c80 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -33,7 +33,6 @@ updater_common_static_libraries := \
libfec \
libfec_rs \
libverity_tree \
- libfs_mgr \
libgtest_prod \
liblog \
liblp \
@@ -48,9 +47,24 @@ updater_common_static_libraries := \
libcrypto \
libcrypto_utils \
libcutils \
- libutils \
- libtune2fs \
- $(tune2fs_static_libraries)
+ libutils
+
+
+# Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function
+# named "Register_<libname>()". Here we emit a little C function that
+# gets #included by updater.cpp. It calls all those registration
+# functions.
+# $(1): the path to the register.inc file
+# $(2): a list of TARGET_RECOVERY_UPDATER_LIBS
+define generate-register-inc
+ $(hide) mkdir -p $(dir $(1))
+ $(hide) echo "" > $(1)
+ $(hide) $(foreach lib,$(2),echo "extern void Register_$(lib)(void);" >> $(1);)
+ $(hide) echo "void RegisterDeviceExtensions() {" >> $(1)
+ $(hide) $(foreach lib,$(2),echo " Register_$(lib)();" >> $(1);)
+ $(hide) echo "}" >> $(1)
+endef
+
# updater (static executable)
# ===============================
@@ -59,7 +73,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := updater
LOCAL_SRC_FILES := \
- updater.cpp
+ updater_main.cpp
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include
@@ -69,33 +83,26 @@ LOCAL_CFLAGS := \
-Werror
LOCAL_STATIC_LIBRARIES := \
- libupdater \
+ libupdater_device \
+ libupdater_core \
$(TARGET_RECOVERY_UPDATER_LIBS) \
$(TARGET_RECOVERY_UPDATER_EXTRA_LIBS) \
- $(updater_common_static_libraries)
+ $(updater_common_static_libraries) \
+ libfs_mgr \
+ libtune2fs \
+ $(tune2fs_static_libraries)
-# Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function
-# named "Register_<libname>()". Here we emit a little C function that
-# gets #included by updater.c. It calls all those registration
-# functions.
+LOCAL_MODULE_CLASS := EXECUTABLES
+inc := $(call local-generated-sources-dir)/register.inc
# Devices can also add libraries to TARGET_RECOVERY_UPDATER_EXTRA_LIBS.
# These libs are also linked in with updater, but we don't try to call
# any sort of registration function for these. Use this variable for
# any subsidiary static libraries required for your registered
# extension libs.
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-inc := $(call local-generated-sources-dir)/register.inc
-
$(inc) : libs := $(TARGET_RECOVERY_UPDATER_LIBS)
$(inc) :
- $(hide) mkdir -p $(dir $@)
- $(hide) echo "" > $@
- $(hide) $(foreach lib,$(libs),echo "extern void Register_$(lib)(void);" >> $@;)
- $(hide) echo "void RegisterDeviceExtensions() {" >> $@
- $(hide) $(foreach lib,$(libs),echo " Register_$(lib)();" >> $@;)
- $(hide) echo "}" >> $@
+ $(call generate-register-inc,$@,$(libs))
LOCAL_GENERATED_SOURCES := $(inc)
@@ -104,3 +111,42 @@ inc :=
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
+
+
+# update_host_simulator (static executable)
+# ===============================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := update_host_simulator
+LOCAL_MODULE_HOST_OS := linux
+
+LOCAL_SRC_FILES := \
+ update_simulator_main.cpp
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+
+LOCAL_CFLAGS := \
+ -Wall \
+ -Werror
+
+LOCAL_STATIC_LIBRARIES := \
+ libupdater_host \
+ libupdater_core \
+ $(TARGET_RECOVERY_UPDATER_HOST_LIBS) \
+ $(TARGET_RECOVERY_UPDATER_HOST_EXTRA_LIBS) \
+ $(updater_common_static_libraries) \
+ libfstab
+
+LOCAL_MODULE_CLASS := EXECUTABLES
+inc := $(call local-generated-sources-dir)/register.inc
+
+$(inc) : libs := $(TARGET_RECOVERY_UPDATER_HOST_LIBS)
+$(inc) :
+ $(call generate-register-inc,$@,$(libs))
+
+LOCAL_GENERATED_SOURCES := $(inc)
+
+inc :=
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index 07c3c7b52..2d41f610b 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -42,17 +42,18 @@
#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>
#include <brotli/decode.h>
#include <fec/io.h>
#include <openssl/sha.h>
-#include <private/android_filesystem_config.h>
#include <verity/hash_tree_builder.h>
#include <ziparchive/zip_archive.h>
#include "edify/expr.h"
+#include "edify/updater_interface.h"
#include "otautil/dirutil.h"
#include "otautil/error_code.h"
#include "otautil/paths.h"
@@ -60,12 +61,16 @@
#include "otautil/rangeset.h"
#include "private/commands.h"
#include "updater/install.h"
-#include "updater/updater.h"
-// Set this to 0 to interpret 'erase' transfers to mean do a
-// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret
-// erase to mean fill the region with zeroes.
+#ifdef __ANDROID__
+#include <private/android_filesystem_config.h>
+// Set this to 0 to interpret 'erase' transfers to mean do a BLKDISCARD ioctl (the normal behavior).
+// Set to 1 to interpret erase to mean fill the region with zeroes.
#define DEBUG_ERASE 0
+#else
+#define DEBUG_ERASE 1
+#define AID_SYSTEM -1
+#endif // __ANDROID__
static constexpr size_t BLOCKSIZE = 4096;
static constexpr mode_t STASH_DIRECTORY_MODE = 0700;
@@ -1668,42 +1673,43 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
return StringValue("");
}
- UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
- if (ui == nullptr) {
+ auto updater = state->updater;
+ auto block_device_path = updater->FindBlockDeviceName(blockdev_filename->data);
+ if (block_device_path.empty()) {
+ LOG(ERROR) << "Block device path for " << blockdev_filename->data << " not found. " << name
+ << " failed.";
return StringValue("");
}
- FILE* cmd_pipe = ui->cmd_pipe;
- ZipArchiveHandle za = ui->package_zip;
-
- if (cmd_pipe == nullptr || za == nullptr) {
+ ZipArchiveHandle za = updater->GetPackageHandle();
+ if (za == nullptr) {
return StringValue("");
}
- ZipString path_data(patch_data_fn->data.c_str());
+ std::string_view path_data(patch_data_fn->data);
ZipEntry patch_entry;
if (FindEntry(za, path_data, &patch_entry) != 0) {
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;
- ZipString new_data(new_data_fn->data.c_str());
+ std::string_view new_data(new_data_fn->data);
ZipEntry new_entry;
if (FindEntry(za, new_data, &new_entry) != 0) {
LOG(ERROR) << name << "(): no file \"" << new_data_fn->data << "\" in package";
return StringValue("");
}
- params.fd.reset(TEMP_FAILURE_RETRY(open(blockdev_filename->data.c_str(), O_RDWR)));
+ params.fd.reset(TEMP_FAILURE_RETRY(open(block_device_path.c_str(), O_RDWR)));
if (params.fd == -1) {
failure_type = errno == EIO ? kEioFailure : kFileOpenFailure;
- PLOG(ERROR) << "open \"" << blockdev_filename->data << "\" failed";
+ PLOG(ERROR) << "open \"" << block_device_path << "\" failed";
return StringValue("");
}
uint8_t digest[SHA_DIGEST_LENGTH];
- if (!Sha1DevicePath(blockdev_filename->data, digest)) {
+ if (!Sha1DevicePath(block_device_path, digest)) {
return StringValue("");
}
params.stashbase = print_sha1(digest);
@@ -1716,8 +1722,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
struct stat sb;
int result = stat(updated_marker.c_str(), &sb);
if (result == 0) {
- LOG(INFO) << "Skipping already updated partition " << blockdev_filename->data
- << " based on marker";
+ LOG(INFO) << "Skipping already updated partition " << block_device_path << " based on marker";
return StringValue("t");
}
} else {
@@ -1887,8 +1892,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);
}
}
@@ -1913,13 +1920,15 @@ pbiudone:
LOG(INFO) << "stashed " << params.stashed << " blocks";
LOG(INFO) << "max alloc needed was " << params.buffer.size();
- const char* partition = strrchr(blockdev_filename->data.c_str(), '/');
+ const char* partition = strrchr(block_device_path.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.
@@ -2019,7 +2028,7 @@ Value* BlockImageVerifyFn(const char* name, State* state,
// clang-format off
{ Command::Type::ABORT, PerformCommandAbort },
{ Command::Type::BSDIFF, PerformCommandDiff },
- { Command::Type::COMPUTE_HASH_TREE, PerformCommandComputeHashTree },
+ { Command::Type::COMPUTE_HASH_TREE, nullptr },
{ Command::Type::ERASE, nullptr },
{ Command::Type::FREE, PerformCommandFree },
{ Command::Type::IMGDIFF, PerformCommandDiff },
@@ -2079,10 +2088,17 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique
return StringValue("");
}
- android::base::unique_fd fd(open(blockdev_filename->data.c_str(), O_RDWR));
+ auto block_device_path = state->updater->FindBlockDeviceName(blockdev_filename->data);
+ if (block_device_path.empty()) {
+ LOG(ERROR) << "Block device path for " << blockdev_filename->data << " not found. " << name
+ << " failed.";
+ return StringValue("");
+ }
+
+ android::base::unique_fd fd(open(block_device_path.c_str(), O_RDWR));
if (fd == -1) {
CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure;
- ErrorAbort(state, cause_code, "open \"%s\" failed: %s", blockdev_filename->data.c_str(),
+ ErrorAbort(state, cause_code, "open \"%s\" failed: %s", block_device_path.c_str(),
strerror(errno));
return StringValue("");
}
@@ -2096,7 +2112,7 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique
std::vector<uint8_t> buffer(BLOCKSIZE);
for (const auto& [begin, end] : rs) {
if (!check_lseek(fd, static_cast<off64_t>(begin) * BLOCKSIZE, SEEK_SET)) {
- ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(),
+ ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", block_device_path.c_str(),
strerror(errno));
return StringValue("");
}
@@ -2104,7 +2120,7 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique
for (size_t j = begin; j < end; ++j) {
if (!android::base::ReadFully(fd, buffer.data(), BLOCKSIZE)) {
CauseCode cause_code = errno == EIO ? kEioFailure : kFreadFailure;
- ErrorAbort(state, cause_code, "failed to read %s: %s", blockdev_filename->data.c_str(),
+ ErrorAbort(state, cause_code, "failed to read %s: %s", block_device_path.c_str(),
strerror(errno));
return StringValue("");
}
@@ -2143,10 +2159,17 @@ Value* CheckFirstBlockFn(const char* name, State* state,
return StringValue("");
}
- android::base::unique_fd fd(open(arg_filename->data.c_str(), O_RDONLY));
+ auto block_device_path = state->updater->FindBlockDeviceName(arg_filename->data);
+ if (block_device_path.empty()) {
+ LOG(ERROR) << "Block device path for " << arg_filename->data << " not found. " << name
+ << " failed.";
+ return StringValue("");
+ }
+
+ android::base::unique_fd fd(open(block_device_path.c_str(), O_RDONLY));
if (fd == -1) {
CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure;
- ErrorAbort(state, cause_code, "open \"%s\" failed: %s", arg_filename->data.c_str(),
+ ErrorAbort(state, cause_code, "open \"%s\" failed: %s", block_device_path.c_str(),
strerror(errno));
return StringValue("");
}
@@ -2156,7 +2179,7 @@ Value* CheckFirstBlockFn(const char* name, State* state,
if (ReadBlocks(blk0, &block0_buffer, fd) == -1) {
CauseCode cause_code = errno == EIO ? kEioFailure : kFreadFailure;
- ErrorAbort(state, cause_code, "failed to read %s: %s", arg_filename->data.c_str(),
+ ErrorAbort(state, cause_code, "failed to read %s: %s", block_device_path.c_str(),
strerror(errno));
return StringValue("");
}
@@ -2172,8 +2195,10 @@ 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));
+ state->updater->UiPrint(
+ android::base::StringPrintf("Device was remounted R/W %" PRIu16 " times", mount_count));
+ state->updater->UiPrint(
+ android::base::StringPrintf("Last remount happened on %s", ctime(&mount_time)));
}
return StringValue("t");
@@ -2209,14 +2234,21 @@ Value* BlockImageRecoverFn(const char* name, State* state,
return StringValue("");
}
+ auto block_device_path = state->updater->FindBlockDeviceName(filename->data);
+ if (block_device_path.empty()) {
+ LOG(ERROR) << "Block device path for " << filename->data << " not found. " << name
+ << " failed.";
+ return StringValue("");
+ }
+
// Output notice to log when recover is attempted
- LOG(INFO) << filename->data << " image corrupted, attempting to recover...";
+ LOG(INFO) << block_device_path << " image corrupted, attempting to recover...";
// When opened with O_RDWR, libfec rewrites corrupted blocks when they are read
- fec::io fh(filename->data, O_RDWR);
+ fec::io fh(block_device_path, O_RDWR);
if (!fh) {
- ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(),
+ ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", block_device_path.c_str(),
strerror(errno));
return StringValue("");
}
@@ -2242,7 +2274,7 @@ Value* BlockImageRecoverFn(const char* name, State* state,
if (fh.pread(buffer, BLOCKSIZE, static_cast<off64_t>(j) * BLOCKSIZE) != BLOCKSIZE) {
ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s",
- filename->data.c_str(), j, strerror(errno));
+ block_device_path.c_str(), j, strerror(errno));
return StringValue("");
}
@@ -2258,7 +2290,7 @@ Value* BlockImageRecoverFn(const char* name, State* state,
// read and check if the errors field value has increased.
}
}
- LOG(INFO) << "..." << filename->data << " image recovered successfully.";
+ LOG(INFO) << "..." << block_device_path << " image recovered successfully.";
return StringValue("t");
}
diff --git a/updater/include/updater/install.h b/updater/include/updater/install.h
index 8d6ca4728..9fe203149 100644
--- a/updater/include/updater/install.h
+++ b/updater/include/updater/install.h
@@ -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
diff --git a/updater/include/updater/simulator_runtime.h b/updater/include/updater/simulator_runtime.h
new file mode 100644
index 000000000..93fa2a4e5
--- /dev/null
+++ b/updater/include/updater/simulator_runtime.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "edify/updater_runtime_interface.h"
+#include "updater/target_files.h"
+
+class SimulatorRuntime : public UpdaterRuntimeInterface {
+ public:
+ explicit SimulatorRuntime(TargetFiles* source) : source_(source) {}
+
+ bool IsSimulator() const override {
+ return true;
+ }
+
+ std::string GetProperty(const std::string_view key,
+ const std::string_view default_value) const override;
+
+ int Mount(const std::string_view location, const std::string_view mount_point,
+ const std::string_view fs_type, const std::string_view mount_options) override;
+ bool IsMounted(const std::string_view mount_point) const override;
+ std::pair<bool, int> Unmount(const std::string_view mount_point) override;
+
+ bool ReadFileToString(const std::string_view filename, std::string* content) const override;
+ bool WriteStringToFile(const std::string_view content,
+ const std::string_view filename) const override;
+
+ int WipeBlockDevice(const std::string_view filename, size_t len) const override;
+ int RunProgram(const std::vector<std::string>& args, bool is_vfork) const override;
+ int Tune2Fs(const std::vector<std::string>& args) const override;
+
+ private:
+ std::string FindBlockDeviceName(const std::string_view name) const override;
+
+ TargetFiles* source_;
+ std::map<std::string, std::string, std::less<>> mounted_partitions_;
+};
diff --git a/updater/include/updater/target_files.h b/updater/include/updater/target_files.h
new file mode 100644
index 000000000..9ef1a5bf3
--- /dev/null
+++ b/updater/include/updater/target_files.h
@@ -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.
+ */
+
+#pragma once
+
+#include <string>
+
+// This class parses a given target file for the build properties and image files. Then it creates
+// and maintains the temporary files to simulate the block devices on host.
+class TargetFiles {
+ public:
+ TargetFiles(std::string path, std::string work_dir)
+ : path_(std::move(path)), work_dir_(std::move(work_dir)) {}
+
+ std::string GetProperty(const std::string_view key, const std::string_view default_value) const;
+
+ std::string FindBlockDeviceName(const std::string_view name) const;
+
+ private:
+ std::string path_; // Path to the target file.
+
+ std::string work_dir_; // A temporary directory to store the extracted image files
+};
diff --git a/updater/include/updater/updater.h b/updater/include/updater/updater.h
index f4a2fe874..08816bf36 100644
--- a/updater/include/updater/updater.h
+++ b/updater/include/updater/updater.h
@@ -14,22 +14,81 @@
* limitations under the License.
*/
-#ifndef _UPDATER_UPDATER_H_
-#define _UPDATER_UPDATER_H_
+#pragma once
+#include <stdint.h>
#include <stdio.h>
+
+#include <memory>
+#include <string>
+#include <string_view>
+
#include <ziparchive/zip_archive.h>
-typedef struct {
- FILE* cmd_pipe;
- ZipArchiveHandle package_zip;
- int version;
+#include "edify/expr.h"
+#include "edify/updater_interface.h"
+#include "otautil/error_code.h"
+#include "otautil/sysutil.h"
+
+class Updater : public UpdaterInterface {
+ public:
+ explicit Updater(std::unique_ptr<UpdaterRuntimeInterface> run_time)
+ : runtime_(std::move(run_time)) {}
+
+ ~Updater() override;
+
+ // Memory-maps the OTA package and opens it as a zip file. Also sets up the command pipe and
+ // UpdaterRuntime.
+ bool Init(int fd, const std::string_view package_filename, bool is_retry);
+
+ // Parses and evaluates the updater-script in the OTA package. Reports the error code if the
+ // evaluation fails.
+ bool RunUpdate();
+
+ // Writes the message to command pipe, adds a new line in the end.
+ void WriteToCommandPipe(const std::string_view message, bool flush = false) const override;
+
+ // Sends over the message to recovery to print it on the screen.
+ void UiPrint(const std::string_view message) const override;
+
+ std::string FindBlockDeviceName(const std::string_view name) const override;
+
+ UpdaterRuntimeInterface* GetRuntime() const override {
+ return runtime_.get();
+ }
+ ZipArchiveHandle GetPackageHandle() const override {
+ return package_handle_;
+ }
+ std::string GetResult() const override {
+ return result_;
+ }
+
+ uint8_t* GetMappedPackageAddress() const override {
+ return mapped_package_.addr;
+ }
+
+ private:
+ friend class UpdaterTestBase;
+ friend class UpdaterTest;
+ // Where in the package we expect to find the edify script to execute.
+ // (Note it's "updateR-script", not the older "update-script".)
+ static constexpr const char* SCRIPT_NAME = "META-INF/com/google/android/updater-script";
+
+ // Reads the entry |name| in the zip archive and put the result in |content|.
+ bool ReadEntryToString(ZipArchiveHandle za, const std::string& entry_name, std::string* content);
+
+ // Parses the error code embedded in state->errmsg; and reports the error code and cause code.
+ void ParseAndReportErrorCode(State* state);
+
+ std::unique_ptr<UpdaterRuntimeInterface> runtime_;
- uint8_t* package_zip_addr;
- size_t package_zip_len;
-} UpdaterInfo;
+ MemMapping mapped_package_;
+ ZipArchiveHandle package_handle_{ nullptr };
+ std::string updater_script_;
-struct selabel_handle;
-extern struct selabel_handle *sehandle;
+ bool is_retry_{ false };
+ std::unique_ptr<FILE, decltype(&fclose)> cmd_pipe_{ nullptr, fclose };
-#endif
+ std::string result_;
+ std::vector<std::string> skipped_functions_;
+};
diff --git a/updater/include/updater/updater_runtime.h b/updater/include/updater/updater_runtime.h
new file mode 100644
index 000000000..e97eb49b1
--- /dev/null
+++ b/updater/include/updater/updater_runtime.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "edify/updater_runtime_interface.h"
+
+struct selabel_handle;
+
+class UpdaterRuntime : public UpdaterRuntimeInterface {
+ public:
+ explicit UpdaterRuntime(struct selabel_handle* sehandle) : sehandle_(sehandle) {}
+ ~UpdaterRuntime() override = default;
+
+ bool IsSimulator() const override {
+ return false;
+ }
+
+ std::string GetProperty(const std::string_view key,
+ const std::string_view default_value) const override;
+
+ std::string FindBlockDeviceName(const std::string_view name) const override;
+
+ int Mount(const std::string_view location, const std::string_view mount_point,
+ const std::string_view fs_type, const std::string_view mount_options) override;
+ bool IsMounted(const std::string_view mount_point) const override;
+ std::pair<bool, int> Unmount(const std::string_view mount_point) override;
+
+ bool ReadFileToString(const std::string_view filename, std::string* content) const override;
+ bool WriteStringToFile(const std::string_view content,
+ const std::string_view filename) const override;
+
+ int WipeBlockDevice(const std::string_view filename, size_t len) const override;
+ int RunProgram(const std::vector<std::string>& args, bool is_vfork) const override;
+ int Tune2Fs(const std::vector<std::string>& args) const override;
+
+ struct selabel_handle* sehandle_{ nullptr };
+};
diff --git a/updater/install.cpp b/updater/install.cpp
index 20a204a83..c82351ec4 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -53,45 +53,31 @@
#include <openssl/sha.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
-#include <tune2fs.h>
#include <ziparchive/zip_archive.h>
#include "edify/expr.h"
+#include "edify/updater_interface.h"
+#include "edify/updater_runtime_interface.h"
#include "otautil/dirutil.h"
#include "otautil/error_code.h"
#include "otautil/mounts.h"
#include "otautil/print_sha1.h"
#include "otautil/sysutil.h"
-#include "updater/updater.h"
-// Send over the buffer to recovery though the command pipe.
-static void uiPrint(State* state, const std::string& buffer) {
- UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
+#ifndef __ANDROID__
+#include <cutils/memory.h> // for strlcpy
+#endif
- // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "".
- // So skip sending empty strings to UI.
- std::vector<std::string> lines = android::base::Split(buffer, "\n");
- for (auto& line : lines) {
- if (!line.empty()) {
- fprintf(ui->cmd_pipe, "ui_print %s\n", line.c_str());
- }
+static bool UpdateBlockDeviceNameForPartition(UpdaterInterface* updater, Partition* partition) {
+ CHECK(updater);
+ std::string name = updater->FindBlockDeviceName(partition->name);
+ if (name.empty()) {
+ LOG(ERROR) << "Failed to find the block device " << partition->name;
+ return false;
}
- // On the updater side, we need to dump the contents to stderr (which has
- // been redirected to the log file). Because the recovery will only print
- // the contents to screen when processing pipe command ui_print.
- LOG(INFO) << buffer;
-}
-
-void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) {
- std::string error_msg;
-
- va_list ap;
- va_start(ap, format);
- android::base::StringAppendV(&error_msg, format, ap);
- va_end(ap);
-
- uiPrint(state, error_msg);
+ partition->name = std::move(name);
+ return true;
}
// This is the updater side handler for ui_print() in edify script. Contents will be sent over to
@@ -103,7 +89,7 @@ Value* UIPrintFn(const char* name, State* state, const std::vector<std::unique_p
}
std::string buffer = android::base::Join(args, "");
- uiPrint(state, buffer);
+ state->updater->UiPrint(buffer);
return StringValue(buffer);
}
@@ -129,10 +115,9 @@ Value* PackageExtractFileFn(const char* name, State* state,
const std::string& zip_path = args[0];
const std::string& dest_path = args[1];
- ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
- ZipString zip_string_path(zip_path.c_str());
+ ZipArchiveHandle za = state->updater->GetPackageHandle();
ZipEntry entry;
- if (FindEntry(za, zip_string_path, &entry) != 0) {
+ if (FindEntry(za, zip_path, &entry) != 0) {
LOG(ERROR) << name << ": no " << zip_path << " in package";
return StringValue("");
}
@@ -173,10 +158,9 @@ Value* PackageExtractFileFn(const char* name, State* state,
}
const std::string& zip_path = args[0];
- ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
- ZipString zip_string_path(zip_path.c_str());
+ ZipArchiveHandle za = state->updater->GetPackageHandle();
ZipEntry entry;
- if (FindEntry(za, zip_string_path, &entry) != 0) {
+ if (FindEntry(za, zip_path, &entry) != 0) {
return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name,
zip_path.c_str());
}
@@ -229,6 +213,11 @@ Value* PatchPartitionCheckFn(const char* name, State* state,
args[1].c_str(), err.c_str());
}
+ if (!UpdateBlockDeviceNameForPartition(state->updater, &source) ||
+ !UpdateBlockDeviceNameForPartition(state->updater, &target)) {
+ return StringValue("");
+ }
+
bool result = PatchPartitionCheck(target, source);
return StringValue(result ? "t" : "");
}
@@ -270,6 +259,11 @@ Value* PatchPartitionFn(const char* name, State* state,
return ErrorAbort(state, kArgsParsingFailure, "%s(): Invalid patch arg", name);
}
+ if (!UpdateBlockDeviceNameForPartition(state->updater, &source) ||
+ !UpdateBlockDeviceNameForPartition(state->updater, &target)) {
+ return StringValue("");
+ }
+
bool result = PatchPartition(target, source, *values[0], nullptr);
return StringValue(result ? "t" : "");
}
@@ -313,26 +307,11 @@ Value* MountFn(const char* name, State* state, const std::vector<std::unique_ptr
name);
}
- {
- char* secontext = nullptr;
-
- if (sehandle) {
- selabel_lookup(sehandle, &secontext, mount_point.c_str(), 0755);
- setfscreatecon(secontext);
- }
-
- mkdir(mount_point.c_str(), 0755);
-
- if (secontext) {
- freecon(secontext);
- setfscreatecon(nullptr);
- }
- }
-
- if (mount(location.c_str(), mount_point.c_str(), fs_type.c_str(),
- MS_NOATIME | MS_NODEV | MS_NODIRATIME, mount_options.c_str()) < 0) {
- uiPrintf(state, "%s: Failed to mount %s at %s: %s", name, location.c_str(), mount_point.c_str(),
- strerror(errno));
+ auto updater = state->updater;
+ if (updater->GetRuntime()->Mount(location, mount_point, fs_type, mount_options) != 0) {
+ updater->UiPrint(android::base::StringPrintf("%s: Failed to mount %s at %s: %s", name,
+ location.c_str(), mount_point.c_str(),
+ strerror(errno)));
return StringValue("");
}
@@ -355,9 +334,8 @@ Value* IsMountedFn(const char* name, State* state, const std::vector<std::unique
"mount_point argument to unmount() can't be empty");
}
- scan_mounted_volumes();
- MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str());
- if (vol == nullptr) {
+ auto updater_runtime = state->updater->GetRuntime();
+ if (!updater_runtime->IsMounted(mount_point)) {
return StringValue("");
}
@@ -378,39 +356,20 @@ Value* UnmountFn(const char* name, State* state, const std::vector<std::unique_p
"mount_point argument to unmount() can't be empty");
}
- scan_mounted_volumes();
- MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str());
- if (vol == nullptr) {
- uiPrintf(state, "Failed to unmount %s: No such volume", mount_point.c_str());
+ auto updater = state->updater;
+ auto [mounted, result] = updater->GetRuntime()->Unmount(mount_point);
+ if (!mounted) {
+ updater->UiPrint(
+ android::base::StringPrintf("Failed to unmount %s: No such volume", mount_point.c_str()));
return nullptr;
- } else {
- int ret = unmount_mounted_volume(vol);
- if (ret != 0) {
- uiPrintf(state, "Failed to unmount %s: %s", mount_point.c_str(), strerror(errno));
- }
+ } else if (result != 0) {
+ updater->UiPrint(android::base::StringPrintf("Failed to unmount %s: %s", mount_point.c_str(),
+ strerror(errno)));
}
return StringValue(mount_point);
}
-static int exec_cmd(const std::vector<std::string>& args) {
- CHECK(!args.empty());
- auto argv = StringVectorToNullTerminatedArray(args);
-
- pid_t child;
- if ((child = vfork()) == 0) {
- execv(argv[0], argv.data());
- _exit(EXIT_FAILURE);
- }
-
- int status;
- waitpid(child, &status, 0);
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- LOG(ERROR) << args[0] << " failed with status " << WEXITSTATUS(status);
- }
- return WEXITSTATUS(status);
-}
-
// format(fs_type, partition_type, location, fs_size, mount_point)
//
// fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location>
@@ -455,6 +414,7 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt
fs_size.c_str());
}
+ auto updater_runtime = state->updater->GetRuntime();
if (fs_type == "ext4") {
std::vector<std::string> mke2fs_args = {
"/system/bin/mke2fs", "-t", "ext4", "-b", "4096", location
@@ -463,12 +423,13 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt
mke2fs_args.push_back(std::to_string(size / 4096LL));
}
- if (auto status = exec_cmd(mke2fs_args); status != 0) {
+ if (auto status = updater_runtime->RunProgram(mke2fs_args, true); status != 0) {
LOG(ERROR) << name << ": mke2fs failed (" << status << ") on " << location;
return StringValue("");
}
- if (auto status = exec_cmd({ "/system/bin/e2fsdroid", "-e", "-a", mount_point, location });
+ if (auto status = updater_runtime->RunProgram(
+ { "/system/bin/e2fsdroid", "-e", "-a", mount_point, location }, true);
status != 0) {
LOG(ERROR) << name << ": e2fsdroid failed (" << status << ") on " << location;
return StringValue("");
@@ -487,12 +448,13 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt
if (size >= 512) {
f2fs_args.push_back(std::to_string(size / 512));
}
- if (auto status = exec_cmd(f2fs_args); status != 0) {
+ if (auto status = updater_runtime->RunProgram(f2fs_args, true); status != 0) {
LOG(ERROR) << name << ": make_f2fs failed (" << status << ") on " << location;
return StringValue("");
}
- if (auto status = exec_cmd({ "/system/bin/sload_f2fs", "-t", mount_point, location });
+ if (auto status = updater_runtime->RunProgram(
+ { "/system/bin/sload_f2fs", "-t", mount_point, location }, true);
status != 0) {
LOG(ERROR) << name << ": sload_f2fs failed (" << status << ") on " << location;
return StringValue("");
@@ -531,8 +493,7 @@ Value* ShowProgressFn(const char* name, State* state,
sec_str.c_str());
}
- UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
- fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
+ state->updater->WriteToCommandPipe(android::base::StringPrintf("progress %f %d", frac, sec));
return StringValue(frac_str);
}
@@ -555,8 +516,7 @@ Value* SetProgressFn(const char* name, State* state,
frac_str.c_str());
}
- UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
- fprintf(ui->cmd_pipe, "set_progress %f\n", frac);
+ state->updater->WriteToCommandPipe(android::base::StringPrintf("set_progress %f", frac));
return StringValue(frac_str);
}
@@ -569,7 +529,9 @@ Value* GetPropFn(const char* name, State* state, const std::vector<std::unique_p
if (!Evaluate(state, argv[0], &key)) {
return nullptr;
}
- std::string value = android::base::GetProperty(key, "");
+
+ auto updater_runtime = state->updater->GetRuntime();
+ std::string value = updater_runtime->GetProperty(key, "");
return StringValue(value);
}
@@ -594,7 +556,8 @@ Value* FileGetPropFn(const char* name, State* state,
const std::string& key = args[1];
std::string buffer;
- if (!android::base::ReadFileToString(filename, &buffer)) {
+ auto updater_runtime = state->updater->GetRuntime();
+ if (!updater_runtime->ReadFileToString(filename, &buffer)) {
ErrorAbort(state, kFreadFailure, "%s: failed to read %s", name, filename.c_str());
return nullptr;
}
@@ -655,7 +618,8 @@ Value* WipeCacheFn(const char* name, State* state, const std::vector<std::unique
return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name,
argv.size());
}
- fprintf(static_cast<UpdaterInfo*>(state->cookie)->cmd_pipe, "wipe_cache\n");
+
+ state->updater->WriteToCommandPipe("wipe_cache");
return StringValue("t");
}
@@ -669,26 +633,8 @@ Value* RunProgramFn(const char* name, State* state, const std::vector<std::uniqu
return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
- auto exec_args = StringVectorToNullTerminatedArray(args);
- LOG(INFO) << "about to run program [" << exec_args[0] << "] with " << argv.size() << " args";
-
- pid_t child = fork();
- if (child == 0) {
- execv(exec_args[0], exec_args.data());
- PLOG(ERROR) << "run_program: execv failed";
- _exit(EXIT_FAILURE);
- }
-
- int status;
- waitpid(child, &status, 0);
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) != 0) {
- LOG(ERROR) << "run_program: child exited with status " << WEXITSTATUS(status);
- }
- } else if (WIFSIGNALED(status)) {
- LOG(ERROR) << "run_program: child terminated by signal " << WTERMSIG(status);
- }
-
+ auto updater_runtime = state->updater->GetRuntime();
+ auto status = updater_runtime->RunProgram(args, false);
return StringValue(std::to_string(status));
}
@@ -706,7 +652,8 @@ Value* ReadFileFn(const char* name, State* state, const std::vector<std::unique_
const std::string& filename = args[0];
std::string contents;
- if (android::base::ReadFileToString(filename, &contents)) {
+ auto updater_runtime = state->updater->GetRuntime();
+ if (updater_runtime->ReadFileToString(filename, &contents)) {
return new Value(Value::Type::STRING, std::move(contents));
}
@@ -735,12 +682,12 @@ Value* WriteValueFn(const char* name, State* state, const std::vector<std::uniqu
}
const std::string& value = args[0];
- if (!android::base::WriteStringToFile(value, filename)) {
+ auto updater_runtime = state->updater->GetRuntime();
+ if (!updater_runtime->WriteStringToFile(value, filename)) {
PLOG(ERROR) << name << ": Failed to write to \"" << filename << "\"";
return StringValue("");
- } else {
- return StringValue("t");
}
+ return StringValue("t");
}
// Immediately reboot the device. Recovery is not finished normally,
@@ -778,7 +725,7 @@ Value* RebootNowFn(const char* name, State* state, const std::vector<std::unique
return StringValue("");
}
- reboot("reboot," + property);
+ Reboot(property);
sleep(5);
return ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name);
@@ -866,16 +813,10 @@ Value* WipeBlockDeviceFn(const char* name, State* state, const std::vector<std::
if (!android::base::ParseUint(len_str.c_str(), &len)) {
return nullptr;
}
- android::base::unique_fd fd(open(filename.c_str(), O_WRONLY));
- if (fd == -1) {
- PLOG(ERROR) << "Failed to open " << filename;
- return StringValue("");
- }
- // The wipe_block_device function in ext4_utils returns 0 on success and 1
- // for failure.
- int status = wipe_block_device(fd, len);
- return StringValue((status == 0) ? "t" : "");
+ auto updater_runtime = state->updater->GetRuntime();
+ int status = updater_runtime->WipeBlockDevice(filename, len);
+ return StringValue(status == 0 ? "t" : "");
}
Value* EnableRebootFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
@@ -883,8 +824,7 @@ Value* EnableRebootFn(const char* name, State* state, const std::vector<std::uni
return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name,
argv.size());
}
- UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
- fprintf(ui->cmd_pipe, "enable_reboot\n");
+ state->updater->WriteToCommandPipe("enable_reboot");
return StringValue("t");
}
@@ -900,10 +840,8 @@ Value* Tune2FsFn(const char* name, State* state, const std::vector<std::unique_p
// tune2fs expects the program name as its first arg.
args.insert(args.begin(), "tune2fs");
- auto tune2fs_args = StringVectorToNullTerminatedArray(args);
-
- // tune2fs changes the filesystem parameters on an ext2 filesystem; it returns 0 on success.
- if (auto result = tune2fs_main(tune2fs_args.size() - 1, tune2fs_args.data()); result != 0) {
+ auto updater_runtime = state->updater->GetRuntime();
+ if (auto result = updater_runtime->Tune2Fs(args); result != 0) {
return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", name, result);
}
return StringValue("t");
diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp
new file mode 100644
index 000000000..c3b3d951c
--- /dev/null
+++ b/updater/simulator_runtime.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 "updater/simulator_runtime.h"
+
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/wipe.h>
+#include <selinux/label.h>
+
+#include "otautil/mounts.h"
+#include "otautil/sysutil.h"
+
+std::string SimulatorRuntime::GetProperty(const std::string_view key,
+ const std::string_view default_value) const {
+ return source_->GetProperty(key, default_value);
+}
+
+int SimulatorRuntime::Mount(const std::string_view location, const std::string_view mount_point,
+ const std::string_view /* fs_type */,
+ const std::string_view /* mount_options */) {
+ if (auto mounted_location = mounted_partitions_.find(mount_point);
+ mounted_location != mounted_partitions_.end() && mounted_location->second != location) {
+ LOG(ERROR) << mount_point << " has been mounted at " << mounted_location->second;
+ return -1;
+ }
+
+ mounted_partitions_.emplace(mount_point, location);
+ return 0;
+}
+
+bool SimulatorRuntime::IsMounted(const std::string_view mount_point) const {
+ return mounted_partitions_.find(mount_point) != mounted_partitions_.end();
+}
+
+std::pair<bool, int> SimulatorRuntime::Unmount(const std::string_view mount_point) {
+ if (!IsMounted(mount_point)) {
+ return { false, -1 };
+ }
+
+ mounted_partitions_.erase(std::string(mount_point));
+ return { true, 0 };
+}
+
+std::string SimulatorRuntime::FindBlockDeviceName(const std::string_view name) const {
+ return source_->FindBlockDeviceName(name);
+}
+
+// TODO(xunchang) implement the utility functions in simulator.
+int SimulatorRuntime::RunProgram(const std::vector<std::string>& args, bool /* is_vfork */) const {
+ LOG(INFO) << "Running program with args " << android::base::Join(args, " ");
+ return 0;
+}
+
+int SimulatorRuntime::Tune2Fs(const std::vector<std::string>& args) const {
+ LOG(INFO) << "Running Tune2Fs with args " << android::base::Join(args, " ");
+ return 0;
+}
+
+int SimulatorRuntime::WipeBlockDevice(const std::string_view filename, size_t /* len */) const {
+ LOG(INFO) << "SKip wiping block device " << filename;
+ return 0;
+}
+
+bool SimulatorRuntime::ReadFileToString(const std::string_view filename,
+ std::string* /* content */) const {
+ LOG(INFO) << "SKip reading filename " << filename;
+ return true;
+}
+
+bool SimulatorRuntime::WriteStringToFile(const std::string_view content,
+ const std::string_view filename) const {
+ LOG(INFO) << "SKip writing " << content.size() << " bytes to file " << filename;
+ return true;
+}
diff --git a/updater/target_files.cpp b/updater/target_files.cpp
new file mode 100644
index 000000000..53671dd9d
--- /dev/null
+++ b/updater/target_files.cpp
@@ -0,0 +1,26 @@
+/*
+ * 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 "updater/target_files.h"
+
+std::string TargetFiles::GetProperty(const std::string_view /*key*/,
+ const std::string_view default_value) const {
+ return std::string(default_value);
+}
+
+std::string TargetFiles::FindBlockDeviceName(const std::string_view name) const {
+ return std::string(name);
+}
diff --git a/updater/update_simulator_main.cpp b/updater/update_simulator_main.cpp
new file mode 100644
index 000000000..d10453c2f
--- /dev/null
+++ b/updater/update_simulator_main.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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 <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+#include "otautil/error_code.h"
+#include "otautil/paths.h"
+#include "updater/blockimg.h"
+#include "updater/install.h"
+#include "updater/simulator_runtime.h"
+#include "updater/target_files.h"
+#include "updater/updater.h"
+
+int main(int argc, char** argv) {
+ // Write the logs to stdout.
+ android::base::InitLogging(argv, &android::base::StderrLogger);
+
+ if (argc != 3 && argc != 4) {
+ LOG(ERROR) << "unexpected number of arguments: " << argc << std::endl
+ << "Usage: " << argv[0] << " <source_target-file> <ota_package>";
+ return 1;
+ }
+
+ // TODO(xunchang) implement a commandline parser, e.g. it can take an oem property so that the
+ // file_getprop() will return correct value.
+
+ std::string source_target_file = argv[1];
+ std::string package_name = argv[2];
+
+ // Configure edify's functions.
+ RegisterBuiltins();
+ RegisterInstallFunctions();
+ RegisterBlockImageFunctions();
+
+ 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);
+
+ TemporaryFile cmd_pipe;
+
+ TemporaryDir source_temp_dir;
+ TargetFiles source(source_target_file, source_temp_dir.path);
+
+ Updater updater(std::make_unique<SimulatorRuntime>(&source));
+ if (!updater.Init(cmd_pipe.release(), package_name, false)) {
+ return 1;
+ }
+
+ if (!updater.RunUpdate()) {
+ return 1;
+ }
+
+ LOG(INFO) << "\nscript succeeded, result: " << updater.GetResult();
+
+ return 0;
+}
diff --git a/updater/updater.cpp b/updater/updater.cpp
index 7b5a3f938..8f4a6ede5 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -16,8 +16,6 @@
#include "updater/updater.h"
-#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -25,198 +23,162 @@
#include <android-base/logging.h>
#include <android-base/strings.h>
-#include <selinux/android.h>
-#include <selinux/label.h>
-#include <selinux/selinux.h>
-#include <ziparchive/zip_archive.h>
-
-#include "edify/expr.h"
-#include "otautil/dirutil.h"
-#include "otautil/error_code.h"
-#include "otautil/sysutil.h"
-#include "updater/blockimg.h"
-#include "updater/dynamic_partitions.h"
-#include "updater/install.h"
-
-// Generated by the makefile, this function defines the
-// RegisterDeviceExtensions() function, which calls all the
-// registration functions for device-specific extensions.
-#include "register.inc"
-
-// Where in the package we expect to find the edify script to execute.
-// (Note it's "updateR-script", not the older "update-script".)
-static constexpr const char* SCRIPT_NAME = "META-INF/com/google/android/updater-script";
-
-struct selabel_handle *sehandle;
-
-static void UpdaterLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */,
- const char* /* tag */, const char* /* file */, unsigned int /* line */,
- const char* message) {
- fprintf(stdout, "%s\n", message);
-}
-int main(int argc, char** argv) {
- // Various things log information to stdout or stderr more or less
- // at random (though we've tried to standardize on stdout). The
- // log file makes more sense if buffering is turned off so things
- // appear in the right order.
- setbuf(stdout, nullptr);
- setbuf(stderr, nullptr);
-
- // We don't have logcat yet under recovery. Update logs will always be written to stdout
- // (which is redirected to recovery.log).
- android::base::InitLogging(argv, &UpdaterLogger);
-
- if (argc != 4 && argc != 5) {
- LOG(ERROR) << "unexpected number of arguments: " << argc;
- return 1;
- }
+#include "edify/updater_runtime_interface.h"
- char* version = argv[1];
- if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') {
- // We support version 1, 2, or 3.
- LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1];
- return 2;
+Updater::~Updater() {
+ if (package_handle_) {
+ CloseArchive(package_handle_);
}
+}
+bool Updater::Init(int fd, const std::string_view package_filename, bool is_retry) {
// Set up the pipe for sending commands back to the parent process.
+ cmd_pipe_.reset(fdopen(fd, "wb"));
+ if (!cmd_pipe_) {
+ LOG(ERROR) << "Failed to open the command pipe";
+ return false;
+ }
- int fd = atoi(argv[2]);
- FILE* cmd_pipe = fdopen(fd, "wb");
- setlinebuf(cmd_pipe);
-
- // Extract the script from the package.
+ setlinebuf(cmd_pipe_.get());
- const char* package_filename = argv[3];
- MemMapping map;
- if (!map.MapFile(package_filename)) {
- LOG(ERROR) << "failed to map package " << argv[3];
- return 3;
+ if (!mapped_package_.MapFile(std::string(package_filename))) {
+ LOG(ERROR) << "failed to map package " << package_filename;
+ return false;
}
- ZipArchiveHandle za;
- int open_err = OpenArchiveFromMemory(map.addr, map.length, argv[3], &za);
- if (open_err != 0) {
- LOG(ERROR) << "failed to open package " << argv[3] << ": " << ErrorCodeString(open_err);
- CloseArchive(za);
- return 3;
+ if (int open_err = OpenArchiveFromMemory(mapped_package_.addr, mapped_package_.length,
+ std::string(package_filename).c_str(), &package_handle_);
+ open_err != 0) {
+ LOG(ERROR) << "failed to open package " << package_filename << ": "
+ << ErrorCodeString(open_err);
+ return false;
}
-
- ZipString script_name(SCRIPT_NAME);
- ZipEntry script_entry;
- int find_err = FindEntry(za, script_name, &script_entry);
- if (find_err != 0) {
- LOG(ERROR) << "failed to find " << SCRIPT_NAME << " in " << package_filename << ": "
- << ErrorCodeString(find_err);
- CloseArchive(za);
- return 4;
+ if (!ReadEntryToString(package_handle_, SCRIPT_NAME, &updater_script_)) {
+ return false;
}
- std::string script;
- script.resize(script_entry.uncompressed_length);
- int extract_err = ExtractToMemory(za, &script_entry, reinterpret_cast<uint8_t*>(&script[0]),
- script_entry.uncompressed_length);
- if (extract_err != 0) {
- LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err);
- CloseArchive(za);
- return 5;
- }
+ is_retry_ = is_retry;
- // Configure edify's functions.
+ return true;
+}
- RegisterBuiltins();
- RegisterInstallFunctions();
- RegisterBlockImageFunctions();
- RegisterDynamicPartitionsFunctions();
- RegisterDeviceExtensions();
+bool Updater::RunUpdate() {
+ CHECK(runtime_);
// Parse the script.
-
std::unique_ptr<Expr> root;
int error_count = 0;
- int error = ParseString(script, &root, &error_count);
+ int error = ParseString(updater_script_, &root, &error_count);
if (error != 0 || error_count > 0) {
LOG(ERROR) << error_count << " parse errors";
- CloseArchive(za);
- return 6;
- }
-
- sehandle = selinux_android_file_context_handle();
- selinux_android_set_sehandle(sehandle);
-
- if (!sehandle) {
- fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n");
+ return false;
}
// Evaluate the parsed script.
+ State state(updater_script_, this);
+ state.is_retry = is_retry_;
+
+ bool status = Evaluate(&state, root, &result_);
+ if (status) {
+ fprintf(cmd_pipe_.get(), "ui_print script succeeded: result was [%s]\n", result_.c_str());
+ // Even though the script doesn't abort, still log the cause code if result is empty.
+ if (result_.empty() && state.cause_code != kNoCause) {
+ fprintf(cmd_pipe_.get(), "log cause: %d\n", state.cause_code);
+ }
+ for (const auto& func : skipped_functions_) {
+ LOG(WARNING) << "Skipped executing function " << func;
+ }
+ return true;
+ }
- UpdaterInfo updater_info;
- updater_info.cmd_pipe = cmd_pipe;
- updater_info.package_zip = za;
- updater_info.version = atoi(version);
- updater_info.package_zip_addr = map.addr;
- updater_info.package_zip_len = map.length;
+ ParseAndReportErrorCode(&state);
+ return false;
+}
- State state(script, &updater_info);
+void Updater::WriteToCommandPipe(const std::string_view message, bool flush) const {
+ fprintf(cmd_pipe_.get(), "%s\n", std::string(message).c_str());
+ if (flush) {
+ fflush(cmd_pipe_.get());
+ }
+}
- if (argc == 5) {
- if (strcmp(argv[4], "retry") == 0) {
- state.is_retry = true;
- } else {
- printf("unexpected argument: %s", argv[4]);
+void Updater::UiPrint(const std::string_view message) const {
+ // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "".
+ // so skip sending empty strings to ui.
+ std::vector<std::string> lines = android::base::Split(std::string(message), "\n");
+ for (const auto& line : lines) {
+ if (!line.empty()) {
+ fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str());
}
}
- std::string result;
- bool status = Evaluate(&state, root, &result);
-
- if (!status) {
- if (state.errmsg.empty()) {
- LOG(ERROR) << "script aborted (no error message)";
- fprintf(cmd_pipe, "ui_print script aborted (no error message)\n");
- } else {
- LOG(ERROR) << "script aborted: " << state.errmsg;
- const std::vector<std::string> lines = android::base::Split(state.errmsg, "\n");
- for (const std::string& line : lines) {
- // Parse the error code in abort message.
- // Example: "E30: This package is for bullhead devices."
- if (!line.empty() && line[0] == 'E') {
- if (sscanf(line.c_str(), "E%d: ", &state.error_code) != 1) {
- LOG(ERROR) << "Failed to parse error code: [" << line << "]";
- }
+ // on the updater side, we need to dump the contents to stderr (which has
+ // been redirected to the log file). because the recovery will only print
+ // the contents to screen when processing pipe command ui_print.
+ LOG(INFO) << message;
+}
+
+std::string Updater::FindBlockDeviceName(const std::string_view name) const {
+ return runtime_->FindBlockDeviceName(name);
+}
+
+void Updater::ParseAndReportErrorCode(State* state) {
+ CHECK(state);
+ if (state->errmsg.empty()) {
+ LOG(ERROR) << "script aborted (no error message)";
+ fprintf(cmd_pipe_.get(), "ui_print script aborted (no error message)\n");
+ } else {
+ LOG(ERROR) << "script aborted: " << state->errmsg;
+ const std::vector<std::string> lines = android::base::Split(state->errmsg, "\n");
+ for (const std::string& line : lines) {
+ // Parse the error code in abort message.
+ // Example: "E30: This package is for bullhead devices."
+ if (!line.empty() && line[0] == 'E') {
+ if (sscanf(line.c_str(), "E%d: ", &state->error_code) != 1) {
+ LOG(ERROR) << "Failed to parse error code: [" << line << "]";
}
- fprintf(cmd_pipe, "ui_print %s\n", line.c_str());
}
+ fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str());
}
+ }
- // Installation has been aborted. Set the error code to kScriptExecutionFailure unless
- // a more specific code has been set in errmsg.
- if (state.error_code == kNoError) {
- state.error_code = kScriptExecutionFailure;
- }
- fprintf(cmd_pipe, "log error: %d\n", state.error_code);
- // Cause code should provide additional information about the abort.
- if (state.cause_code != kNoCause) {
- fprintf(cmd_pipe, "log cause: %d\n", state.cause_code);
- if (state.cause_code == kPatchApplicationFailure) {
- LOG(INFO) << "Patch application failed, retry update.";
- fprintf(cmd_pipe, "retry_update\n");
- } else if (state.cause_code == kEioFailure) {
- LOG(INFO) << "Update failed due to EIO, retry update.";
- fprintf(cmd_pipe, "retry_update\n");
- }
+ // Installation has been aborted. Set the error code to kScriptExecutionFailure unless
+ // a more specific code has been set in errmsg.
+ if (state->error_code == kNoError) {
+ state->error_code = kScriptExecutionFailure;
+ }
+ fprintf(cmd_pipe_.get(), "log error: %d\n", state->error_code);
+ // Cause code should provide additional information about the abort.
+ if (state->cause_code != kNoCause) {
+ fprintf(cmd_pipe_.get(), "log cause: %d\n", state->cause_code);
+ if (state->cause_code == kPatchApplicationFailure) {
+ LOG(INFO) << "Patch application failed, retry update.";
+ fprintf(cmd_pipe_.get(), "retry_update\n");
+ } else if (state->cause_code == kEioFailure) {
+ LOG(INFO) << "Update failed due to EIO, retry update.";
+ fprintf(cmd_pipe_.get(), "retry_update\n");
}
+ }
+}
- if (updater_info.package_zip) {
- CloseArchive(updater_info.package_zip);
- }
- return 7;
- } else {
- fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]\n", result.c_str());
+bool Updater::ReadEntryToString(ZipArchiveHandle za, const std::string& entry_name,
+ std::string* content) {
+ ZipEntry entry;
+ int find_err = FindEntry(za, entry_name, &entry);
+ if (find_err != 0) {
+ LOG(ERROR) << "failed to find " << entry_name
+ << " in the package: " << ErrorCodeString(find_err);
+ return false;
}
- if (updater_info.package_zip) {
- CloseArchive(updater_info.package_zip);
+ content->resize(entry.uncompressed_length);
+ int extract_err = ExtractToMemory(za, &entry, reinterpret_cast<uint8_t*>(&content->at(0)),
+ entry.uncompressed_length);
+ if (extract_err != 0) {
+ LOG(ERROR) << "failed to read " << entry_name
+ << " from package: " << ErrorCodeString(extract_err);
+ return false;
}
- return 0;
+ return true;
}
diff --git a/updater/updater_main.cpp b/updater/updater_main.cpp
new file mode 100644
index 000000000..055a8ac76
--- /dev/null
+++ b/updater/updater_main.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+
+#include "edify/expr.h"
+#include "updater/blockimg.h"
+#include "updater/dynamic_partitions.h"
+#include "updater/install.h"
+#include "updater/updater.h"
+#include "updater/updater_runtime.h"
+
+// Generated by the makefile, this function defines the
+// RegisterDeviceExtensions() function, which calls all the
+// registration functions for device-specific extensions.
+#include "register.inc"
+
+static void UpdaterLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */,
+ const char* /* tag */, const char* /* file */, unsigned int /* line */,
+ const char* message) {
+ fprintf(stdout, "%s\n", message);
+}
+
+int main(int argc, char** argv) {
+ // Various things log information to stdout or stderr more or less
+ // at random (though we've tried to standardize on stdout). The
+ // log file makes more sense if buffering is turned off so things
+ // appear in the right order.
+ setbuf(stdout, nullptr);
+ setbuf(stderr, nullptr);
+
+ // We don't have logcat yet under recovery. Update logs will always be written to stdout
+ // (which is redirected to recovery.log).
+ android::base::InitLogging(argv, &UpdaterLogger);
+
+ if (argc != 4 && argc != 5) {
+ LOG(ERROR) << "unexpected number of arguments: " << argc;
+ return 1;
+ }
+
+ char* version = argv[1];
+ if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') {
+ // We support version 1, 2, or 3.
+ LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1];
+ return 1;
+ }
+
+ int fd;
+ if (!android::base::ParseInt(argv[2], &fd)) {
+ LOG(ERROR) << "Failed to parse fd in " << argv[2];
+ return 1;
+ }
+
+ std::string package_name = argv[3];
+
+ bool is_retry = false;
+ if (argc == 5) {
+ if (strcmp(argv[4], "retry") == 0) {
+ is_retry = true;
+ } else {
+ LOG(ERROR) << "unexpected argument: " << argv[4];
+ return 1;
+ }
+ }
+
+ // Configure edify's functions.
+ RegisterBuiltins();
+ RegisterInstallFunctions();
+ RegisterBlockImageFunctions();
+ RegisterDynamicPartitionsFunctions();
+ RegisterDeviceExtensions();
+
+ auto sehandle = selinux_android_file_context_handle();
+ selinux_android_set_sehandle(sehandle);
+
+ Updater updater(std::make_unique<UpdaterRuntime>(sehandle));
+ if (!updater.Init(fd, package_name, is_retry)) {
+ return 1;
+ }
+
+ if (!updater.RunUpdate()) {
+ return 1;
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/updater/updater_runtime.cpp b/updater/updater_runtime.cpp
new file mode 100644
index 000000000..761f99975
--- /dev/null
+++ b/updater/updater_runtime.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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 "updater/updater_runtime.h"
+
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/wipe.h>
+#include <selinux/label.h>
+#include <tune2fs.h>
+
+#include "otautil/mounts.h"
+#include "otautil/sysutil.h"
+
+std::string UpdaterRuntime::GetProperty(const std::string_view key,
+ const std::string_view default_value) const {
+ return android::base::GetProperty(std::string(key), std::string(default_value));
+}
+
+std::string UpdaterRuntime::FindBlockDeviceName(const std::string_view name) const {
+ return std::string(name);
+}
+
+int UpdaterRuntime::Mount(const std::string_view location, const std::string_view mount_point,
+ const std::string_view fs_type, const std::string_view mount_options) {
+ std::string mount_point_string(mount_point);
+ char* secontext = nullptr;
+ if (sehandle_) {
+ selabel_lookup(sehandle_, &secontext, mount_point_string.c_str(), 0755);
+ setfscreatecon(secontext);
+ }
+
+ mkdir(mount_point_string.c_str(), 0755);
+
+ if (secontext) {
+ freecon(secontext);
+ setfscreatecon(nullptr);
+ }
+
+ return mount(std::string(location).c_str(), mount_point_string.c_str(),
+ std::string(fs_type).c_str(), MS_NOATIME | MS_NODEV | MS_NODIRATIME,
+ std::string(mount_options).c_str());
+}
+
+bool UpdaterRuntime::IsMounted(const std::string_view mount_point) const {
+ scan_mounted_volumes();
+ MountedVolume* vol = find_mounted_volume_by_mount_point(std::string(mount_point).c_str());
+ return vol != nullptr;
+}
+
+std::pair<bool, int> UpdaterRuntime::Unmount(const std::string_view mount_point) {
+ scan_mounted_volumes();
+ MountedVolume* vol = find_mounted_volume_by_mount_point(std::string(mount_point).c_str());
+ if (vol == nullptr) {
+ return { false, -1 };
+ }
+
+ int ret = unmount_mounted_volume(vol);
+ return { true, ret };
+}
+
+bool UpdaterRuntime::ReadFileToString(const std::string_view filename, std::string* content) const {
+ return android::base::ReadFileToString(std::string(filename), content);
+}
+
+bool UpdaterRuntime::WriteStringToFile(const std::string_view content,
+ const std::string_view filename) const {
+ return android::base::WriteStringToFile(std::string(content), std::string(filename));
+}
+
+int UpdaterRuntime::WipeBlockDevice(const std::string_view filename, size_t len) const {
+ android::base::unique_fd fd(open(std::string(filename).c_str(), O_WRONLY));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << filename;
+ return false;
+ }
+ // The wipe_block_device function in ext4_utils returns 0 on success and 1 for failure.
+ return wipe_block_device(fd, len);
+}
+
+int UpdaterRuntime::RunProgram(const std::vector<std::string>& args, bool is_vfork) const {
+ CHECK(!args.empty());
+ auto argv = StringVectorToNullTerminatedArray(args);
+ LOG(INFO) << "about to run program [" << args[0] << "] with " << argv.size() << " args";
+
+ pid_t child = is_vfork ? vfork() : fork();
+ if (child == 0) {
+ execv(argv[0], argv.data());
+ PLOG(ERROR) << "run_program: execv failed";
+ _exit(EXIT_FAILURE);
+ }
+
+ int status;
+ waitpid(child, &status, 0);
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0) {
+ LOG(ERROR) << "run_program: child exited with status " << WEXITSTATUS(status);
+ }
+ } else if (WIFSIGNALED(status)) {
+ LOG(ERROR) << "run_program: child terminated by signal " << WTERMSIG(status);
+ }
+
+ return status;
+}
+
+int UpdaterRuntime::Tune2Fs(const std::vector<std::string>& args) const {
+ auto tune2fs_args = StringVectorToNullTerminatedArray(args);
+ // tune2fs changes the filesystem parameters on an ext2 filesystem; it returns 0 on success.
+ return tune2fs_main(tune2fs_args.size() - 1, tune2fs_args.data());
+}