summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.bp31
-rw-r--r--CleanSpec.mk18
-rw-r--r--OWNERS4
-rw-r--r--README.md28
-rw-r--r--TEST_MAPPING14
-rw-r--r--applypatch/Android.bp59
-rw-r--r--applypatch/applypatch.cpp18
-rw-r--r--applypatch/applypatch_modes.cpp2
-rw-r--r--applypatch/imgdiff.cpp7
-rw-r--r--applypatch/include/applypatch/applypatch.h7
-rw-r--r--applypatch/vendor_flash_recovery.rc3
-rw-r--r--boot_control/Android.bp37
-rw-r--r--boot_control/boot_control.cpp401
-rw-r--r--bootloader_message/Android.bp13
-rw-r--r--bootloader_message/bootloader_message.cpp74
-rw-r--r--bootloader_message/include/bootloader_message/bootloader_message.h137
-rw-r--r--common.h38
-rw-r--r--edify/Android.bp2
-rw-r--r--edify/expr.cpp4
-rw-r--r--edify/include/edify/expr.h9
-rw-r--r--edify/include/edify/updater_interface.h48
-rw-r--r--edify/include/edify/updater_runtime_interface.h74
-rw-r--r--edify/parser.yy2
-rw-r--r--etc/init.rc19
-rw-r--r--fastboot/fastboot.cpp4
-rw-r--r--fsck_unshare_blocks.cpp151
-rw-r--r--fuse_sideload/Android.bp4
-rw-r--r--fuse_sideload/fuse_provider.cpp84
-rw-r--r--fuse_sideload/include/fuse_provider.h41
-rw-r--r--install/Android.bp17
-rw-r--r--install/adb_install.cpp16
-rw-r--r--install/fuse_install.cpp (renamed from install/fuse_sdcard_install.cpp)86
-rw-r--r--install/include/install/adb_install.h11
-rw-r--r--install/include/install/fuse_install.h30
-rw-r--r--install/include/install/install.h26
-rw-r--r--install/include/install/package.h9
-rw-r--r--install/include/install/snapshot_utils.h30
-rw-r--r--install/include/install/wipe_device.h (renamed from install/include/install/fuse_sdcard_install.h)11
-rw-r--r--install/include/private/setup_commands.h8
-rw-r--r--install/install.cpp241
-rw-r--r--install/package.cpp18
-rw-r--r--install/snapshot_utils.cpp74
-rw-r--r--install/verifier.cpp10
-rw-r--r--install/wipe_data.cpp13
-rw-r--r--install/wipe_device.cpp197
-rw-r--r--minadbd/Android.bp26
-rw-r--r--minadbd/README.md32
-rw-r--r--minadbd/fuse_adb_provider.h4
-rw-r--r--minadbd/include/minadbd/types.h (renamed from minadbd/minadbd_types.h)0
-rw-r--r--minadbd/minadbd.cpp2
-rw-r--r--minadbd/minadbd_services.cpp31
-rw-r--r--minadbd/minadbd_services_test.cpp2
-rw-r--r--minui/events.cpp126
-rw-r--r--minui/graphics.cpp4
-rw-r--r--minui/graphics_drm.cpp4
-rw-r--r--minui/include/minui/minui.h1
-rw-r--r--minui/resources.cpp24
-rw-r--r--misc_writer/Android.bp40
-rw-r--r--misc_writer/misc_writer.cpp106
-rw-r--r--otautil/Android.bp34
-rw-r--r--otautil/include/otautil/boot_state.h (renamed from fsck_unshare_blocks.h)24
-rw-r--r--otautil/include/otautil/rangeset.h7
-rw-r--r--otautil/include/otautil/sysutil.h15
-rw-r--r--otautil/rangeset.cpp52
-rw-r--r--otautil/sysutil.cpp20
-rw-r--r--recovery-persist.cpp22
-rw-r--r--recovery-refresh.cpp3
-rw-r--r--recovery.cpp489
-rw-r--r--recovery_main.cpp95
-rw-r--r--recovery_ui/Android.bp1
-rw-r--r--recovery_ui/device.cpp13
-rw-r--r--recovery_ui/include/recovery_ui/device.h12
-rw-r--r--recovery_ui/include/recovery_ui/screen_ui.h3
-rw-r--r--recovery_ui/include/recovery_ui/stub_ui.h6
-rw-r--r--recovery_ui/include/recovery_ui/ui.h21
-rw-r--r--recovery_ui/screen_ui.cpp46
-rw-r--r--recovery_ui/stub_ui.cpp36
-rw-r--r--recovery_ui/ui.cpp24
-rw-r--r--recovery_utils/Android.bp81
-rw-r--r--recovery_utils/battery_utils.cpp89
-rw-r--r--recovery_utils/include/recovery_utils/battery_utils.h33
-rw-r--r--recovery_utils/include/recovery_utils/logging.h (renamed from otautil/include/otautil/logging.h)2
-rw-r--r--recovery_utils/include/recovery_utils/parse_install_logs.h (renamed from otautil/include/otautil/parse_install_logs.h)0
-rw-r--r--recovery_utils/include/recovery_utils/roots.h (renamed from otautil/include/otautil/roots.h)5
-rw-r--r--recovery_utils/include/recovery_utils/thermalutil.h (renamed from otautil/include/otautil/thermalutil.h)0
-rw-r--r--recovery_utils/logging.cpp (renamed from otautil/logging.cpp)21
-rw-r--r--recovery_utils/parse_install_logs.cpp (renamed from otautil/parse_install_logs.cpp)2
-rw-r--r--recovery_utils/roots.cpp (renamed from otautil/roots.cpp)60
-rw-r--r--recovery_utils/thermalutil.cpp (renamed from otautil/thermalutil.cpp)2
-rw-r--r--tests/Android.bp81
-rw-r--r--tests/AndroidTest.xml33
-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/applypatch_test.cpp4
-rw-r--r--tests/unit/battery_utils_test.cpp27
-rw-r--r--tests/unit/bootloader_message_test.cpp (renamed from tests/component/bootloader_message_test.cpp)34
-rw-r--r--tests/unit/edify_test.cpp (renamed from tests/component/edify_test.cpp)0
-rw-r--r--tests/unit/fuse_provider_test.cpp104
-rw-r--r--tests/unit/fuse_sideload_test.cpp (renamed from tests/component/sideload_test.cpp)4
-rw-r--r--tests/unit/host/imgdiff_test.cpp (renamed from tests/component/imgdiff_test.cpp)0
-rw-r--r--tests/unit/host/update_simulator_test.cpp403
-rw-r--r--tests/unit/install_test.cpp (renamed from tests/component/install_test.cpp)142
-rw-r--r--tests/unit/locale_test.cpp4
-rw-r--r--tests/unit/package_test.cpp5
-rw-r--r--tests/unit/parse_install_logs_test.cpp2
-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/image_generator/ImageGenerator.java2
-rw-r--r--tools/recovery_l10n/res/values-ar/strings.xml4
-rw-r--r--tools/recovery_l10n/res/values-hy/strings.xml2
-rw-r--r--update_verifier/update_verifier.cpp4
-rw-r--r--updater/Android.bp124
-rw-r--r--updater/Android.mk51
-rw-r--r--updater/blockimg.cpp116
-rw-r--r--updater/build_info.cpp139
-rw-r--r--updater/dynamic_partitions.cpp313
-rw-r--r--updater/include/updater/build_info.h74
-rw-r--r--updater/include/updater/install.h11
-rw-r--r--updater/include/updater/simulator_runtime.h62
-rw-r--r--updater/include/updater/target_files.h71
-rw-r--r--updater/include/updater/updater.h85
-rw-r--r--updater/include/updater/updater_runtime.h62
-rw-r--r--updater/install.cpp220
-rw-r--r--updater/mounts.cpp (renamed from otautil/mounts.cpp)2
-rw-r--r--updater/mounts.h (renamed from otautil/include/otautil/mounts.h)0
-rw-r--r--updater/simulator_runtime.cpp132
-rw-r--r--updater/target_files.cpp287
-rw-r--r--updater/update_simulator_main.cpp167
-rw-r--r--updater/updater.cpp278
-rw-r--r--updater/updater_main.cpp116
-rw-r--r--updater/updater_runtime.cpp132
-rw-r--r--updater/updater_runtime_dynamic_partitions.cpp343
-rw-r--r--updater_sample/Android.bp1
-rw-r--r--updater_sample/AndroidManifest.xml1
-rw-r--r--updater_sample/OWNERS2
-rw-r--r--updater_sample/README.md6
143 files changed, 4899 insertions, 2937 deletions
diff --git a/Android.bp b/Android.bp
index f367e5e7d..8e29dc8fd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -58,13 +58,16 @@ cc_defaults {
],
shared_libs: [
- "android.hardware.health@2.0",
+ "android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
"libbase",
"libbootloader_message",
"libcrypto",
"libcutils",
"libfs_mgr",
+ "liblp",
"liblog",
+ "libprotobuf-cpp-lite",
"libziparchive",
],
@@ -73,11 +76,9 @@ cc_defaults {
"libinstall",
"librecovery_fastboot",
"libminui",
+ "librecovery_utils",
"libotautil",
-
- // external dependencies
- "libhealthhalutils",
- "libfstab",
+ "libsnapshot_nobinder",
],
}
@@ -90,16 +91,22 @@ cc_library_static {
],
srcs: [
- "fsck_unshare_blocks.cpp",
"recovery.cpp",
],
shared_libs: [
- "libfusesideload",
"librecovery_ui",
],
}
+prebuilt_etc {
+ name: "init_recovery.rc",
+ filename: "init.rc",
+ src: "etc/init.rc",
+ sub_dir: "init/hw",
+ recovery: true,
+}
+
cc_binary {
name: "recovery",
recovery: true,
@@ -107,6 +114,7 @@ cc_binary {
defaults: [
"libinstall_defaults",
"librecovery_defaults",
+ "librecovery_utils_defaults",
],
srcs: [
@@ -124,11 +132,13 @@ cc_binary {
required: [
"e2fsdroid.recovery",
+ "init_recovery.rc",
"librecovery_ui_ext",
"minadbd",
"mke2fs.conf.recovery",
"mke2fs.recovery",
"recovery_deps",
+ "ueventd.rc.recovery",
],
}
@@ -147,12 +157,10 @@ cc_binary {
shared_libs: [
"libbase",
"liblog",
- "libmetricslogger",
],
static_libs: [
- "libotautil",
- "libfstab",
+ "librecovery_utils",
],
init_rc: [
@@ -178,8 +186,7 @@ cc_binary {
],
static_libs: [
- "libotautil",
- "libfstab",
+ "librecovery_utils",
],
init_rc: [
diff --git a/CleanSpec.mk b/CleanSpec.mk
index a7ab0d9be..d4e9e437b 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -51,6 +51,24 @@ $(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)
+
+$(call add-clean-step, find $(OUT_DIR) -type f -name "SystemUpdaterSample*" -print0 | xargs -0 rm -f)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUpdaterSample)
+
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libbrotli.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libbz.so)
+
+# Move recovery resources from /system to /vendor.
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/applypatch)
+$(call add-clean-step, rm -r $(PRODUCT_OUT)/symbols/system/bin/applypatch)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/bin/applypatch)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/bin/install-recovery.sh)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/etc/recovery-resource.dat)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/recovery-from-boot.p)
+
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/OWNERS b/OWNERS
index b3f11dcd6..79dd9f776 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,5 @@
+elsk@google.com
enh@google.com
-tbao@google.com
+nhdo@google.com
xunchang@google.com
+zhaojiac@google.com
diff --git a/README.md b/README.md
index efcd318b5..bd1cf7dea 100644
--- a/README.md
+++ b/README.md
@@ -4,29 +4,41 @@ The Recovery Image
Quick turn-around testing
-------------------------
- mm -j && m ramdisk-nodeps && m recoveryimage-nodeps
+* Devices using recovery-as-boot (e.g. Pixels, which set BOARD\_USES\_RECOVERY\_AS\_BOOT)
- # To boot into the new recovery image
- # without flashing the recovery partition:
- adb reboot bootloader
- fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
+ # After setting up environment and lunch.
+ m -j bootimage
+ adb reboot bootloader
+
+ # Pixel devices don't support booting into recovery mode with `fastboot boot`.
+ fastboot flash boot
+
+ # Manually choose `Recovery mode` from bootloader menu.
+
+* Devices with a separate recovery image (e.g. Nexus)
+
+ # After setting up environment and lunch.
+ mm -j && m ramdisk-nodeps && m recoveryimage-nodeps
+ adb reboot bootloader
+
+ # To boot into the new recovery image without flashing the recovery partition:
+ fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
Running the tests
-----------------
+
# After setting up environment and lunch.
mmma -j bootable/recovery
- # Running the tests on device.
+ # Running the tests on device (under normal boot).
adb root
adb sync data
# 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
new file mode 100644
index 000000000..a3045828e
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,14 @@
+{
+ "presubmit": [
+ {
+ "name": "minadbd_test"
+ },
+ {
+ "name": "recovery_unit_test"
+ },
+ {
+ "name": "recovery_host_test",
+ "host": true
+ }
+ ]
+}
diff --git a/applypatch/Android.bp b/applypatch/Android.bp
index 620ca6cc9..13a962584 100644
--- a/applypatch/Android.bp
+++ b/applypatch/Android.bp
@@ -31,6 +31,7 @@ cc_library_static {
name: "libapplypatch",
host_supported: true,
+ vendor_available: true,
defaults: [
"applypatch_defaults",
@@ -51,12 +52,15 @@ cc_library_static {
"libbase",
"libbspatch",
"libbz",
- "libcrypto",
"libedify",
"libotautil",
"libz",
],
+ shared_libs: [
+ "libcrypto",
+ ],
+
target: {
darwin: {
enabled: false,
@@ -66,6 +70,7 @@ cc_library_static {
cc_library_static {
name: "libapplypatch_modes",
+ vendor_available: true,
defaults: [
"applypatch_defaults",
@@ -78,14 +83,18 @@ cc_library_static {
static_libs: [
"libapplypatch",
"libbase",
- "libcrypto",
"libedify",
"libotautil",
],
+
+ shared_libs: [
+ "libcrypto",
+ ],
}
cc_binary {
name: "applypatch",
+ vendor: true,
defaults: [
"applypatch_defaults",
@@ -100,25 +109,29 @@ cc_binary {
"libapplypatch",
"libedify",
"libotautil",
+
+ // External dependencies.
"libbspatch",
+ "libbrotli",
+ "libbz",
],
shared_libs: [
"libbase",
- "libbrotli",
- "libbz",
"libcrypto",
"liblog",
"libz",
"libziparchive",
],
+
+ init_rc: [
+ "vendor_flash_recovery.rc",
+ ],
}
-cc_library_static {
+cc_library_host_static {
name: "libimgdiff",
- host_supported: true,
-
defaults: [
"applypatch_defaults",
],
@@ -170,35 +183,3 @@ cc_binary_host {
"libz",
],
}
-
-cc_library_static {
- name: "libimgpatch",
-
- // The host module is for recovery_host_test (Linux only).
- host_supported: true,
-
- defaults: [
- "applypatch_defaults",
- ],
-
- srcs: [
- "bspatch.cpp",
- "imgpatch.cpp",
- ],
-
- static_libs: [
- "libbase",
- "libbspatch",
- "libbz",
- "libcrypto",
- "libedify",
- "libotautil",
- "libz",
- ],
-
- target: {
- darwin: {
- enabled: false,
- },
- },
-}
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index 90d8e8604..adda6976d 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -47,7 +47,7 @@
using namespace std::string_literals;
static bool GenerateTarget(const Partition& target, const FileContents& source_file,
- const Value& patch, const Value* bonus_data);
+ const Value& patch, const Value* bonus_data, bool backup_source);
bool LoadFileContents(const std::string& filename, FileContents* file) {
// No longer allow loading contents from eMMC partitions.
@@ -266,7 +266,7 @@ int ShowLicenses() {
}
bool PatchPartition(const Partition& target, const Partition& source, const Value& patch,
- const Value* bonus) {
+ const Value* bonus, bool backup_source) {
LOG(INFO) << "Patching " << target.name;
// We try to load and check against the target hash first.
@@ -279,8 +279,8 @@ bool PatchPartition(const Partition& target, const Partition& source, const Valu
}
FileContents source_file;
- if (ReadPartitionToBuffer(source, &source_file, true)) {
- return GenerateTarget(target, source_file, patch, bonus);
+ if (ReadPartitionToBuffer(source, &source_file, backup_source)) {
+ return GenerateTarget(target, source_file, patch, bonus, backup_source);
}
LOG(ERROR) << "Failed to find any match";
@@ -326,7 +326,7 @@ bool FlashPartition(const Partition& partition, const std::string& source_filena
}
static bool GenerateTarget(const Partition& target, const FileContents& source_file,
- const Value& patch, const Value* bonus_data) {
+ const Value& patch, const Value* bonus_data, bool backup_source) {
uint8_t expected_sha1[SHA_DIGEST_LENGTH];
if (ParseSha1(target.hash, expected_sha1) != 0) {
LOG(ERROR) << "Failed to parse target hash \"" << target.hash << "\"";
@@ -351,11 +351,11 @@ static bool GenerateTarget(const Partition& target, const FileContents& source_f
}
// We write the original source to cache, in case the partition write is interrupted.
- if (!CheckAndFreeSpaceOnCache(source_file.data.size())) {
+ if (backup_source && !CheckAndFreeSpaceOnCache(source_file.data.size())) {
LOG(ERROR) << "Not enough free space on /cache";
return false;
}
- if (!SaveFileContents(Paths::Get().cache_temp_source(), &source_file)) {
+ if (backup_source && !SaveFileContents(Paths::Get().cache_temp_source(), &source_file)) {
LOG(ERROR) << "Failed to back up source file";
return false;
}
@@ -415,7 +415,9 @@ static bool GenerateTarget(const Partition& target, const FileContents& source_f
}
// Delete the backup copy of the source.
- unlink(Paths::Get().cache_temp_source().c_str());
+ if (backup_source) {
+ unlink(Paths::Get().cache_temp_source().c_str());
+ }
// Success!
return true;
diff --git a/applypatch/applypatch_modes.cpp b/applypatch/applypatch_modes.cpp
index b46659808..bb5eeae9d 100644
--- a/applypatch/applypatch_modes.cpp
+++ b/applypatch/applypatch_modes.cpp
@@ -87,7 +87,7 @@ static int PatchMode(const std::string& target_emmc, const std::string& source_e
bonus = std::make_unique<Value>(Value::Type::BLOB, std::move(bonus_contents));
}
- return PatchPartition(target, source, patch, bonus.get()) ? 0 : 1;
+ return PatchPartition(target, source, patch, bonus.get(), false) ? 0 : 1;
}
static void Usage() {
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/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h
index 6fc6f0fc9..799f4b2d7 100644
--- a/applypatch/include/applypatch/applypatch.h
+++ b/applypatch/include/applypatch/applypatch.h
@@ -73,10 +73,11 @@ std::ostream& operator<<(std::ostream& os, const Partition& partition);
// the 'target' Partition. While patching, it will backup the data on the source partition to
// /cache, so that the patching could be resumed on interruption even if both of the source and
// target partitions refer to the same device. The function is idempotent if called multiple times.
-// An optional arg 'bonus' can be provided, if the patch was generated with a bonus output.
-// Returns the patching result.
+// 'bonus' can be provided if the patch was generated with a bonus output, or nullptr.
+// 'backup_source' indicates whether the source partition should be backed up prior to the update
+// (e.g. when doing in-place update). Returns the patching result.
bool PatchPartition(const Partition& target, const Partition& source, const Value& patch,
- const Value* bonus);
+ const Value* bonus, bool backup_source);
// Returns whether the contents of the eMMC target or the cached file match the embedded hash.
// It will look for the backup on /cache if the given partition doesn't match the checksum.
diff --git a/applypatch/vendor_flash_recovery.rc b/applypatch/vendor_flash_recovery.rc
new file mode 100644
index 000000000..37a7c2be7
--- /dev/null
+++ b/applypatch/vendor_flash_recovery.rc
@@ -0,0 +1,3 @@
+service vendor_flash_recovery /vendor/bin/install-recovery.sh
+ class main
+ oneshot
diff --git a/boot_control/Android.bp b/boot_control/Android.bp
deleted file mode 100644
index 7720ead50..000000000
--- a/boot_control/Android.bp
+++ /dev/null
@@ -1,37 +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.
-//
-
-cc_library_shared {
- name: "bootctrl.default",
- recovery_available: true,
- relative_install_path: "hw",
-
- srcs: ["boot_control.cpp"],
-
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- "-Werror",
- "-Wall",
- "-Wextra",
- ],
-
- shared_libs: [
- "libbase",
- "libbootloader_message",
- "libfs_mgr",
- "liblog",
- ],
-}
diff --git a/boot_control/boot_control.cpp b/boot_control/boot_control.cpp
deleted file mode 100644
index ec97b6ced..000000000
--- a/boot_control/boot_control.cpp
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
- * Copyright (C) 2015 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 <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <hardware/boot_control.h>
-#include <hardware/hardware.h>
-
-#include <bootloader_message/bootloader_message.h>
-
-struct boot_control_private_t {
- // The base struct needs to be first in the list.
- boot_control_module_t base;
-
- // Whether this struct was initialized with data from the bootloader message
- // that doesn't change until next reboot.
- bool initialized;
-
- // The path to the misc_device as reported in the fstab.
- const char* misc_device;
-
- // The number of slots present on the device.
- unsigned int num_slots;
-
- // The slot where we are running from.
- unsigned int current_slot;
-};
-
-namespace {
-
-// The number of boot attempts that should be made from a new slot before
-// rolling back to the previous slot.
-constexpr unsigned int kDefaultBootAttempts = 7;
-static_assert(kDefaultBootAttempts < 8, "tries_remaining field only has 3 bits");
-
-constexpr unsigned int kMaxNumSlots =
- sizeof(bootloader_control::slot_info) / sizeof(bootloader_control::slot_info[0]);
-constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" };
-constexpr off_t kBootloaderControlOffset = offsetof(bootloader_message_ab, slot_suffix);
-
-static uint32_t CRC32(const uint8_t* buf, size_t size) {
- static uint32_t crc_table[256];
-
- // Compute the CRC-32 table only once.
- if (!crc_table[1]) {
- for (uint32_t i = 0; i < 256; ++i) {
- uint32_t crc = i;
- for (uint32_t j = 0; j < 8; ++j) {
- uint32_t mask = -(crc & 1);
- crc = (crc >> 1) ^ (0xEDB88320 & mask);
- }
- crc_table[i] = crc;
- }
- }
-
- uint32_t ret = -1;
- for (size_t i = 0; i < size; ++i) {
- ret = (ret >> 8) ^ crc_table[(ret ^ buf[i]) & 0xFF];
- }
-
- return ~ret;
-}
-
-// Return the little-endian representation of the CRC-32 of the first fields
-// in |boot_ctrl| up to the crc32_le field.
-uint32_t BootloaderControlLECRC(const bootloader_control* boot_ctrl) {
- return htole32(
- CRC32(reinterpret_cast<const uint8_t*>(boot_ctrl), offsetof(bootloader_control, crc32_le)));
-}
-
-bool LoadBootloaderControl(const char* misc_device, bootloader_control* buffer) {
- android::base::unique_fd fd(open(misc_device, O_RDONLY));
- if (fd.get() == -1) {
- PLOG(ERROR) << "failed to open " << misc_device;
- return false;
- }
- if (lseek(fd, kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
- PLOG(ERROR) << "failed to lseek " << misc_device;
- return false;
- }
- if (!android::base::ReadFully(fd.get(), buffer, sizeof(bootloader_control))) {
- PLOG(ERROR) << "failed to read " << misc_device;
- return false;
- }
- return true;
-}
-
-bool UpdateAndSaveBootloaderControl(const char* misc_device, bootloader_control* buffer) {
- buffer->crc32_le = BootloaderControlLECRC(buffer);
- android::base::unique_fd fd(open(misc_device, O_WRONLY | O_SYNC));
- if (fd.get() == -1) {
- PLOG(ERROR) << "failed to open " << misc_device;
- return false;
- }
- if (lseek(fd.get(), kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
- PLOG(ERROR) << "failed to lseek " << misc_device;
- return false;
- }
- if (!android::base::WriteFully(fd.get(), buffer, sizeof(bootloader_control))) {
- PLOG(ERROR) << "failed to write " << misc_device;
- return false;
- }
- return true;
-}
-
-void InitDefaultBootloaderControl(const boot_control_private_t* module,
- bootloader_control* boot_ctrl) {
- memset(boot_ctrl, 0, sizeof(*boot_ctrl));
-
- if (module->current_slot < kMaxNumSlots) {
- strlcpy(boot_ctrl->slot_suffix, kSlotSuffixes[module->current_slot],
- sizeof(boot_ctrl->slot_suffix));
- }
- boot_ctrl->magic = BOOT_CTRL_MAGIC;
- boot_ctrl->version = BOOT_CTRL_VERSION;
-
- // Figure out the number of slots by checking if the partitions exist,
- // otherwise assume the maximum supported by the header.
- boot_ctrl->nb_slot = kMaxNumSlots;
- std::string base_path = module->misc_device;
- size_t last_path_sep = base_path.rfind('/');
- if (last_path_sep != std::string::npos) {
- // We test the existence of the "boot" partition on each possible slot,
- // which is a partition required by Android Bootloader Requirements.
- base_path = base_path.substr(0, last_path_sep + 1) + "boot";
- int last_existing_slot = -1;
- int first_missing_slot = -1;
- for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
- std::string partition_path = base_path + kSlotSuffixes[slot];
- struct stat part_stat;
- int err = stat(partition_path.c_str(), &part_stat);
- if (!err) {
- last_existing_slot = slot;
- LOG(INFO) << "Found slot: " << kSlotSuffixes[slot];
- } else if (err < 0 && errno == ENOENT && first_missing_slot == -1) {
- first_missing_slot = slot;
- }
- }
- // We only declare that we found the actual number of slots if we found all
- // the boot partitions up to the number of slots, and no boot partition
- // after that. Not finding any of the boot partitions implies a problem so
- // we just leave the number of slots in the maximum value.
- if ((last_existing_slot != -1 && last_existing_slot + 1 == first_missing_slot) ||
- (first_missing_slot == -1 && last_existing_slot + 1 == kMaxNumSlots)) {
- boot_ctrl->nb_slot = last_existing_slot + 1;
- LOG(INFO) << "Found a system with " << last_existing_slot + 1 << " slots.";
- }
- }
-
- for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
- slot_metadata entry = {};
-
- if (slot < boot_ctrl->nb_slot) {
- entry.priority = 7;
- entry.tries_remaining = kDefaultBootAttempts;
- entry.successful_boot = 0;
- } else {
- entry.priority = 0; // Unbootable
- }
-
- // When the boot_control stored on disk is invalid, we assume that the
- // current slot is successful. The bootloader should repair this situation
- // before booting and write a valid boot_control slot, so if we reach this
- // stage it means that the misc partition was corrupted since boot.
- if (module->current_slot == slot) {
- entry.successful_boot = 1;
- }
-
- boot_ctrl->slot_info[slot] = entry;
- }
- boot_ctrl->recovery_tries_remaining = 0;
-
- boot_ctrl->crc32_le = BootloaderControlLECRC(boot_ctrl);
-}
-
-// Return the index of the slot suffix passed or -1 if not a valid slot suffix.
-int SlotSuffixToIndex(const char* suffix) {
- for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
- if (!strcmp(kSlotSuffixes[slot], suffix)) return slot;
- }
- return -1;
-}
-
-// Initialize the boot_control_private struct with the information from
-// the bootloader_message buffer stored in |boot_ctrl|. Returns whether the
-// initialization succeeded.
-bool BootControl_lazyInitialization(boot_control_private_t* module) {
- if (module->initialized) return true;
-
- // Initialize the current_slot from the read-only property. If the property
- // was not set (from either the command line or the device tree), we can later
- // initialize it from the bootloader_control struct.
- std::string suffix_prop = android::base::GetProperty("ro.boot.slot_suffix", "");
- module->current_slot = SlotSuffixToIndex(suffix_prop.c_str());
-
- std::string err;
- std::string device = get_bootloader_message_blk_device(&err);
- if (device.empty()) return false;
-
- bootloader_control boot_ctrl;
- if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) return false;
-
- // Note that since there isn't a module unload function this memory is leaked.
- module->misc_device = strdup(device.c_str());
- module->initialized = true;
-
- // Validate the loaded data, otherwise we will destroy it and re-initialize it
- // with the current information.
- uint32_t computed_crc32 = BootloaderControlLECRC(&boot_ctrl);
- if (boot_ctrl.crc32_le != computed_crc32) {
- LOG(WARNING) << "Invalid boot control found, expected CRC-32 0x" << std::hex << computed_crc32
- << " but found 0x" << std::hex << boot_ctrl.crc32_le << ". Re-initializing.";
- InitDefaultBootloaderControl(module, &boot_ctrl);
- UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl);
- }
-
- module->num_slots = boot_ctrl.nb_slot;
- return true;
-}
-
-void BootControl_init(boot_control_module_t* module) {
- BootControl_lazyInitialization(reinterpret_cast<boot_control_private_t*>(module));
-}
-
-unsigned int BootControl_getNumberSlots(boot_control_module_t* module) {
- return reinterpret_cast<boot_control_private_t*>(module)->num_slots;
-}
-
-unsigned int BootControl_getCurrentSlot(boot_control_module_t* module) {
- return reinterpret_cast<boot_control_private_t*>(module)->current_slot;
-}
-
-int BootControl_markBootSuccessful(boot_control_module_t* module) {
- boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
-
- bootloader_control bootctrl;
- if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
-
- bootctrl.slot_info[bootctrl_module->current_slot].successful_boot = 1;
- // tries_remaining == 0 means that the slot is not bootable anymore, make
- // sure we mark the current slot as bootable if it succeeds in the last
- // attempt.
- bootctrl.slot_info[bootctrl_module->current_slot].tries_remaining = 1;
- if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
- return 0;
-}
-
-int BootControl_setActiveBootSlot(boot_control_module_t* module, unsigned int slot) {
- boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
-
- if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
- // Invalid slot number.
- return -1;
- }
-
- bootloader_control bootctrl;
- if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
-
- // Set every other slot with a lower priority than the new "active" slot.
- const unsigned int kActivePriority = 15;
- const unsigned int kActiveTries = 6;
- for (unsigned int i = 0; i < bootctrl_module->num_slots; ++i) {
- if (i != slot) {
- if (bootctrl.slot_info[i].priority >= kActivePriority)
- bootctrl.slot_info[i].priority = kActivePriority - 1;
- }
- }
-
- // Note that setting a slot as active doesn't change the successful bit.
- // The successful bit will only be changed by setSlotAsUnbootable().
- bootctrl.slot_info[slot].priority = kActivePriority;
- bootctrl.slot_info[slot].tries_remaining = kActiveTries;
-
- // Setting the current slot as active is a way to revert the operation that
- // set *another* slot as active at the end of an updater. This is commonly
- // used to cancel the pending update. We should only reset the verity_corrpted
- // bit when attempting a new slot, otherwise the verity bit on the current
- // slot would be flip.
- if (slot != bootctrl_module->current_slot) bootctrl.slot_info[slot].verity_corrupted = 0;
-
- if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
- return 0;
-}
-
-int BootControl_setSlotAsUnbootable(struct boot_control_module* module, unsigned int slot) {
- boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
-
- if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
- // Invalid slot number.
- return -1;
- }
-
- bootloader_control bootctrl;
- if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
-
- // The only way to mark a slot as unbootable, regardless of the priority is to
- // set the tries_remaining to 0.
- bootctrl.slot_info[slot].successful_boot = 0;
- bootctrl.slot_info[slot].tries_remaining = 0;
- if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
- return 0;
-}
-
-int BootControl_isSlotBootable(struct boot_control_module* module, unsigned int slot) {
- boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
-
- if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
- // Invalid slot number.
- return -1;
- }
-
- bootloader_control bootctrl;
- if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
-
- return bootctrl.slot_info[slot].tries_remaining;
-}
-
-int BootControl_isSlotMarkedSuccessful(struct boot_control_module* module, unsigned int slot) {
- boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
-
- if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
- // Invalid slot number.
- return -1;
- }
-
- bootloader_control bootctrl;
- if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
-
- return bootctrl.slot_info[slot].successful_boot && bootctrl.slot_info[slot].tries_remaining;
-}
-
-const char* BootControl_getSuffix(boot_control_module_t* module, unsigned int slot) {
- if (slot >= kMaxNumSlots || slot >= reinterpret_cast<boot_control_private_t*>(module)->num_slots) {
- return NULL;
- }
- return kSlotSuffixes[slot];
-}
-
-static int BootControl_open(const hw_module_t* module __unused, const char* id __unused,
- hw_device_t** device __unused) {
- /* Nothing to do currently. */
- return 0;
-}
-
-struct hw_module_methods_t BootControl_methods = {
- .open = BootControl_open,
-};
-
-} // namespace
-
-boot_control_private_t HAL_MODULE_INFO_SYM = {
- .base =
- {
- .common =
- {
- .tag = HARDWARE_MODULE_TAG,
- .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1,
- .hal_api_version = HARDWARE_HAL_API_VERSION,
- .id = BOOT_CONTROL_HARDWARE_MODULE_ID,
- .name = "AOSP reference bootctrl HAL",
- .author = "The Android Open Source Project",
- .methods = &BootControl_methods,
- },
- .init = BootControl_init,
- .getNumberSlots = BootControl_getNumberSlots,
- .getCurrentSlot = BootControl_getCurrentSlot,
- .markBootSuccessful = BootControl_markBootSuccessful,
- .setActiveBootSlot = BootControl_setActiveBootSlot,
- .setSlotAsUnbootable = BootControl_setSlotAsUnbootable,
- .isSlotBootable = BootControl_isSlotBootable,
- .getSuffix = BootControl_getSuffix,
- .isSlotMarkedSuccessful = BootControl_isSlotMarkedSuccessful,
- },
- .initialized = false,
- .misc_device = nullptr,
- .num_slots = 0,
- .current_slot = 0,
-};
diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp
index 450dad08b..6443a077c 100644
--- a/bootloader_message/Android.bp
+++ b/bootloader_message/Android.bp
@@ -36,6 +36,18 @@ cc_library {
"libbootloader_message_defaults",
],
recovery_available: true,
+ host_supported: true,
+
+ target: {
+ host: {
+ shared_libs: [
+ "libcutils", // for strlcpy
+ ],
+ },
+ darwin: {
+ enabled: false,
+ },
+ }
}
cc_library_static {
@@ -44,4 +56,5 @@ cc_library_static {
"libbootloader_message_defaults",
],
vendor: true,
+ recovery_available: true,
}
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index c1ebeaa82..b70d54e5c 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -20,6 +20,7 @@
#include <fcntl.h>
#include <string.h>
+#include <optional>
#include <string>
#include <string_view>
#include <vector>
@@ -30,19 +31,23 @@
#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;
+static std::optional<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;
+std::string get_misc_blk_device(std::string* err) {
+ if (g_misc_device_for_test.has_value() && !g_misc_device_for_test->empty()) {
+ return *g_misc_device_for_test;
}
Fstab fstab;
if (!ReadDefaultFstab(&fstab)) {
@@ -106,8 +111,8 @@ static bool read_misc_partition(void* p, size_t size, const std::string& misc_bl
return true;
}
-static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
- size_t offset, std::string* err) {
+bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
+ size_t offset, std::string* err) {
android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
if (fd == -1) {
*err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
@@ -179,6 +184,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)) {
@@ -197,13 +210,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;
}
@@ -235,41 +250,60 @@ bool write_wipe_package(const std::string& package_data, std::string* err) {
if (misc_blk_device.empty()) {
return false;
}
+ static constexpr size_t kMaximumWipePackageSize =
+ SYSTEM_SPACE_OFFSET_IN_MISC - WIPE_PACKAGE_OFFSET_IN_MISC;
+ if (package_data.size() > kMaximumWipePackageSize) {
+ *err = "Wipe package size " + std::to_string(package_data.size()) + " exceeds " +
+ std::to_string(kMaximumWipePackageSize) + " bytes";
+ return false;
+ }
return write_misc_partition(package_data.data(), package_data.size(), misc_blk_device,
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;
+static bool ValidateSystemSpaceRegion(size_t offset, size_t size, std::string* err) {
+ if (size <= SYSTEM_SPACE_SIZE_IN_MISC && offset <= (SYSTEM_SPACE_SIZE_IN_MISC - size)) {
+ return true;
+ }
+ *err = android::base::StringPrintf("Out of bound access (offset %zu size %zu)", offset, size);
+ return false;
}
-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);
+static bool ReadMiscPartitionSystemSpace(void* data, size_t size, size_t offset, std::string* err) {
+ if (!ValidateSystemSpaceRegion(offset, size, err)) {
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,
+ return read_misc_partition(data, size, misc_blk_device, SYSTEM_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);
+static bool WriteMiscPartitionSystemSpace(const void* data, size_t size, size_t offset,
+ std::string* err) {
+ if (!ValidateSystemSpaceRegion(offset, size, err)) {
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,
+ return write_misc_partition(data, size, misc_blk_device, SYSTEM_SPACE_OFFSET_IN_MISC + offset,
err);
}
+bool ReadMiscVirtualAbMessage(misc_virtual_ab_message* message, std::string* err) {
+ return ReadMiscPartitionSystemSpace(message, sizeof(*message),
+ offsetof(misc_system_space_layout, virtual_ab_message), err);
+}
+
+bool WriteMiscVirtualAbMessage(const misc_virtual_ab_message& message, std::string* err) {
+ return WriteMiscPartitionSystemSpace(&message, sizeof(message),
+ offsetof(misc_system_space_layout, virtual_ab_message), 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 95dd8f4c9..e4cf09b22 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -25,12 +25,15 @@
// 0 - 2K For bootloader_message
// 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used
// as bootloader_message_ab struct)
-// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices
+// 16K - 32K Used by uncrypt and recovery to store wipe_package for A/B devices
+// 32K - 64K System space, used for miscellanious AOSP features. See below.
// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
// are not configurable without changing all of them.
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;
+constexpr size_t SYSTEM_SPACE_OFFSET_IN_MISC = 32 * 1024;
+constexpr size_t SYSTEM_SPACE_SIZE_IN_MISC = 32 * 1024;
/* Bootloader Message (2-KiB)
*
@@ -80,116 +83,47 @@ struct bootloader_message {
char reserved[1184];
};
-/**
- * We must be cautious when changing the bootloader_message struct size,
- * because A/B-specific fields may end up with different offsets.
- */
-#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
-static_assert(sizeof(struct bootloader_message) == 2048,
- "struct bootloader_message size changes, which may break A/B devices");
-#endif
-
-/**
- * The A/B-specific bootloader message structure (4-KiB).
- *
- * We separate A/B boot control metadata from the regular bootloader
- * message struct and keep it here. Everything that's A/B-specific
- * stays after struct bootloader_message, which should be managed by
- * the A/B-bootloader or boot control HAL.
- *
- * The slot_suffix field is used for A/B implementations where the
- * bootloader does not set the androidboot.ro.boot.slot_suffix kernel
- * commandline parameter. This is used by fs_mgr to mount /system and
- * other partitions with the slotselect flag set in fstab. A/B
- * implementations are free to use all 32 bytes and may store private
- * data past the first NUL-byte in this field. It is encouraged, but
- * not mandatory, to use 'struct bootloader_control' described below.
- *
- * The update_channel field is used to store the Omaha update channel
- * if update_engine is compiled with Omaha support.
- */
-struct bootloader_message_ab {
- struct bootloader_message message;
- char slot_suffix[32];
- char update_channel[128];
+// Holds Virtual A/B merge status information. Current version is 1. New fields
+// must be added to the end.
+struct misc_virtual_ab_message {
+ uint8_t version;
+ uint32_t magic;
+ uint8_t merge_status; // IBootControl 1.1, MergeStatus enum.
+ uint8_t source_slot; // Slot number when merge_status was written.
+ uint8_t reserved[57];
+} __attribute__((packed));
- // Round up the entire struct to 4096-byte.
- char reserved[1888];
-};
+#define MISC_VIRTUAL_AB_MESSAGE_VERSION 2
+#define MISC_VIRTUAL_AB_MAGIC_HEADER 0x56740AB0
-/**
- * Be cautious about the struct size change, in case we put anything post
- * bootloader_message_ab struct (b/29159185).
- */
#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
-static_assert(sizeof(struct bootloader_message_ab) == 4096,
- "struct bootloader_message_ab size changes");
+static_assert(sizeof(struct misc_virtual_ab_message) == 64,
+ "struct misc_virtual_ab_message has wrong size");
#endif
-#define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */
-#define BOOT_CTRL_VERSION 1
-
-struct slot_metadata {
- // Slot priority with 15 meaning highest priority, 1 lowest
- // priority and 0 the slot is unbootable.
- uint8_t priority : 4;
- // Number of times left attempting to boot this slot.
- uint8_t tries_remaining : 3;
- // 1 if this slot has booted successfully, 0 otherwise.
- uint8_t successful_boot : 1;
- // 1 if this slot is corrupted from a dm-verity corruption, 0
- // otherwise.
- uint8_t verity_corrupted : 1;
- // Reserved for further use.
- uint8_t reserved : 7;
+// This struct is not meant to be used directly, rather, it is to make
+// computation of offsets easier. New fields must be added to the end.
+struct misc_system_space_layout {
+ misc_virtual_ab_message virtual_ab_message;
} __attribute__((packed));
-/* Bootloader Control AB
- *
- * This struct can be used to manage A/B metadata. It is designed to
- * be put in the 'slot_suffix' field of the 'bootloader_message'
- * structure described above. It is encouraged to use the
- * 'bootloader_control' structure to store the A/B metadata, but not
- * mandatory.
- */
-struct bootloader_control {
- // NUL terminated active slot suffix.
- char slot_suffix[4];
- // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
- uint32_t magic;
- // Version of struct being used (see BOOT_CTRL_VERSION).
- uint8_t version;
- // Number of slots being managed.
- uint8_t nb_slot : 3;
- // Number of times left attempting to boot recovery.
- uint8_t recovery_tries_remaining : 3;
- // Ensure 4-bytes alignment for slot_info field.
- uint8_t reserved0[2];
- // Per-slot information. Up to 4 slots.
- struct slot_metadata slot_info[4];
- // Reserved for further use.
- uint8_t reserved1[8];
- // CRC32 of all 28 bytes preceding this field (little endian
- // format).
- uint32_t crc32_le;
-} __attribute__((packed));
-
-#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
-static_assert(sizeof(struct bootloader_control) ==
- sizeof(((struct bootloader_message_ab *)0)->slot_suffix),
- "struct bootloader_control has wrong size");
-#endif
-
#ifdef __cplusplus
#include <string>
#include <vector>
+// Gets the block device name of /misc partition.
+std::string get_misc_blk_device(std::string* err);
// Return the block device name for the bootloader message partition and waits
// for the device for up to 10 seconds. In case of error returns the empty
// string.
std::string get_bootloader_message_blk_device(std::string* err);
+// Writes |size| bytes of data from buffer |p| to |misc_blk_device| at |offset|. If the write fails,
+// sets the error message in |err|.
+bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
+ size_t offset, std::string* err);
+
// Read bootloader message into boot. Error message will be set in err.
bool read_bootloader_message(bootloader_message* boot, std::string* err);
@@ -208,6 +142,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);
@@ -229,13 +168,9 @@ 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);
+// Read or write the Virtual A/B message from system space in /misc.
+bool ReadMiscVirtualAbMessage(misc_virtual_ab_message* message, std::string* err);
+bool WriteMiscVirtualAbMessage(const misc_virtual_ab_message& message, std::string* err);
#else
diff --git a/common.h b/common.h
deleted file mode 100644
index a524a4184..000000000
--- a/common.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2007 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>
-
-// Not using the command-line defined macro here because this header could be included by
-// device-specific recovery libraries. We static assert the value consistency in recovery.cpp.
-static constexpr int kRecoveryApiVersion = 3;
-
-class RecoveryUI;
-struct selabel_handle;
-
-extern struct selabel_handle* sehandle;
-extern RecoveryUI* ui;
-extern bool has_cache;
-
-// The current stage, e.g. "1/2".
-extern std::string stage;
-
-// The reason argument provided in "--reason=".
-extern const char* reason;
-
-bool is_ro_debuggable();
diff --git a/edify/Android.bp b/edify/Android.bp
index 42947eb4e..0ab53d6dd 100644
--- a/edify/Android.bp
+++ b/edify/Android.bp
@@ -16,6 +16,8 @@ cc_library_static {
name: "libedify",
host_supported: true,
+ vendor_available: true,
+ recovery_available: true,
srcs: [
"expr.cpp",
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..aa977e3c8
--- /dev/null
+++ b/edify/include/edify/updater_interface.h
@@ -0,0 +1,48 @@
+/*
+ * 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;
+ virtual size_t GetMappedPackageLength() 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..d3d26da64
--- /dev/null
+++ b/edify/include/edify/updater_runtime_interface.h
@@ -0,0 +1,74 @@
+/*
+ * 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;
+
+ // Dynamic partition related functions.
+ virtual bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) = 0;
+ virtual bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) = 0;
+ virtual bool UpdateDynamicPartitions(const std::string_view op_list_value) = 0;
+};
diff --git a/edify/parser.yy b/edify/parser.yy
index 3a63c37f8..37bcdd031 100644
--- a/edify/parser.yy
+++ b/edify/parser.yy
@@ -72,7 +72,7 @@ static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) {
%parse-param {std::unique_ptr<Expr>* root}
%parse-param {int* error_count}
-%error-verbose
+%define parse.error verbose
/* declarations in increasing order of precedence */
%left ';'
diff --git a/etc/init.rc b/etc/init.rc
index 0822aba03..3ec45db2f 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -4,6 +4,10 @@ on early-init
# Set the security context of /postinstall if present.
restorecon /postinstall
+ # Copy prebuilt ld.config.txt into linkerconfig directory
+ copy /system/etc/ld.config.txt /linkerconfig/ld.config.txt
+ chmod 444 /linkerconfig/ld.config.txt
+
start ueventd
setprop sys.usb.configfs 0
@@ -44,10 +48,6 @@ on boot
class_start default
-# Load properties from /system/ + /factory after fs mount.
-on load_system_props_action
- load_system_props
-
on firmware_mounts_complete
rm /dev/.booting
@@ -58,11 +58,6 @@ on late-init
trigger post-fs
trigger post-fs-data
- # Load properties from /system/ + /factory after fs mount. Place
- # this in another action so that the load will be scheduled after the prior
- # issued fs triggers have completed.
- trigger load_system_props_action
-
# Remove a file to wake up anything waiting for firmware
trigger firmware_mounts_complete
@@ -95,10 +90,6 @@ service fastbootd /system/bin/fastbootd
on property:service.adb.root=1
restart adbd
-# Always start adbd on userdebug and eng builds
-on fs && property:ro.debuggable=1
- setprop sys.usb.config adb
-
on fs && property:sys.usb.configfs=1
mount configfs none /config
mkdir /config/usb_gadget/g1 0770 shell shell
@@ -134,7 +125,7 @@ on property:sys.usb.config=fastboot
on property:sys.usb.config=none && property:sys.usb.configfs=0
stop adbd
- stop fastboot
+ stop fastbootd
write /sys/class/android_usb/android0/enable 0
setprop sys.usb.state ${sys.usb.config}
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
deleted file mode 100644
index e74f8ba6f..000000000
--- a/fsck_unshare_blocks.cpp
+++ /dev/null
@@ -1,151 +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 "fsck_unshare_blocks.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <spawn.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/unique_fd.h>
-#include <fstab/fstab.h>
-
-#include "otautil/roots.h"
-
-static constexpr const char* SYSTEM_E2FSCK_BIN = "/system/bin/e2fsck_static";
-static constexpr const char* TMP_E2FSCK_BIN = "/tmp/e2fsck.bin";
-
-static bool copy_file(const char* source, const char* dest) {
- android::base::unique_fd source_fd(open(source, O_RDONLY));
- if (source_fd < 0) {
- PLOG(ERROR) << "open %s failed" << source;
- return false;
- }
-
- android::base::unique_fd dest_fd(open(dest, O_CREAT | O_WRONLY, S_IRWXU));
- if (dest_fd < 0) {
- PLOG(ERROR) << "open %s failed" << dest;
- return false;
- }
-
- for (;;) {
- char buf[4096];
- ssize_t rv = read(source_fd, buf, sizeof(buf));
- if (rv < 0) {
- PLOG(ERROR) << "read failed";
- return false;
- }
- if (rv == 0) {
- break;
- }
- if (write(dest_fd, buf, rv) != rv) {
- PLOG(ERROR) << "write failed";
- return false;
- }
- }
- return true;
-}
-
-static bool run_e2fsck(const std::string& partition) {
- Volume* volume = volume_for_mount_point(partition);
- if (!volume) {
- LOG(INFO) << "No fstab entry for " << partition << ", skipping.";
- return true;
- }
-
- LOG(INFO) << "Running e2fsck on device " << volume->blk_device;
-
- std::vector<std::string> args = { TMP_E2FSCK_BIN, "-p", "-E", "unshare_blocks",
- volume->blk_device };
- std::vector<char*> argv(args.size());
- std::transform(args.cbegin(), args.cend(), argv.begin(),
- [](const std::string& arg) { return const_cast<char*>(arg.c_str()); });
- argv.push_back(nullptr);
-
- pid_t child;
- char* env[] = { nullptr };
- if (posix_spawn(&child, argv[0], nullptr, nullptr, argv.data(), env)) {
- PLOG(ERROR) << "posix_spawn failed";
- return false;
- }
-
- int status = 0;
- int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
- if (ret < 0) {
- PLOG(ERROR) << "waitpid failed";
- return false;
- }
- if (!WIFEXITED(status)) {
- LOG(ERROR) << "e2fsck exited abnormally: " << status;
- return false;
- }
- int return_code = WEXITSTATUS(status);
- if (return_code >= 8) {
- LOG(ERROR) << "e2fsck could not unshare blocks: " << return_code;
- return false;
- }
-
- LOG(INFO) << "Successfully unshared blocks on " << partition;
- return true;
-}
-
-bool do_fsck_unshare_blocks() {
- // List of partitions we will try to e2fsck -E 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();
- bool mounted = ensure_path_mounted_at(system_root, "/mnt/system") != -1;
- partitions.push_back(system_root);
-
- if (!mounted) {
- LOG(ERROR) << "Failed to mount system image.";
- return false;
- }
- if (!copy_file(SYSTEM_E2FSCK_BIN, TMP_E2FSCK_BIN)) {
- LOG(ERROR) << "Could not copy e2fsck to /tmp.";
- return false;
- }
- if (umount("/mnt/system") < 0) {
- PLOG(ERROR) << "umount failed";
- return false;
- }
-
- bool ok = true;
- for (const auto& partition : partitions) {
- ok &= run_e2fsck(partition);
- }
-
- if (ok) {
- LOG(INFO) << "Finished running e2fsck.";
- } else {
- LOG(ERROR) << "Finished running e2fsck, but not all partitions succceeded.";
- }
- return ok;
-}
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..8fa1b5c2e 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;
@@ -46,6 +49,11 @@ FuseFileDataProvider::FuseFileDataProvider(const std::string& path, uint32_t blo
fuse_block_size_ = block_size;
}
+std::unique_ptr<FuseDataProvider> FuseFileDataProvider::CreateFromFile(const std::string& path,
+ uint32_t block_size) {
+ return std::make_unique<FuseFileDataProvider>(path, block_size);
+}
+
bool FuseFileDataProvider::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_;
@@ -69,3 +77,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<FuseDataProvider> 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..3cdaef33d 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:
@@ -41,6 +44,8 @@ class FuseDataProvider {
virtual bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
uint32_t start_block) const = 0;
+ virtual bool Valid() const = 0;
+
virtual void Close() {}
protected:
@@ -57,10 +62,13 @@ class FuseFileDataProvider : public FuseDataProvider {
public:
FuseFileDataProvider(const std::string& path, uint32_t block_size);
+ static std::unique_ptr<FuseDataProvider> CreateFromFile(const std::string& path,
+ uint32_t block_size);
+
bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
uint32_t start_block) const override;
- bool Valid() const {
+ bool Valid() const override {
return fd_ != -1;
}
@@ -70,3 +78,34 @@ 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<FuseDataProvider> 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;
+
+ bool Valid() const override {
+ return fd_ != -1;
+ }
+
+ 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 ea893a075..bed3bc504 100644
--- a/install/Android.bp
+++ b/install/Android.bp
@@ -19,10 +19,6 @@ cc_defaults {
"recovery_defaults",
],
- header_libs: [
- "libminadbd_headers",
- ],
-
shared_libs: [
"libbase",
"libbootloader_message",
@@ -32,7 +28,6 @@ cc_defaults {
"libfusesideload",
"libhidl-gen-utils",
"libhidlbase",
- "libhidltransport",
"liblog",
"libselinux",
"libtinyxml2",
@@ -42,12 +37,12 @@ cc_defaults {
],
static_libs: [
+ "librecovery_utils",
"libotautil",
+ "libsnapshot_nobinder",
// external dependencies
- "libvintf_recovery",
"libvintf",
- "libfstab",
],
}
@@ -62,11 +57,17 @@ cc_library_static {
srcs: [
"adb_install.cpp",
"asn1_decoder.cpp",
- "fuse_sdcard_install.cpp",
+ "fuse_install.cpp",
"install.cpp",
"package.cpp",
+ "snapshot_utils.cpp",
"verifier.cpp",
"wipe_data.cpp",
+ "wipe_device.cpp",
+ ],
+
+ header_libs: [
+ "libminadbd_headers",
],
shared_libs: [
diff --git a/install/adb_install.cpp b/install/adb_install.cpp
index 9497df501..ee79a32c0 100644
--- a/install/adb_install.cpp
+++ b/install/adb_install.cpp
@@ -44,7 +44,7 @@
#include "fuse_sideload.h"
#include "install/install.h"
#include "install/wipe_data.h"
-#include "minadbd_types.h"
+#include "minadbd/types.h"
#include "otautil/sysutil.h"
#include "recovery_ui/device.h"
#include "recovery_ui/ui.h"
@@ -90,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
@@ -110,7 +110,11 @@ static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) {
break;
}
}
- *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui);
+
+ auto package =
+ Package::CreateFilePackage(FUSE_SIDELOAD_HOST_PATHNAME,
+ std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
+ *result = InstallPackage(package.get(), FUSE_SIDELOAD_HOST_PATHNAME, false, 0, ui);
break;
}
@@ -120,7 +124,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
@@ -331,7 +335,7 @@ static void CreateMinadbdServiceAndExecuteCommands(
signal(SIGPIPE, SIG_DFL);
}
-int ApplyFromAdb(Device* device, 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.
@@ -342,7 +346,7 @@ int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot
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,
diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_install.cpp
index 1aa8768e7..143b5d3fb 100644
--- a/install/fuse_sdcard_install.cpp
+++ b/install/fuse_install.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "install/fuse_sdcard_install.h"
+#include "install/fuse_install.h"
#include <dirent.h>
#include <signal.h>
@@ -27,6 +27,7 @@
#include <algorithm>
#include <functional>
#include <memory>
+#include <string>
#include <vector>
#include <android-base/logging.h>
@@ -36,7 +37,7 @@
#include "fuse_provider.h"
#include "fuse_sideload.h"
#include "install/install.h"
-#include "otautil/roots.h"
+#include "recovery_utils/roots.h"
static constexpr const char* SDCARD_ROOT = "/sdcard";
// How long (in seconds) we wait for the fuse-provided package file to
@@ -74,7 +75,8 @@ static std::string BrowseDirectory(const std::string& path, Device* device, Reco
// Skip "." and ".." entries.
if (name == "." || name == "..") continue;
dirs.push_back(name + "/");
- } else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) {
+ } else if (de->d_type == DT_REG && (android::base::EndsWithIgnoreCase(name, ".zip") ||
+ android::base::EndsWithIgnoreCase(name, ".map"))) {
entries.push_back(name);
}
}
@@ -119,49 +121,44 @@ static std::string BrowseDirectory(const std::string& path, Device* device, Reco
// Unreachable.
}
-static bool StartSdcardFuse(const std::string& path) {
- auto file_data_reader = std::make_unique<FuseFileDataProvider>(path, 65536);
-
- if (!file_data_reader->Valid()) {
+static bool StartInstallPackageFuse(std::string_view path) {
+ if (path.empty()) {
return false;
}
- // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
- // that our open file continues to work but new references see it as unmounted.
- umount2("/sdcard", MNT_DETACH);
+ constexpr auto FUSE_BLOCK_SIZE = 65536;
+ bool is_block_map = android::base::ConsumePrefix(&path, "@");
+ auto fuse_data_provider =
+ is_block_map ? FuseBlockDataProvider::CreateFromBlockMap(std::string(path), FUSE_BLOCK_SIZE)
+ : FuseFileDataProvider::CreateFromFile(std::string(path), FUSE_BLOCK_SIZE);
- return run_fuse_sideload(std::move(file_data_reader)) == 0;
-}
-
-int ApplyFromSdcard(Device* device, RecoveryUI* ui) {
- if (ensure_path_mounted(SDCARD_ROOT) != 0) {
- LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n";
- return INSTALL_ERROR;
+ if (!fuse_data_provider || !fuse_data_provider->Valid()) {
+ LOG(ERROR) << "Failed to create fuse data provider.";
+ return false;
}
- std::string path = BrowseDirectory(SDCARD_ROOT, device, ui);
- if (path.empty()) {
- LOG(ERROR) << "\n-- No package file selected.\n";
- ensure_path_unmounted(SDCARD_ROOT);
- return INSTALL_ERROR;
+ if (android::base::StartsWith(path, SDCARD_ROOT)) {
+ // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
+ // that our open file continues to work but new references see it as unmounted.
+ umount2(SDCARD_ROOT, MNT_DETACH);
}
- ui->Print("\n-- Install %s ...\n", path.c_str());
- SetSdcardUpdateBootloaderMessage();
+ return run_fuse_sideload(std::move(fuse_data_provider)) == 0;
+}
+InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui) {
// We used to use fuse in a thread as opposed to a process. Since accessing
// through fuse involves going from kernel to userspace to kernel, it leads
// to deadlock when a page fault occurs. (Bug: 26313124)
pid_t child;
if ((child = fork()) == 0) {
- bool status = StartSdcardFuse(path);
+ bool status = StartInstallPackageFuse(path);
_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) {
@@ -183,8 +180,11 @@ int ApplyFromSdcard(Device* device, RecoveryUI* ui) {
break;
}
}
-
- result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui);
+ auto package =
+ Package::CreateFilePackage(FUSE_SIDELOAD_HOST_PATHNAME,
+ std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
+ result =
+ InstallPackage(package.get(), FUSE_SIDELOAD_HOST_PATHNAME, false, 0 /* retry_count */, ui);
break;
}
@@ -201,6 +201,32 @@ int ApplyFromSdcard(Device* device, RecoveryUI* ui) {
LOG(ERROR) << "Error exit from the fuse process: " << WEXITSTATUS(status);
}
+ return result;
+}
+
+InstallResult ApplyFromSdcard(Device* device) {
+ auto ui = device->GetUI();
+ if (ensure_path_mounted(SDCARD_ROOT) != 0) {
+ LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n";
+ return INSTALL_ERROR;
+ }
+
+ std::string path = BrowseDirectory(SDCARD_ROOT, device, ui);
+ if (path.empty()) {
+ LOG(ERROR) << "\n-- No package file selected.\n";
+ ensure_path_unmounted(SDCARD_ROOT);
+ return INSTALL_ERROR;
+ }
+
+ // Hint the install function to read from a block map file.
+ if (android::base::EndsWithIgnoreCase(path, ".map")) {
+ path = "@" + path;
+ }
+
+ ui->Print("\n-- Install %s ...\n", path.c_str());
+ SetSdcardUpdateBootloaderMessage();
+
+ auto result = InstallWithFuseFromPath(path, ui);
ensure_path_unmounted(SDCARD_ROOT);
return result;
}
diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h
index 3a0a81747..880022361 100644
--- a/install/include/install/adb_install.h
+++ b/install/include/install/adb_install.h
@@ -16,9 +16,10 @@
#pragma once
-#include <recovery_ui/device.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(Device* device, 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_install.h b/install/include/install/fuse_install.h
new file mode 100644
index 000000000..63b116aeb
--- /dev/null
+++ b/install/include/install/fuse_install.h
@@ -0,0 +1,30 @@
+/*
+ * 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_view>
+
+#include "install/install.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
+
+// Starts FUSE with the package from |path| as the data source. And installs the package from
+// |FUSE_SIDELOAD_HOST_PATHNAME|. The |path| can point to the location of a package zip file or a
+// block map file with the prefix '@'; e.g. /sdcard/package.zip, @/cache/recovery/block.map.
+InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui);
+
+InstallResult ApplyFromSdcard(Device* device);
diff --git a/install/include/install/install.h b/install/include/install/install.h
index ed0f6c7da..bef23e9ca 100644
--- a/install/include/install/install.h
+++ b/install/include/install/install.h
@@ -44,11 +44,12 @@ enum class OtaType {
BRICK,
};
-// 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);
+// Installs the given update package. The package_id is a string provided by the caller (e.g. the
+// package path) to identify the package and log to last_install. 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.
+InstallResult InstallPackage(Package* package, const std::string_view package_id,
+ bool should_wipe_cache, int retry_count, RecoveryUI* ui);
// Verifies the package by ota keys. Returns true if the package is verified successfully,
// otherwise returns false.
@@ -58,17 +59,10 @@ 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);
// Ensures the path to the update package is mounted. Also set the |should_use_fuse| to true if the
// package stays on a removable media.
diff --git a/install/include/install/package.h b/install/include/install/package.h
index cd44d10be..0b4233238 100644
--- a/install/include/install/package.h
+++ b/install/include/install/package.h
@@ -28,6 +28,11 @@
#include "verifier.h"
+enum class PackageType {
+ kMemory,
+ kFile,
+};
+
// This class serves as a wrapper for an OTA update package. It aims to provide the common
// interface for both packages loaded in memory and packages read from fd.
class Package : public VerifierInterface {
@@ -41,6 +46,10 @@ class Package : public VerifierInterface {
virtual ~Package() = default;
+ virtual PackageType GetType() const = 0;
+
+ virtual std::string GetPath() const = 0;
+
// Opens the package as a zip file and returns the ZipArchiveHandle.
virtual ZipArchiveHandle GetZipArchiveHandle() = 0;
diff --git a/install/include/install/snapshot_utils.h b/install/include/install/snapshot_utils.h
new file mode 100644
index 000000000..f4b978d2e
--- /dev/null
+++ b/install/include/install/snapshot_utils.h
@@ -0,0 +1,30 @@
+/*
+ * 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 "recovery_ui/device.h"
+
+bool FinishPendingSnapshotMerges(Device* device);
+
+/*
+ * This function tries to create the snapshotted devices in the case a Virtual
+ * A/B device is updating.
+ * The function returns false in case of critical failure that would prevent
+ * the further mountings of devices, or true in case of success, if either the
+ * devices were created or there was no need to.
+ */
+bool CreateSnapshotPartitions();
diff --git a/install/include/install/fuse_sdcard_install.h b/install/include/install/wipe_device.h
index d9214ca3b..c60b99997 100644
--- a/install/include/install/fuse_sdcard_install.h
+++ b/install/include/install/wipe_device.h
@@ -16,7 +16,14 @@
#pragma once
+#include <string>
+#include <vector>
+
+#include "install/package.h"
#include "recovery_ui/device.h"
-#include "recovery_ui/ui.h"
-int ApplyFromSdcard(Device* device, RecoveryUI* ui);
+// 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 9203ef0e5..1c9bf2fd2 100644
--- a/install/install.cpp
+++ b/install/install.cpp
@@ -45,23 +45,23 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <vintf/VintfObjectRecovery.h>
#include "install/package.h"
#include "install/verifier.h"
#include "install/wipe_data.h"
#include "otautil/error_code.h"
#include "otautil/paths.h"
-#include "otautil/roots.h"
#include "otautil/sysutil.h"
-#include "otautil/thermalutil.h"
#include "private/setup_commands.h"
#include "recovery_ui/ui.h"
+#include "recovery_utils/roots.h"
+#include "recovery_utils/thermalutil.h"
using namespace std::chrono_literals;
static constexpr int kRecoveryApiVersion = 3;
-// Assert the version defined in code and in Android.mk are consistent.
+// We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed
+// into target_files.zip. Assert the version defined in code and in Android.mk are consistent.
static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions.");
// Default allocation of progress bar segments to operations
@@ -74,9 +74,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;
}
@@ -140,14 +139,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", "");
@@ -155,7 +154,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.
@@ -173,36 +172,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
@@ -219,7 +218,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;
}
}
@@ -227,21 +226,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);
@@ -249,15 +247,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 = {
@@ -267,20 +264,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();
@@ -289,13 +285,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:
@@ -312,7 +307,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) {
@@ -326,21 +321,25 @@ 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(Package* package, bool* wipe_cache,
+ std::vector<std::string>* log_buffer, int retry_count,
+ int* max_temperature, RecoveryUI* ui) {
std::map<std::string, std::string> metadata;
+ auto zip = package->GetZipArchiveHandle();
if (!ReadMetadataFromPackage(zip, &metadata)) {
LOG(ERROR) << "Failed to parse metadata in the zip file";
return INSTALL_CORRUPT;
}
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) {
+ if (is_ab) {
+ CHECK(package->GetType() == PackageType::kFile);
+ }
+
+ // 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,13 +385,15 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b
// updater requests logging the string (e.g. cause of the failure).
//
+ std::string package_path = package->GetPath();
+
std::vector<std::string> args;
- if (int update_status =
- is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args)
- : SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args);
- update_status != 0) {
+ if (auto setup_result =
+ is_ab ? SetUpAbUpdateCommands(package_path, zip, pipe_write.get(), &args)
+ : SetUpNonAbUpdateCommands(package_path, zip, retry_count, pipe_write.get(), &args);
+ !setup_result) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
- return update_status;
+ return INSTALL_CORRUPT;
}
pid_t pid = fork();
@@ -491,11 +492,11 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b
}
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != EXIT_SUCCESS) {
- LOG(ERROR) << "Error in " << package << " (status " << WEXITSTATUS(status) << ")";
+ LOG(ERROR) << "Error in " << package_path << " (status " << WEXITSTATUS(status) << ")";
return INSTALL_ERROR;
}
} else if (WIFSIGNALED(status)) {
- LOG(ERROR) << "Error in " << package << " (killed by signal " << WTERMSIG(status) << ")";
+ LOG(ERROR) << "Error in " << package_path << " (killed by signal " << WTERMSIG(status) << ")";
return INSTALL_ERROR;
} else {
LOG(FATAL) << "Invalid status code " << status;
@@ -504,153 +505,57 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b
return INSTALL_SUCCESS;
}
-// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
-// entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA
-// package.
-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) {
- LOG(INFO) << "Package doesn't contain " << COMPATIBILITY_ZIP_ENTRY << " entry";
- return true;
- }
-
- std::string zip_content(compatibility_entry.uncompressed_length, '\0');
- int32_t ret;
- if ((ret = ExtractToMemory(package_zip, &compatibility_entry,
- reinterpret_cast<uint8_t*>(&zip_content[0]),
- compatibility_entry.uncompressed_length)) != 0) {
- LOG(ERROR) << "Failed to read " << COMPATIBILITY_ZIP_ENTRY << ": " << ErrorCodeString(ret);
- return false;
- }
-
- ZipArchiveHandle zip_handle;
- ret = OpenArchiveFromMemory(static_cast<void*>(const_cast<char*>(zip_content.data())),
- zip_content.size(), COMPATIBILITY_ZIP_ENTRY, &zip_handle);
- if (ret != 0) {
- LOG(ERROR) << "Failed to OpenArchiveFromMemory: " << ErrorCodeString(ret);
- return false;
- }
-
- // Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents.
- void* cookie;
- ret = StartIteration(zip_handle, &cookie, nullptr, nullptr);
- if (ret != 0) {
- LOG(ERROR) << "Failed to start iterating zip entries: " << ErrorCodeString(ret);
- CloseArchive(zip_handle);
- return false;
- }
- std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
-
- std::vector<std::string> compatibility_info;
- ZipEntry info_entry;
- ZipString 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);
- CloseArchive(zip_handle);
- return false;
- }
- compatibility_info.emplace_back(std::move(content));
- }
- CloseArchive(zip_handle);
-
- // VintfObjectRecovery::CheckCompatibility returns zero on success.
- std::string err;
- int result = android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err);
- if (result == 0) {
- return true;
- }
-
- LOG(ERROR) << "Failed to verify package compatibility (result " << result << "): " << err;
- 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(Package* package, bool* wipe_cache,
+ 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...
ui->SetProgressType(RecoveryUI::DETERMINATE);
ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
- LOG(INFO) << "Update location: " << path;
-
- // Map the update package into memory.
- ui->Print("Opening update package...\n");
-
- if (needs_mount) {
- if (path[0] == '@') {
- ensure_path_mounted(path.substr(1));
- } else {
- ensure_path_mounted(path);
- }
- }
-
- auto package = Package::CreateMemoryPackage(
- path, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
- if (!package) {
- log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
- return INSTALL_CORRUPT;
- }
// Verify package.
- if (!verify_package(package.get(), ui)) {
+ if (!verify_package(package, ui)) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
return INSTALL_CORRUPT;
}
- // Try to open the package.
- ZipArchiveHandle zip = package->GetZipArchiveHandle();
- if (!zip) {
- log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
- return INSTALL_CORRUPT;
- }
-
- // Additionally verify the compatibility of the package if it's a fresh install.
- if (retry_count == 0 && !verify_package_compatibility(zip)) {
- log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
- return INSTALL_CORRUPT;
- }
-
// Verify and install the contents of the package.
ui->Print("Installing update...\n");
if (retry_count > 0) {
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(package, 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) {
- CHECK(!path.empty());
-
+InstallResult InstallPackage(Package* package, const std::string_view package_id,
+ bool should_wipe_cache, int retry_count, RecoveryUI* ui) {
auto start = std::chrono::system_clock::now();
int start_temperature = GetMaxValueFromThermalZone();
int max_temperature = start_temperature;
- int result;
+ InstallResult result;
std::vector<std::string> log_buffer;
- if (setup_install_mounts() != 0) {
+
+ ui->Print("Supported API: %d\n", kRecoveryApiVersion);
+
+ ui->Print("Finding update package...\n");
+ LOG(INFO) << "Update package id: " << package_id;
+ if (!package) {
+ log_buffer.push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
+ result = INSTALL_CORRUPT;
+ } else 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(package, &updater_wipe_cache, &log_buffer, retry_count,
+ &max_temperature, ui);
should_wipe_cache = should_wipe_cache || updater_wipe_cache;
}
@@ -678,7 +583,7 @@ int install_package(const std::string& path, bool should_wipe_cache, bool needs_
// The first two lines need to be the package name and install result.
std::vector<std::string> log_header = {
- path,
+ std::string(package_id),
result == INSTALL_SUCCESS ? "1" : "0",
"time_total: " + std::to_string(time_total),
"retry: " + std::to_string(retry_count),
diff --git a/install/package.cpp b/install/package.cpp
index 4402f4855..86fc0647d 100644
--- a/install/package.cpp
+++ b/install/package.cpp
@@ -40,12 +40,20 @@ class MemoryPackage : public Package {
~MemoryPackage() override;
+ PackageType GetType() const override {
+ return PackageType::kMemory;
+ }
+
// Memory maps the package file if necessary. Initializes the start address and size of the
// package.
uint64_t GetPackageSize() const override {
return package_size_;
}
+ std::string GetPath() const override {
+ return path_;
+ }
+
bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
ZipArchiveHandle GetZipArchiveHandle() override;
@@ -82,10 +90,18 @@ class FilePackage : public Package {
~FilePackage() override;
+ PackageType GetType() const override {
+ return PackageType::kFile;
+ }
+
uint64_t GetPackageSize() const override {
return package_size_;
}
+ std::string GetPath() const override {
+ return path_;
+ }
+
bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
ZipArchiveHandle GetZipArchiveHandle() override;
@@ -253,7 +269,7 @@ ZipArchiveHandle FilePackage::GetZipArchiveHandle() {
return zip_handle_;
}
- if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_); err != 0) {
+ if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_, false); err != 0) {
LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err);
return nullptr;
}
diff --git a/install/snapshot_utils.cpp b/install/snapshot_utils.cpp
new file mode 100644
index 000000000..7235e67c8
--- /dev/null
+++ b/install/snapshot_utils.cpp
@@ -0,0 +1,74 @@
+
+/*
+ * 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 <android-base/logging.h>
+#include <android-base/properties.h>
+#include <libsnapshot/snapshot.h>
+
+#include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
+#include "recovery_utils/roots.h"
+
+using android::snapshot::CreateResult;
+using android::snapshot::SnapshotManager;
+
+bool FinishPendingSnapshotMerges(Device* device) {
+ if (!android::base::GetBoolProperty("ro.virtual_ab.enabled", false)) {
+ return true;
+ }
+
+ RecoveryUI* ui = device->GetUI();
+ auto sm = SnapshotManager::NewForFirstStageMount();
+ if (!sm) {
+ ui->Print("Could not create SnapshotManager.\n");
+ return false;
+ }
+
+ auto callback = [&]() -> void {
+ double progress;
+ sm->GetUpdateState(&progress);
+ ui->Print("Waiting for merge to complete: %.2f\n", progress);
+ };
+ if (!sm->HandleImminentDataWipe(callback)) {
+ ui->Print("Unable to check merge status and/or complete update merge.\n");
+ return false;
+ }
+ return true;
+}
+
+bool CreateSnapshotPartitions() {
+ if (!android::base::GetBoolProperty("ro.virtual_ab.enabled", false)) {
+ // If the device does not support Virtual A/B, there's no need to create
+ // snapshot devices.
+ return true;
+ }
+
+ auto sm = SnapshotManager::NewForFirstStageMount();
+ if (!sm) {
+ // SnapshotManager could not be created. The device is still in a
+ // consistent state and can continue with the mounting of the existing
+ // devices, but cannot initialize snapshot devices.
+ LOG(WARNING) << "Could not create SnapshotManager";
+ return true;
+ }
+
+ auto ret = sm->RecoveryCreateSnapshotDevices();
+ if (ret == CreateResult::ERROR) {
+ return false;
+ }
+ return true;
+}
diff --git a/install/verifier.cpp b/install/verifier.cpp
index 6ba1d77c3..ab750442d 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_view 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_data.cpp b/install/wipe_data.cpp
index 765a8152b..287208583 100644
--- a/install/wipe_data.cpp
+++ b/install/wipe_data.cpp
@@ -27,10 +27,11 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include "install/snapshot_utils.h"
#include "otautil/dirutil.h"
-#include "otautil/logging.h"
-#include "otautil/roots.h"
#include "recovery_ui/ui.h"
+#include "recovery_utils/logging.h"
+#include "recovery_utils/roots.h"
constexpr const char* CACHE_ROOT = "/cache";
constexpr const char* DATA_ROOT = "/data";
@@ -104,6 +105,12 @@ bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm_func) {
bool WipeData(Device* device, bool convert_fbe) {
RecoveryUI* ui = device->GetUI();
ui->Print("\n-- Wiping data...\n");
+
+ if (!FinishPendingSnapshotMerges(device)) {
+ ui->Print("Unable to check update status or complete merge, cannot wipe partitions.\n");
+ return false;
+ }
+
bool success = device->PreWipeData();
if (success) {
success &= EraseVolume(DATA_ROOT, ui, convert_fbe);
@@ -120,4 +127,4 @@ bool WipeData(Device* device, bool convert_fbe) {
}
ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
return success;
-} \ No newline at end of file
+}
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..c39c73443 100644
--- a/minadbd/Android.bp
+++ b/minadbd/Android.bp
@@ -26,6 +26,10 @@ cc_defaults {
include_dirs: [
"system/core/adb",
],
+
+ header_libs: [
+ "libminadbd_headers",
+ ],
}
// `libminadbd_services` is analogous to the `libadbd_services` for regular `adbd`, but providing
@@ -36,6 +40,7 @@ cc_library {
defaults: [
"minadbd_defaults",
+ "librecovery_utils_defaults",
],
srcs: [
@@ -43,6 +48,11 @@ cc_library {
"minadbd_services.cpp",
],
+ static_libs: [
+ "librecovery_utils",
+ "libotautil",
+ ],
+
shared_libs: [
"libadbd",
"libbase",
@@ -54,9 +64,12 @@ cc_library {
cc_library_headers {
name: "libminadbd_headers",
recovery_available: true,
- // TODO create a include dir
export_include_dirs: [
- ".",
+ "include",
+ ],
+ // adb_install.cpp
+ visibility: [
+ "//bootable/recovery/install",
],
}
@@ -78,6 +91,10 @@ cc_binary {
"libcrypto",
"libminadbd_services",
],
+
+ required: [
+ "adbd_system_binaries_recovery",
+ ]
}
cc_test {
@@ -86,6 +103,7 @@ cc_test {
defaults: [
"minadbd_defaults",
+ "librecovery_utils_defaults",
],
srcs: [
@@ -96,12 +114,14 @@ cc_test {
static_libs: [
"libminadbd_services",
"libfusesideload",
+ "librecovery_utils",
+ "libotautil",
"libadbd",
- "libcrypto",
],
shared_libs: [
"libbase",
+ "libcrypto",
"libcutils",
"liblog",
],
diff --git a/minadbd/README.md b/minadbd/README.md
index 5a0a067de..9a1958309 100644
--- a/minadbd/README.md
+++ b/minadbd/README.md
@@ -1,8 +1,24 @@
-minadbd is now mostly built from libadbd. The fuse features are unique to
-minadbd, and services.c has been modified as follows:
-
- - all services removed
- - all host mode support removed
- - `sideload_service()` added; this is the only service supported. It
- receives a single blob of data, writes it to a fixed filename, and
- makes the process exit.
+minadbd
+=======
+
+`minadbd` is analogous to the regular `adbd`, but providing the minimal services to support
+recovery-specific use cases. Generally speaking, `adbd` = `libadbd` + `libadbd_services`, whereas
+`minadbd` = `libadbd` + `libminadbd_services`.
+
+Although both modules may be installed into the recovery image, only one of them, or none, can be
+active at any given time.
+
+- The start / stop of `adbd` is managed via system property `sys.usb.config`, when setting to `adb`
+ or `none` respectively. Upon starting recovery mode, `adbd` is started in debuggable builds by
+ default; otherwise `adbd` will stay off at all times in user builds. See the triggers in
+ `bootable/recovery/etc/init.rc`.
+
+- `minadbd` is started by `recovery` as needed.
+ - When requested to start `minadbd`, `recovery` stops `adbd` first, if it's running; it then forks
+ and execs `minadbd` in a separate process.
+ - `minadbd` talks to host-side `adb` server to get user requests.
+ - `minadbd` handles some requests directly, e.g. querying device properties for rescue service.
+ - `minadbd` communicates with `recovery` to fulfill requests regarding package installation. See
+ the comments in `bootable/recovery/install/adb_install.cpp` for the IPC protocol between
+ `recovery` and `minadbd`.
+ - Upon exiting `minadbd`, `recovery` restarts `adbd` if it was previously running.
diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h
index c5561e57d..43c07d28e 100644
--- a/minadbd/fuse_adb_provider.h
+++ b/minadbd/fuse_adb_provider.h
@@ -29,6 +29,10 @@ class FuseAdbDataProvider : public FuseDataProvider {
bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
uint32_t start_block) const override;
+ bool Valid() const override {
+ return fd_ != -1;
+ }
+
private:
// The underlying source to read data from (i.e. the one that talks to the host).
int fd_;
diff --git a/minadbd/minadbd_types.h b/minadbd/include/minadbd/types.h
index 002523f1f..002523f1f 100644
--- a/minadbd/minadbd_types.h
+++ b/minadbd/include/minadbd/types.h
diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp
index c80d5490a..7b82faa05 100644
--- a/minadbd/minadbd.cpp
+++ b/minadbd/minadbd.cpp
@@ -28,8 +28,8 @@
#include "adb_auth.h"
#include "transport.h"
+#include "minadbd/types.h"
#include "minadbd_services.h"
-#include "minadbd_types.h"
using namespace std::string_literals;
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index 03341e4b8..eb91fb3e4 100644
--- a/minadbd/minadbd_services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -41,10 +41,10 @@
#include "adb.h"
#include "adb_unique_fd.h"
#include "adb_utils.h"
-#include "fdevent.h"
#include "fuse_adb_provider.h"
#include "fuse_sideload.h"
-#include "minadbd_types.h"
+#include "minadbd/types.h"
+#include "recovery_utils/battery_utils.h"
#include "services.h"
#include "sysdeps.h"
@@ -161,7 +161,10 @@ static void RescueInstallHostService(unique_fd sfd, const std::string& args) {
// 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) {
+ constexpr const char* kRescueBatteryLevelProp = "rescue.battery_level";
static const std::set<std::string> kGetpropAllowedProps = {
+ // clang-format off
+ kRescueBatteryLevelProp,
"ro.build.date.utc",
"ro.build.fingerprint",
"ro.build.flavor",
@@ -171,18 +174,28 @@ static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) {
"ro.build.version.incremental",
"ro.product.device",
"ro.product.vendor.device",
+ // clang-format on
};
+
+ auto query_prop = [](const std::string& key) {
+ if (key == kRescueBatteryLevelProp) {
+ auto battery_info = GetBatteryInfo();
+ return std::to_string(battery_info.capacity);
+ }
+ return android::base::GetProperty(key, "");
+ };
+
std::string result;
if (prop.empty()) {
for (const auto& key : kGetpropAllowedProps) {
- auto value = android::base::GetProperty(key, "");
+ auto value = query_prop(key);
if (value.empty()) {
continue;
}
result += "[" + key + "]: [" + value + "]\n";
}
} else if (kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end()) {
- result = android::base::GetProperty(prop, "") + "\n";
+ result = query_prop(prop) + "\n";
}
if (result.empty()) {
result = "\n";
@@ -255,7 +268,7 @@ static void WipeDeviceService(unique_fd fd, const std::string& args) {
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" ||
@@ -268,17 +281,17 @@ 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 (ConsumePrefix(&name, "rescue-wipe:")) {
+ } else if (android::base::ConsumePrefix(&name, "rescue-wipe:")) {
// rescue-wipe:target:<message-size>
std::string args(name);
return create_service_thread("rescue-wipe",
@@ -293,7 +306,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 f87873792..b694a57d1 100644
--- a/minadbd/minadbd_services_test.cpp
+++ b/minadbd/minadbd_services_test.cpp
@@ -35,8 +35,8 @@
#include "adb_io.h"
#include "fuse_adb_provider.h"
#include "fuse_sideload.h"
+#include "minadbd/types.h"
#include "minadbd_services.h"
-#include "minadbd_types.h"
#include "socket.h"
class MinadbdServicesTest : public ::testing::Test {
diff --git a/minui/events.cpp b/minui/events.cpp
index 7d0250e97..87f811225 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -22,6 +22,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
+#include <sys/inotify.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
@@ -33,6 +34,8 @@
#include "minui/minui.h"
+constexpr const char* INPUT_DEV_DIR = "/dev/input";
+
constexpr size_t MAX_DEVICES = 16;
constexpr size_t MAX_MISC_FDS = 16;
@@ -46,6 +49,8 @@ struct FdInfo {
ev_callback cb;
};
+static bool g_allow_touch_inputs = true;
+static ev_callback g_saved_input_cb;
static android::base::unique_fd g_epoll_fd;
static epoll_event g_polled_events[MAX_DEVICES + MAX_MISC_FDS];
static int g_polled_events_count;
@@ -60,6 +65,80 @@ static bool test_bit(size_t bit, unsigned long* array) { // NOLINT
return (array[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
}
+static bool should_add_input_device(int fd, bool allow_touch_inputs) {
+ // Use unsigned long to match ioctl's parameter type.
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
+
+ // Read the evbits of the input device.
+ if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ return false;
+ }
+
+ // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also
+ // allowed if allow_touch_inputs is set.
+ if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
+ if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int inotify_cb(int fd, __unused uint32_t epevents) {
+ if (g_saved_input_cb == nullptr) return -1;
+
+ // The inotify will put one or several complete events.
+ // Should not read part of one event.
+ int event_len_int;
+ int ret = ioctl(fd, FIONREAD, &event_len_int);
+ if (ret != 0) return -1;
+ if (event_len_int < 0) return -1;
+ size_t event_len = event_len_int;
+
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(INPUT_DEV_DIR), closedir);
+ if (!dir) {
+ return -1;
+ }
+
+ std::vector<int8_t> buf(event_len);
+
+ ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf.data(), event_len));
+ if (r != event_len) {
+ return -1;
+ }
+
+ size_t offset = 0;
+ while (offset < event_len) {
+ struct inotify_event* pevent = reinterpret_cast<struct inotify_event*>(buf.data() + offset);
+ if (offset + sizeof(inotify_event) + pevent->len > event_len) {
+ // The pevent->len is too large and buffer will over flow.
+ // In general, should not happen, just make more stable.
+ return -1;
+ }
+ offset += sizeof(inotify_event) + pevent->len;
+
+ pevent->name[pevent->len] = '\0';
+ if (strncmp(pevent->name, "event", 5)) {
+ continue;
+ }
+
+ android::base::unique_fd dfd(openat(dirfd(dir.get()), pevent->name, O_RDONLY));
+ if (dfd == -1) {
+ break;
+ }
+
+ if (!should_add_input_device(dfd, g_allow_touch_inputs)) {
+ continue;
+ }
+
+ // Only add, we assume the user will not plug out and plug in USB device again and again :)
+ ev_add_fd(std::move(dfd), g_saved_input_cb);
+ }
+
+ return 0;
+}
+
int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
g_epoll_fd.reset();
@@ -68,7 +147,16 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
return -1;
}
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/dev/input"), closedir);
+ android::base::unique_fd inotify_fd(inotify_init1(IN_CLOEXEC));
+ if (inotify_fd.get() == -1) {
+ return -1;
+ }
+
+ if (inotify_add_watch(inotify_fd, INPUT_DEV_DIR, IN_CREATE) < 0) {
+ return -1;
+ }
+
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(INPUT_DEV_DIR), closedir);
if (!dir) {
return -1;
}
@@ -80,22 +168,10 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
android::base::unique_fd fd(openat(dirfd(dir.get()), de->d_name, O_RDONLY | O_CLOEXEC));
if (fd == -1) continue;
- // Use unsigned long to match ioctl's parameter type.
- unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
-
- // Read the evbits of the input device.
- if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ if (!should_add_input_device(fd, allow_touch_inputs)) {
continue;
}
- // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also
- // allowed if allow_touch_inputs is set.
- if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
- if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) {
- continue;
- }
- }
-
epoll_event ev;
ev.events = EPOLLIN | EPOLLWAKEUP;
ev.data.ptr = &ev_fdinfo[g_ev_count];
@@ -116,6 +192,11 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
}
g_epoll_fd.reset(epoll_fd.release());
+
+ g_saved_input_cb = input_cb;
+ g_allow_touch_inputs = allow_touch_inputs;
+ ev_add_fd(std::move(inotify_fd), inotify_cb);
+
return 0;
}
@@ -148,6 +229,7 @@ void ev_exit(void) {
}
g_ev_misc_count = 0;
g_ev_dev_count = 0;
+ g_saved_input_cb = nullptr;
g_epoll_fd.reset();
}
@@ -170,13 +252,17 @@ void ev_dispatch(void) {
}
int ev_get_input(int fd, uint32_t epevents, input_event* ev) {
- if (epevents & EPOLLIN) {
- ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev)));
- if (r == sizeof(*ev)) {
- return 0;
- }
+ if (epevents & EPOLLIN) {
+ ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev)));
+ if (r == sizeof(*ev)) {
+ return 0;
}
- return -1;
+ }
+ if (epevents & EPOLLHUP) {
+ // Delete this watch
+ epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
+ }
+ return -1;
}
int ev_sync_key_state(const ev_set_key_callback& set_key_cb) {
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index 4d1f9b2d2..d34da5674 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -209,7 +209,7 @@ void gr_texticon(int x, int y, const GRSurface* icon) {
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
uint32_t r32 = r, g32 = g, b32 = b, a32 = a;
- if (pixel_format == PixelFormat::ABGR || pixel_format == PixelFormat::BGRA) {
+ if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) {
gr_current = (a32 << 24) | (r32 << 16) | (g32 << 8) | b32;
} else {
gr_current = (a32 << 24) | (b32 << 16) | (g32 << 8) | r32;
@@ -348,6 +348,8 @@ int gr_init() {
pixel_format = PixelFormat::ABGR;
} else if (format == "RGBX_8888") {
pixel_format = PixelFormat::RGBX;
+ } else if (format == "ARGB_8888") {
+ pixel_format = PixelFormat::ARGB;
} else if (format == "BGRA_8888") {
pixel_format = PixelFormat::BGRA;
} else {
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp
index 7b2eed15d..95759e382 100644
--- a/minui/graphics_drm.cpp
+++ b/minui/graphics_drm.cpp
@@ -62,6 +62,8 @@ static int drm_format_to_bpp(uint32_t format) {
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_BGRX8888:
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_XRGB8888:
@@ -87,6 +89,8 @@ std::unique_ptr<GRSurfaceDrm> GRSurfaceDrm::Create(int drm_fd, int width, int he
format = DRM_FORMAT_ARGB8888;
} else if (pixel_format == PixelFormat::RGBX) {
format = DRM_FORMAT_XBGR8888;
+ } else if (pixel_format == PixelFormat::ARGB) {
+ format = DRM_FORMAT_BGRA8888;
} else {
format = DRM_FORMAT_RGB565;
}
diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h
index 36bdcf103..163e41dc6 100644
--- a/minui/include/minui/minui.h
+++ b/minui/include/minui/minui.h
@@ -101,6 +101,7 @@ enum class PixelFormat : int {
ABGR = 1,
RGBX = 2,
BGRA = 3,
+ ARGB = 4,
};
// Initializes the graphics backend and loads font file. Returns 0 on success, or -1 on error. Note
diff --git a/minui/resources.cpp b/minui/resources.cpp
index 069a49529..f635acd1a 100644
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -199,7 +199,7 @@ int res_create_display_surface(const char* name, GRSurface** pSurface) {
}
PixelFormat pixel_format = gr_pixel_format();
- if (pixel_format == PixelFormat::ABGR || pixel_format == PixelFormat::BGRA) {
+ if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) {
png_set_bgr(png_ptr);
}
@@ -271,7 +271,7 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps,
surface[i] = created_surface.release();
}
- if (gr_pixel_format() == PixelFormat::ABGR || gr_pixel_format() == PixelFormat::BGRA) {
+ if (gr_pixel_format() == PixelFormat::ARGB || gr_pixel_format() == PixelFormat::BGRA) {
png_set_bgr(png_ptr);
}
@@ -317,7 +317,7 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) {
}
PixelFormat pixel_format = gr_pixel_format();
- if (pixel_format == PixelFormat::ABGR || pixel_format == PixelFormat::BGRA) {
+ if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) {
png_set_bgr(png_ptr);
}
@@ -347,6 +347,10 @@ bool matches_locale(const std::string& prefix, const std::string& locale) {
// match the locale string without the {script} section.
// For instance, prefix == "en" matches locale == "en-US", prefix == "sr-Latn" matches locale
// == "sr-Latn-BA", and prefix == "zh-CN" matches locale == "zh-Hans-CN".
+ if (prefix.empty()) {
+ return false;
+ }
+
if (android::base::StartsWith(locale, prefix)) {
return true;
}
@@ -414,12 +418,18 @@ int res_create_localized_alpha_surface(const char* name,
__unused int len = row[4];
char* loc = reinterpret_cast<char*>(&row[5]);
- if (y + 1 + h >= height || matches_locale(loc, locale)) {
+ // We need to include one additional line for the metadata of the localized image.
+ if (y + 1 + h > height) {
+ printf("Read exceeds the image boundary, y %u, h %d, height %u\n", y, h, height);
+ return -8;
+ }
+
+ if (matches_locale(loc, locale)) {
printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
auto surface = GRSurface::Create(w, h, w, 1);
if (!surface) {
- return -8;
+ return -9;
}
for (int i = 0; i < h; ++i, ++y) {
@@ -428,7 +438,7 @@ int res_create_localized_alpha_surface(const char* name,
}
*pSurface = surface.release();
- break;
+ return 0;
}
for (int i = 0; i < h; ++i, ++y) {
@@ -436,7 +446,7 @@ int res_create_localized_alpha_surface(const char* name,
}
}
- return 0;
+ return -10;
}
void res_free_surface(GRSurface* surface) {
diff --git a/misc_writer/Android.bp b/misc_writer/Android.bp
deleted file mode 100644
index 567143c79..000000000
--- a/misc_writer/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// 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
deleted file mode 100644
index 1d9702ebf..000000000
--- a/misc_writer/misc_writer.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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 0a21731e8..3b3f9cbc4 100644
--- a/otautil/Android.bp
+++ b/otautil/Android.bp
@@ -16,6 +16,7 @@ cc_library_static {
name: "libotautil",
host_supported: true,
+ vendor_available: true,
recovery_available: true,
defaults: [
@@ -24,44 +25,19 @@ 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: [
"include",
],
-
- target: {
- android: {
- srcs: [
- "dirutil.cpp",
- "logging.cpp",
- "mounts.cpp",
- "parse_install_logs.cpp",
- "roots.cpp",
- "sysutil.cpp",
- "thermalutil.cpp",
- ],
-
- include_dirs: [
- "system/vold",
- ],
-
- static_libs: [
- "libfstab",
- ],
-
- shared_libs: [
- "libcutils",
- "libext4_utils",
- "libfs_mgr",
- "libselinux",
- ],
- },
- },
}
diff --git a/fsck_unshare_blocks.h b/otautil/include/otautil/boot_state.h
index 9de8ef9a3..6c877baef 100644
--- a/fsck_unshare_blocks.h
+++ b/otautil/include/otautil/boot_state.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,9 +14,23 @@
* limitations under the License.
*/
-#ifndef _FILESYSTEM_CMDS_H
-#define _FILESYSTEM_CMDS_H
+#pragma once
-bool do_fsck_unshare_blocks();
+#include <string>
+#include <string_view>
-#endif // _FILESYSTEM_CMDS_H
+class BootState {
+ public:
+ BootState(std::string_view reason, std::string_view stage) : reason_(reason), stage_(stage) {}
+
+ std::string reason() const {
+ return reason_;
+ }
+ std::string stage() const {
+ return stage_;
+ }
+
+ private:
+ std::string reason_; // The reason argument provided in "--reason=".
+ std::string stage_; // The current stage, e.g. "1/2".
+};
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/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/sysutil.cpp b/otautil/sysutil.cpp
index 8366fa0ac..6cd46c6a9 100644
--- a/otautil/sysutil.cpp
+++ b/otautil/sysutil.cpp
@@ -38,7 +38,7 @@
BlockMapData BlockMapData::ParseBlockMapFile(const std::string& block_map_path) {
std::string content;
if (!android::base::ReadFileToString(block_map_path, &content)) {
- LOG(ERROR) << "Failed to read " << block_map_path;
+ PLOG(ERROR) << "Failed to read " << block_map_path;
return {};
}
@@ -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-persist.cpp b/recovery-persist.cpp
index 294017a12..ad101ede2 100644
--- a/recovery-persist.cpp
+++ b/recovery-persist.cpp
@@ -35,16 +35,14 @@
#include <string.h>
#include <unistd.h>
-#include <limits>
#include <string>
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <metricslogger/metrics_logger.h>
#include <private/android_logger.h> /* private pmsg functions */
-#include "otautil/logging.h"
-#include "otautil/parse_install_logs.h"
+#include "recovery_utils/logging.h"
+#include "recovery_utils/parse_install_logs.h"
constexpr const char* LAST_LOG_FILE = "/data/misc/recovery/last_log";
constexpr const char* LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0";
@@ -112,20 +110,6 @@ ssize_t logsave(
return android::base::WriteStringToFile(buffer, destination.c_str());
}
-// Parses the LAST_INSTALL file and reports the update metrics saved under recovery mode.
-static void report_metrics_from_last_install(const std::string& file_name) {
- auto metrics = ParseLastInstall(file_name);
- // TODO(xunchang) report the installation result.
- for (const auto& [event, value] : metrics) {
- if (value > std::numeric_limits<int>::max()) {
- LOG(WARNING) << event << " (" << value << ") exceeds integer max.";
- } else {
- LOG(INFO) << "Uploading " << value << " to " << event;
- android::metricslogger::LogHistogram(event, value);
- }
- }
-}
-
int main(int argc, char **argv) {
/* Is /cache a mount?, we have been delivered where we are not wanted */
@@ -157,7 +141,6 @@ int main(int argc, char **argv) {
if (has_cache) {
// Collects and reports the non-a/b update metrics from last_install; and removes the file
// to avoid duplicate report.
- report_metrics_from_last_install(LAST_INSTALL_FILE_IN_CACHE);
if (access(LAST_INSTALL_FILE_IN_CACHE, F_OK) && unlink(LAST_INSTALL_FILE_IN_CACHE) == -1) {
PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE_IN_CACHE;
}
@@ -181,7 +164,6 @@ int main(int argc, char **argv) {
// For those device without /cache, the last_install file has been copied to
// /data/misc/recovery from pmsg. Looks for the sideload history only.
if (!has_cache) {
- report_metrics_from_last_install(LAST_INSTALL_FILE);
if (access(LAST_INSTALL_FILE, F_OK) && unlink(LAST_INSTALL_FILE) == -1) {
PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE;
}
diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp
index d41755d0a..42acd05be 100644
--- a/recovery-refresh.cpp
+++ b/recovery-refresh.cpp
@@ -38,11 +38,12 @@
//
#include <string.h>
+
#include <string>
#include <private/android_logger.h> /* private pmsg functions */
-#include "otautil/logging.h"
+#include "recovery_utils/logging.h"
int main(int argc, char **argv) {
static const char filter[] = "recovery/";
diff --git a/recovery.cpp b/recovery.cpp
index e51687a7e..0382697ab 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,27 +40,27 @@
#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 <healthhalutils/HealthHalUtils.h>
+#include <fs_mgr/roots.h>
#include <ziparchive/zip_archive.h>
-#include "common.h"
-#include "fsck_unshare_blocks.h"
-#include "fuse_sideload.h"
+#include "bootloader_message/bootloader_message.h"
#include "install/adb_install.h"
-#include "install/fuse_sdcard_install.h"
+#include "install/fuse_install.h"
#include "install/install.h"
#include "install/package.h"
+#include "install/snapshot_utils.h"
#include "install/wipe_data.h"
+#include "install/wipe_device.h"
+#include "otautil/boot_state.h"
#include "otautil/error_code.h"
-#include "otautil/logging.h"
#include "otautil/paths.h"
-#include "otautil/roots.h"
#include "otautil/sysutil.h"
#include "recovery_ui/screen_ui.h"
#include "recovery_ui/ui.h"
+#include "recovery_utils/battery_utils.h"
+#include "recovery_utils/logging.h"
+#include "recovery_utils/roots.h"
static constexpr const char* COMMAND_FILE = "/cache/recovery/command";
static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
@@ -71,13 +69,7 @@ static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale";
static constexpr const char* CACHE_ROOT = "/cache";
-// We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed
-// into target_files.zip. Assert the version defined in code and in Android.mk are consistent.
-static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions.");
-
static bool save_current_log = false;
-std::string stage;
-const char* reason = nullptr;
/*
* The recovery tool communicates with the main system through /cache files.
@@ -86,6 +78,8 @@ const char* reason = nullptr;
*
* The arguments which may be supplied in the recovery.command file:
* --update_package=path - verify install an OTA package file
+ * --install_with_fuse - install the update package with FUSE. This allows installation of large
+ * packages on LP32 builds. Since the mmap will otherwise fail due to out of memory.
* --wipe_data - erase user data (and cache), then reboot
* --prompt_and_wipe_data - prompt the user that data is corrupt, with their consent erase user
* data (and cache), then reboot
@@ -106,7 +100,7 @@ const char* reason = nullptr;
* -- after this, rebooting will restart the erase --
* 5. erase_volume() reformats /data
* 6. erase_volume() reformats /cache
- * 7. finish_recovery() erases BCB
+ * 7. FinishRecovery() erases BCB
* -- after this, rebooting will restart the main system --
* 8. main() calls reboot() to boot main system
*
@@ -116,27 +110,27 @@ 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
+ * 6. FinishRecovery() 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
*/
-bool is_ro_debuggable() {
- return android::base::GetBoolProperty("ro.debuggable", false);
+static bool IsRoDebuggable() {
+ return android::base::GetBoolProperty("ro.debuggable", false);
}
// Clear the recovery command and prepare to boot a (hopefully working) system,
// copy our log file to cache as well (for the system to read). This function is
// idempotent: call it as many times as you like.
-static void finish_recovery() {
+static void FinishRecovery(RecoveryUI* ui) {
std::string locale = ui->GetLocale();
// Save the locale to cache, so if recovery is next started up without a '--locale' argument
// (e.g., directly from the bootloader) it will use the last-known locale.
- if (!locale.empty() && has_cache) {
+ if (!locale.empty() && HasCache()) {
LOG(INFO) << "Saving locale \"" << locale << "\"";
if (ensure_path_mounted(LOCALE_FILE) != 0) {
LOG(ERROR) << "Failed to mount " << LOCALE_FILE;
@@ -145,7 +139,7 @@ static void finish_recovery() {
}
}
- copy_logs(save_current_log, has_cache, sehandle);
+ copy_logs(save_current_log);
// Reset to normal system boot so recovery won't cycle indefinitely.
std::string err;
@@ -154,7 +148,7 @@ static void finish_recovery() {
}
// Remove the command file, so recovery won't repeat indefinitely.
- if (has_cache) {
+ if (HasCache()) {
if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
LOG(WARNING) << "Can't unlink " << COMMAND_FILE;
}
@@ -168,7 +162,7 @@ static bool yes_no(Device* device, const char* question1, const char* question2)
std::vector<std::string> headers{ question1, question2 };
std::vector<std::string> items{ " No", " Yes" };
- size_t chosen_item = ui->ShowMenu(
+ size_t chosen_item = device->GetUI()->ShowMenu(
headers, items, 0, true,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
return (chosen_item == 1);
@@ -178,7 +172,7 @@ static bool ask_to_wipe_data(Device* device) {
std::vector<std::string> headers{ "Wipe all user data?", " THIS CAN NOT BE UNDONE!" };
std::vector<std::string> items{ " Cancel", " Factory data reset" };
- size_t chosen_item = ui->ShowPromptWipeDataConfirmationMenu(
+ size_t chosen_item = device->GetUI()->ShowPromptWipeDataConfirmationMenu(
headers, items,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
@@ -200,7 +194,7 @@ static InstallResult prompt_and_wipe_data(Device* device) {
};
// clang-format on
for (;;) {
- size_t chosen_item = ui->ShowPromptWipeDataMenu(
+ size_t chosen_item = device->GetUI()->ShowPromptWipeDataMenu(
wipe_data_menu_headers, wipe_data_menu_items,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
// If ShowMenu() returned RecoveryUI::KeyError::INTERRUPTED, WaitKey() was interrupted.
@@ -212,7 +206,8 @@ static InstallResult prompt_and_wipe_data(Device* device) {
}
if (ask_to_wipe_data(device)) {
- bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
+ CHECK(device->GetReason().has_value());
+ bool convert_fbe = device->GetReason().value() == "convert_fbe";
if (WipeData(device, convert_fbe)) {
return INSTALL_SUCCESS;
} else {
@@ -222,168 +217,9 @@ 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) {
+ if (HasCache()) {
for (int i = 0; i < KEEP_LOG_COUNT; i++) {
auto add_to_entries = [&](const char* filename) {
std::string log_file(filename);
@@ -417,7 +253,7 @@ static void choose_recovery_file(Device* device) {
size_t chosen_item = 0;
while (true) {
- chosen_item = ui->ShowMenu(
+ chosen_item = device->GetUI()->ShowMenu(
headers, entries, chosen_item, true,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
@@ -427,11 +263,11 @@ static void choose_recovery_file(Device* device) {
}
if (entries[chosen_item] == "Back") break;
- ui->ShowFile(entries[chosen_item]);
+ device->GetUI()->ShowFile(entries[chosen_item]);
}
}
-static void run_graphics_test() {
+static void run_graphics_test(RecoveryUI* ui) {
// Switch to graphics screen.
ui->ShowText(false);
@@ -474,14 +310,64 @@ 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) {
+static void WriteUpdateInProgress() {
+ std::string err;
+ if (!update_bootloader_message({ "--reason=update_in_progress" }, &err)) {
+ LOG(ERROR) << "Failed to WriteUpdateInProgress: " << err;
+ }
+}
+
+static bool AskToReboot(Device* device, Device::BuiltinAction chosen_action) {
+ bool is_non_ab = android::base::GetProperty("ro.boot.slot_suffix", "").empty();
+ bool is_virtual_ab = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
+ if (!is_non_ab && !is_virtual_ab) {
+ // Only prompt for non-A/B or Virtual A/B devices.
+ return true;
+ }
+
+ std::string header_text;
+ std::string item_text;
+ switch (chosen_action) {
+ case Device::REBOOT:
+ header_text = "reboot";
+ item_text = " Reboot system now";
+ break;
+ case Device::SHUTDOWN:
+ header_text = "power off";
+ item_text = " Power off";
+ break;
+ default:
+ LOG(FATAL) << "Invalid chosen action " << chosen_action;
+ break;
+ }
+
+ std::vector<std::string> headers{ "WARNING: Previous installation has failed.",
+ " Your device may fail to boot if you " + header_text +
+ " now.",
+ " Confirm reboot?" };
+ std::vector<std::string> items{ " Cancel", item_text };
+
+ size_t chosen_item = device->GetUI()->ShowMenu(
+ headers, items, 0, true /* menu_only */,
+ std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
+
+ return (chosen_item == 1);
+}
+
+// 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) {
+ auto ui = device->GetUI();
+ bool update_in_progress = (device->GetReason().value_or("") == "update_in_progress");
for (;;) {
- finish_recovery();
+ FinishRecovery(ui);
switch (status) {
case INSTALL_SUCCESS:
case INSTALL_NONE:
+ case INSTALL_SKIPPED:
+ case INSTALL_RETRY:
+ case INSTALL_KEY_INTERRUPTED:
ui->SetBackground(RecoveryUI::NO_COMMAND);
break;
@@ -489,11 +375,23 @@ 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);
+ std::vector<std::string> headers;
+ if (update_in_progress) {
+ headers = { "WARNING: Previous installation has failed.",
+ " Your device may fail to boot if you reboot or power off now." };
+ }
+
size_t chosen_item = ui->ShowMenu(
- {}, device->GetMenuItems(), 0, false,
+ headers, device->GetMenuItems(), 0, false,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
// Handle Interrupt key
if (chosen_item == static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED)) {
@@ -507,19 +405,34 @@ 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;
case Device::ENTER_FASTBOOT:
case Device::ENTER_RECOVERY:
- case Device::REBOOT:
case Device::REBOOT_BOOTLOADER:
case Device::REBOOT_FASTBOOT:
case Device::REBOOT_RECOVERY:
case Device::REBOOT_RESCUE:
- case Device::SHUTDOWN:
return chosen_action;
+ case Device::REBOOT:
+ case Device::SHUTDOWN:
+ if (!ui->IsTextVisible()) {
+ return Device::REBOOT;
+ }
+ // okay to reboot; no need to ask.
+ if (!update_in_progress) {
+ return Device::REBOOT;
+ }
+ // An update might have been failed. Ask if user really wants to reboot.
+ if (AskToReboot(device, chosen_action)) {
+ return Device::REBOOT;
+ }
+ break;
+
case Device::WIPE_DATA:
save_current_log = true;
if (ui->IsTextVisible()) {
@@ -547,6 +460,9 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
case Device::ENTER_RESCUE: {
save_current_log = true;
+ update_in_progress = true;
+ WriteUpdateInProgress();
+
bool adb = true;
Device::BuiltinAction reboot_action;
if (chosen_action == Device::ENTER_RESCUE) {
@@ -557,7 +473,7 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
status = ApplyFromAdb(device, false /* rescue_mode */, &reboot_action);
} else {
adb = false;
- status = ApplyFromSdcard(device, ui);
+ status = ApplyFromSdcard(device);
}
ui->Print("\nInstall from %s completed with status %d.\n", adb ? "ADB" : "SD card", status);
@@ -565,12 +481,15 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
return reboot_action;
}
- if (status != INSTALL_SUCCESS) {
+ if (status == INSTALL_SUCCESS) {
+ update_in_progress = false;
+ if (!ui->IsTextVisible()) {
+ return Device::NO_ACTION; // reboot if logs aren't visible
+ }
+ } else {
ui->SetBackground(RecoveryUI::ERROR);
ui->Print("Installation aborted.\n");
- copy_logs(save_current_log, has_cache, sehandle);
- } else if (!ui->IsTextVisible()) {
- return Device::NO_ACTION; // reboot if logs aren't visible
+ copy_logs(save_current_log);
}
break;
}
@@ -580,7 +499,7 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
break;
case Device::RUN_GRAPHICS_TEST:
- run_graphics_test();
+ run_graphics_test(ui);
break;
case Device::RUN_LOCALE_TEST: {
@@ -588,9 +507,14 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
screen_ui->CheckBackgroundTextImages();
break;
}
+
case Device::MOUNT_SYSTEM:
- // the system partition is mounted at /mnt/system
- if (ensure_path_mounted_at(get_system_root(), "/mnt/system") != -1) {
+ // For Virtual A/B, set up the snapshot devices (if exist).
+ if (!CreateSnapshotPartitions()) {
+ ui->Print("Virtual A/B: snapshot partitions creation failed.\n");
+ break;
+ }
+ if (ensure_path_mounted_at(android::fs_mgr::GetSystemRoot(), "/mnt/system") != -1) {
ui->Print("Mounted /system.\n");
}
break;
@@ -605,74 +529,17 @@ static void print_property(const char* key, const char* name, void* /* cookie */
printf("%s=%s\n", key, name);
}
-static bool is_battery_ok(int* required_battery_level) {
- using android::hardware::health::V1_0::BatteryStatus;
- using android::hardware::health::V2_0::get_health_service;
- using android::hardware::health::V2_0::IHealth;
- using android::hardware::health::V2_0::Result;
- using android::hardware::health::V2_0::toString;
-
- android::sp<IHealth> health = get_health_service();
-
- static constexpr int BATTERY_READ_TIMEOUT_IN_SEC = 10;
- int wait_second = 0;
- while (true) {
- auto charge_status = BatteryStatus::UNKNOWN;
-
- if (health == nullptr) {
- LOG(WARNING) << "no health implementation is found, assuming defaults";
- } else {
- health
- ->getChargeStatus([&charge_status](auto res, auto out_status) {
- if (res == Result::SUCCESS) {
- charge_status = out_status;
- }
- })
- .isOk(); // should not have transport error
- }
-
- // Treat unknown status as charged.
- bool charged = (charge_status != BatteryStatus::DISCHARGING &&
- charge_status != BatteryStatus::NOT_CHARGING);
-
- Result res = Result::UNKNOWN;
- int32_t capacity = INT32_MIN;
- if (health != nullptr) {
- health
- ->getCapacity([&res, &capacity](auto out_res, auto out_capacity) {
- res = out_res;
- capacity = out_capacity;
- })
- .isOk(); // should not have transport error
- }
-
- LOG(INFO) << "charge_status " << toString(charge_status) << ", charged " << charged
- << ", status " << toString(res) << ", capacity " << capacity;
- // At startup, the battery drivers in devices like N5X/N6P take some time to load
- // the battery profile. Before the load finishes, it reports value 50 as a fake
- // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected
- // to finish loading the battery profile earlier than 10 seconds after kernel startup.
- if (res == Result::SUCCESS && capacity == 50) {
- if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) {
- sleep(1);
- wait_second++;
- continue;
- }
- }
- // If we can't read battery percentage, it may be a device without battery. In this
- // situation, use 100 as a fake battery percentage.
- if (res != Result::SUCCESS) {
- capacity = 100;
- }
-
- // GmsCore enters recovery mode to install package when having enough battery percentage.
- // Normally, the threshold is 40% without charger and 20% with charger. So we should check
- // battery with a slightly lower limitation.
- static constexpr int BATTERY_OK_PERCENTAGE = 20;
- static constexpr int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15;
- *required_battery_level = charged ? BATTERY_WITH_CHARGER_OK_PERCENTAGE : BATTERY_OK_PERCENTAGE;
- return capacity >= *required_battery_level;
- }
+static bool IsBatteryOk(int* required_battery_level) {
+ // GmsCore enters recovery mode to install package when having enough battery percentage.
+ // Normally, the threshold is 40% without charger and 20% with charger. So we check the battery
+ // level against a slightly lower limit.
+ constexpr int BATTERY_OK_PERCENTAGE = 20;
+ constexpr int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15;
+
+ auto battery_info = GetBatteryInfo();
+ *required_battery_level =
+ battery_info.charging ? BATTERY_WITH_CHARGER_OK_PERCENTAGE : BATTERY_OK_PERCENTAGE;
+ return battery_info.capacity >= *required_battery_level;
}
// Set the retry count to |retry_count| in BCB.
@@ -726,7 +593,7 @@ static void log_failure_code(ErrorCode code, const std::string& update_package)
Device::BuiltinAction start_recovery(Device* device, const std::vector<std::string>& args) {
static constexpr struct option OPTIONS[] = {
{ "fastboot", no_argument, nullptr, 0 },
- { "fsck_unshare_blocks", no_argument, nullptr, 0 },
+ { "install_with_fuse", no_argument, nullptr, 0 },
{ "just_exit", no_argument, nullptr, 'x' },
{ "locale", required_argument, nullptr, 0 },
{ "prompt_and_wipe_data", no_argument, nullptr, 0 },
@@ -747,6 +614,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
};
const char* update_package = nullptr;
+ bool install_with_fuse = false; // memory map the update package by default.
bool should_wipe_data = false;
bool should_prompt_and_wipe_data = false;
bool should_wipe_cache = false;
@@ -757,7 +625,6 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
bool rescue = false;
bool just_exit = false;
bool shutdown_after = false;
- bool fsck_unshare_blocks = false;
int retry_count = 0;
bool security_update = false;
std::string locale;
@@ -780,14 +647,12 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
break;
case 0: {
std::string option = OPTIONS[option_index].name;
- if (option == "fsck_unshare_blocks") {
- fsck_unshare_blocks = true;
- } else if (option == "locale" || option == "fastboot") {
+ if (option == "install_with_fuse") {
+ install_with_fuse = true;
+ } else if (option == "locale" || option == "fastboot" || option == "reason") {
// Handled in recovery_main.cpp
} else if (option == "prompt_and_wipe_data") {
should_prompt_and_wipe_data = true;
- } else if (option == "reason") {
- reason = optarg;
} else if (option == "rescue") {
rescue = true;
} else if (option == "retry_count") {
@@ -821,15 +686,18 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
}
optind = 1;
- printf("stage is [%s]\n", stage.c_str());
- printf("reason is [%s]\n", reason);
+ printf("stage is [%s]\n", device->GetStage().value_or("").c_str());
+ printf("reason is [%s]\n", device->GetReason().value_or("").c_str());
+
+ auto ui = device->GetUI();
// Set background string to "installing security update" for security update,
// otherwise set it to "installing system update".
ui->SetSystemUpdateText(security_update);
int st_cur, st_max;
- if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) {
+ if (!device->GetStage().has_value() &&
+ sscanf(device->GetStage().value().c_str(), "%d/%d", &st_cur, &st_max) == 2) {
ui->SetStage(st_cur, st_max);
}
@@ -850,9 +718,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
property_list(print_property, nullptr);
printf("\n");
- 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;
@@ -862,12 +728,10 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
// to log the update attempt since update_package is non-NULL.
save_current_log = true;
- int required_battery_level;
- if (retry_count == 0 && !is_battery_ok(&required_battery_level)) {
+ if (int required_battery_level; retry_count == 0 && !IsBatteryOk(&required_battery_level)) {
ui->Print("battery capacity is not enough for installing package: %d%% needed\n",
required_battery_level);
- // Log the error code to last_install when installation skips due to
- // low battery.
+ // Log the error code to last_install when installation skips due to low battery.
log_failure_code(kLowBattery, update_package);
status = INSTALL_SKIPPED;
} else if (retry_count == 0 && bootreason_in_blacklist()) {
@@ -886,25 +750,23 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
if (!SetupPackageMount(update_package, &should_use_fuse)) {
LOG(INFO) << "Failed to set up the package access, skipping installation";
status = INSTALL_ERROR;
- } else if (should_use_fuse) {
+ } else if (install_with_fuse || should_use_fuse) {
LOG(INFO) << "Installing package " << update_package << " with fuse";
- auto file_data_reader = std::make_unique<FuseFileDataProvider>(update_package, 65536);
- status = run_fuse_sideload(std::move(file_data_reader));
+ status = InstallWithFuseFromPath(update_package, ui);
} else if (auto memory_package = Package::CreateMemoryPackage(
update_package,
std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
memory_package != nullptr) {
- status = install_package(update_package, should_wipe_cache, true, retry_count, ui);
+ status = InstallPackage(memory_package.get(), update_package, should_wipe_cache,
+ retry_count, ui);
} else {
// We may fail to memory map the package on 32 bit builds for packages with 2GiB+ size.
// In such cases, we will try to install the package with fuse. This is not the default
// installation method because it introduces a layer of indirection from the kernel space.
LOG(WARNING) << "Failed to memory map package " << update_package
<< "; falling back to install with fuse";
- auto file_data_reader = std::make_unique<FuseFileDataProvider>(update_package, 65536);
- status = run_fuse_sideload(std::move(file_data_reader));
+ status = InstallWithFuseFromPath(update_package, ui);
}
-
if (status != INSTALL_SUCCESS) {
ui->Print("Installation aborted.\n");
@@ -912,14 +774,14 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
// RETRY_LIMIT times before we abandon this OTA update.
static constexpr int RETRY_LIMIT = 4;
if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) {
- copy_logs(save_current_log, has_cache, sehandle);
+ copy_logs(save_current_log);
retry_count += 1;
set_retry_bootloader_message(retry_count, args);
// 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) {
@@ -930,14 +792,15 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
// If this is an eng or userdebug build, then automatically
// turn the text display on if the script fails so the error
// message is visible.
- if (is_ro_debuggable()) {
+ if (IsRoDebuggable()) {
ui->ShowText(true);
}
}
}
} else if (should_wipe_data) {
save_current_log = true;
- bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
+ CHECK(device->GetReason().has_value());
+ bool convert_fbe = device->GetReason().value() == "convert_fbe";
if (!WipeData(device, convert_fbe)) {
status = INSTALL_ERROR;
}
@@ -957,7 +820,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) {
@@ -979,15 +842,11 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
save_current_log = true;
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()) {
- status = INSTALL_ERROR;
- }
} else if (!just_exit) {
// If this is an eng or userdebug build, automatically turn on the text display if no command
// is specified. Note that this should be called before setting the background to avoid
// flickering the background image.
- if (is_ro_debuggable()) {
+ if (IsRoDebuggable()) {
ui->ShowText(true);
}
status = INSTALL_NONE; // No command specified
@@ -1012,7 +871,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;
}
@@ -1020,7 +879,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
}
// Save logs and clean up before rebooting or shutting down.
- finish_recovery();
+ FinishRecovery(ui);
return next_action;
}
diff --git a/recovery_main.cpp b/recovery_main.cpp
index de8ac1f42..30a1fc0a4 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -41,34 +41,37 @@
#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>
#include <selinux/selinux.h>
-#include "common.h"
#include "fastboot/fastboot.h"
#include "install/wipe_data.h"
-#include "otautil/logging.h"
+#include "otautil/boot_state.h"
#include "otautil/paths.h"
-#include "otautil/roots.h"
#include "otautil/sysutil.h"
#include "recovery.h"
#include "recovery_ui/device.h"
#include "recovery_ui/stub_ui.h"
#include "recovery_ui/ui.h"
+#include "recovery_utils/logging.h"
+#include "recovery_utils/roots.h"
static constexpr const char* COMMAND_FILE = "/cache/recovery/command";
static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale";
-static constexpr const char* CACHE_ROOT = "/cache";
+static RecoveryUI* ui = nullptr;
-bool has_cache = false;
+static bool IsRoDebuggable() {
+ return android::base::GetBoolProperty("ro.debuggable", false);
+}
-RecoveryUI* ui = nullptr;
-struct selabel_handle* sehandle;
+static bool IsDeviceUnlocked() {
+ return "orange" == android::base::GetProperty("ro.boot.verifiedbootstate", "");
+}
static void UiLogger(android::base::LogId /* id */, android::base::LogSeverity severity,
const char* /* tag */, const char* /* file */, unsigned int /* line */,
@@ -81,11 +84,12 @@ static void UiLogger(android::base::LogId /* id */, android::base::LogSeverity s
}
}
+// Parses the command line argument from various sources; and reads the stage field from BCB.
// command line args come from, in decreasing precedence:
// - the actual command line
// - the bootloader control block (one per line, after "recovery")
// - the contents of COMMAND_FILE (one per line)
-static std::vector<std::string> get_args(const int argc, char** const argv) {
+static std::vector<std::string> get_args(const int argc, char** const argv, std::string* stage) {
CHECK_GT(argc, 0);
bootloader_message boot = {};
@@ -95,7 +99,9 @@ static std::vector<std::string> get_args(const int argc, char** const argv) {
// If fails, leave a zeroed bootloader_message.
boot = {};
}
- stage = std::string(boot.stage);
+ if (stage) {
+ *stage = std::string(boot.stage);
+ }
std::string boot_command;
if (boot.command[0] != 0) {
@@ -131,7 +137,7 @@ static std::vector<std::string> get_args(const int argc, char** const argv) {
}
// --- if that doesn't work, try the command file (if we have /cache).
- if (args.size() == 1 && has_cache) {
+ if (args.size() == 1 && HasCache()) {
std::string content;
if (ensure_path_mounted(COMMAND_FILE) == 0 &&
android::base::ReadFileToString(COMMAND_FILE, &content)) {
@@ -148,7 +154,7 @@ static std::vector<std::string> get_args(const int argc, char** const argv) {
// Write the arguments (excluding the filename in args[0]) back into the
// bootloader control block. So the device will always boot into recovery to
- // finish the pending work, until finish_recovery() is called.
+ // finish the pending work, until FinishRecovery() is called.
std::vector<std::string> options(args.cbegin() + 1, args.cend());
if (!update_bootloader_message(options, &err)) {
LOG(ERROR) << "Failed to set BCB message: " << err;
@@ -331,14 +337,15 @@ int main(int argc, char** argv) {
redirect_stdio(Paths::Get().temporary_log_file().c_str());
load_volume_table();
- has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr;
- std::vector<std::string> args = get_args(argc, argv);
+ std::string stage;
+ std::vector<std::string> args = get_args(argc, argv, &stage);
auto args_to_parse = StringVectorToNullTerminatedArray(args);
static constexpr struct option OPTIONS[] = {
{ "fastboot", no_argument, nullptr, 0 },
{ "locale", required_argument, nullptr, 0 },
+ { "reason", required_argument, nullptr, 0 },
{ "show_text", no_argument, nullptr, 't' },
{ nullptr, 0, nullptr, 0 },
};
@@ -346,6 +353,13 @@ int main(int argc, char** argv) {
bool show_text = false;
bool fastboot = false;
std::string locale;
+ std::string reason;
+
+ // The code here is only interested in the options that signal the intent to start fastbootd or
+ // recovery. Unrecognized options are likely meant for recovery, which will be processed later in
+ // start_recovery(). Suppress the warnings for such -- even if some flags were indeed invalid, the
+ // code in start_recovery() will capture and report them.
+ opterr = 0;
int arg;
int option_index;
@@ -359,6 +373,8 @@ int main(int argc, char** argv) {
std::string option = OPTIONS[option_index].name;
if (option == "locale") {
locale = optarg;
+ } else if (option == "reason") {
+ reason = optarg;
} else if (option == "fastboot" &&
android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
fastboot = true;
@@ -368,14 +384,14 @@ int main(int argc, char** argv) {
}
}
optind = 1;
+ opterr = 1;
if (locale.empty()) {
- if (has_cache) {
+ if (HasCache()) {
locale = load_locale_from_cache();
}
if (locale.empty()) {
- static constexpr const char* DEFAULT_LOCALE = "en-US";
locale = DEFAULT_LOCALE;
}
}
@@ -415,9 +431,12 @@ int main(int argc, char** argv) {
device->ResetUI(new StubRecoveryUI());
}
}
+
+ BootState boot_state(reason, stage); // recovery_main owns the state of boot.
+ device->SetBootState(&boot_state);
ui = device->GetUI();
- if (!has_cache) {
+ if (!HasCache()) {
device->RemoveMenuItemForAction(Device::WIPE_CACHE);
}
@@ -425,7 +444,7 @@ int main(int argc, char** argv) {
device->RemoveMenuItemForAction(Device::ENTER_FASTBOOT);
}
- if (!is_ro_debuggable()) {
+ if (!IsRoDebuggable()) {
device->RemoveMenuItemForAction(Device::ENTER_RESCUE);
}
@@ -435,7 +454,7 @@ int main(int argc, char** argv) {
LOG(INFO) << "Starting recovery (pid " << getpid() << ") on " << ctime(&start);
LOG(INFO) << "locale is [" << locale << "]";
- sehandle = selinux_android_file_context_handle();
+ auto sehandle = selinux_android_file_context_handle();
selinux_android_set_sehandle(sehandle);
if (!sehandle) {
ui->Print("Warning: No file_contexts\n");
@@ -448,7 +467,9 @@ int main(int argc, char** argv) {
listener_thread.detach();
while (true) {
- std::string usb_config = fastboot ? "fastboot" : is_ro_debuggable() ? "adb" : "none";
+ // We start adbd in recovery for the device with userdebug build or a unlocked bootloader.
+ std::string usb_config =
+ fastboot ? "fastboot" : IsRoDebuggable() || IsDeviceUnlocked() ? "adb" : "none";
std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
if (usb_config != usb_state) {
if (!SetUsbConfig("none")) {
@@ -472,27 +493,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));
@@ -503,14 +528,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;
@@ -522,9 +547,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/Android.bp b/recovery_ui/Android.bp
index ee3149d5e..149ef8acc 100644
--- a/recovery_ui/Android.bp
+++ b/recovery_ui/Android.bp
@@ -23,6 +23,7 @@ cc_library {
srcs: [
"device.cpp",
"screen_ui.cpp",
+ "stub_ui.cpp",
"ui.cpp",
"vr_ui.cpp",
"wear_ui.cpp",
diff --git a/recovery_ui/device.cpp b/recovery_ui/device.cpp
index e7ae1a3e1..d46df92d3 100644
--- a/recovery_ui/device.cpp
+++ b/recovery_ui/device.cpp
@@ -23,6 +23,7 @@
#include <android-base/logging.h>
+#include "otautil/boot_state.h"
#include "recovery_ui/ui.h"
static std::vector<std::pair<std::string, Device::BuiltinAction>> g_menu_actions{
@@ -95,3 +96,15 @@ int Device::HandleMenuKey(int key, bool visible) {
return ui_->HasThreeButtons() ? kNoAction : kHighlightDown;
}
}
+
+void Device::SetBootState(const BootState* state) {
+ boot_state_ = state;
+}
+
+std::optional<std::string> Device::GetReason() const {
+ return boot_state_ ? std::make_optional(boot_state_->reason()) : std::nullopt;
+}
+
+std::optional<std::string> Device::GetStage() const {
+ return boot_state_ ? std::make_optional(boot_state_->stage()) : std::nullopt;
+}
diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h
index 7c76cdb0a..f4f993638 100644
--- a/recovery_ui/include/recovery_ui/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -20,12 +20,15 @@
#include <stddef.h>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
// Forward declaration to avoid including "ui.h".
class RecoveryUI;
+class BootState;
+
class Device {
public:
static constexpr const int kNoAction = -1;
@@ -58,6 +61,8 @@ class Device {
REBOOT_FASTBOOT = 17,
REBOOT_RECOVERY = 18,
REBOOT_RESCUE = 19,
+ REBOOT_FROM_FASTBOOT = 20,
+ SHUTDOWN_FROM_FASTBOOT = 21,
};
explicit Device(RecoveryUI* ui);
@@ -124,9 +129,16 @@ class Device {
return true;
}
+ void SetBootState(const BootState* state);
+ // The getters for reason and stage may return std::nullopt until StartRecovery() is called. It's
+ // the caller's responsibility to perform the check and handle the exception.
+ std::optional<std::string> GetReason() const;
+ std::optional<std::string> GetStage() const;
+
private:
// The RecoveryUI object that should be used to display the user interface for this device.
std::unique_ptr<RecoveryUI> ui_;
+ const BootState* boot_state_{ nullptr };
};
// Disable name mangling, as this function will be loaded via dlsym(3).
diff --git a/recovery_ui/include/recovery_ui/screen_ui.h b/recovery_ui/include/recovery_ui/screen_ui.h
index 5cda2a2e5..92b3c2546 100644
--- a/recovery_ui/include/recovery_ui/screen_ui.h
+++ b/recovery_ui/include/recovery_ui/screen_ui.h
@@ -286,6 +286,9 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface {
// selected.
virtual int SelectMenu(int sel);
+ // Returns the help message displayed on top of the menu.
+ virtual std::vector<std::string> GetMenuHelpMessage() const;
+
virtual void draw_background_locked();
virtual void draw_foreground_locked();
virtual void draw_screen_locked();
diff --git a/recovery_ui/include/recovery_ui/stub_ui.h b/recovery_ui/include/recovery_ui/stub_ui.h
index fb1d8c7a6..511b1314a 100644
--- a/recovery_ui/include/recovery_ui/stub_ui.h
+++ b/recovery_ui/include/recovery_ui/stub_ui.h
@@ -62,11 +62,9 @@ class StubRecoveryUI : public RecoveryUI {
// menu display
size_t ShowMenu(const std::vector<std::string>& /* headers */,
- const std::vector<std::string>& /* items */, size_t initial_selection,
+ const std::vector<std::string>& /* items */, size_t /* initial_selection */,
bool /* menu_only */,
- const std::function<int(int, bool)>& /* key_handler */) override {
- return initial_selection;
- }
+ const std::function<int(int, bool)>& /* key_handler */) override;
size_t ShowPromptWipeDataMenu(const std::vector<std::string>& /* backup_headers */,
const std::vector<std::string>& /* backup_items */,
diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h
index d55322cf0..08ec1d76a 100644
--- a/recovery_ui/include/recovery_ui/ui.h
+++ b/recovery_ui/include/recovery_ui/ui.h
@@ -27,6 +27,8 @@
#include <thread>
#include <vector>
+static constexpr const char* DEFAULT_LOCALE = "en-US";
+
// Abstract class for controlling the user interface during recovery.
class RecoveryUI {
public:
@@ -116,7 +118,7 @@ class RecoveryUI {
// Returns true if you have the volume up/down and power trio typical of phones and tablets, false
// otherwise.
- virtual bool HasThreeButtons();
+ virtual bool HasThreeButtons() const;
// Returns true if it has a power key.
virtual bool HasPowerKey() const;
@@ -228,20 +230,23 @@ class RecoveryUI {
bool InitScreensaver();
void SetScreensaverState(ScreensaverState state);
+
// Key event input queue
std::mutex key_queue_mutex;
std::condition_variable key_queue_cond;
bool key_interrupted_;
int key_queue[256], key_queue_len;
- char key_pressed[KEY_MAX + 1]; // under key_queue_mutex
- int key_last_down; // under key_queue_mutex
- bool key_long_press; // under key_queue_mutex
- int key_down_count; // under key_queue_mutex
- bool enable_reboot; // under key_queue_mutex
- int rel_sum;
+ // key press events
+ std::mutex key_press_mutex;
+ char key_pressed[KEY_MAX + 1];
+ int key_last_down;
+ bool key_long_press;
+ int key_down_count;
+ bool enable_reboot;
+
+ int rel_sum;
int consecutive_power_keys;
- int last_key;
bool has_power_key;
bool has_up_key;
diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp
index 870db621c..087fc0e84 100644
--- a/recovery_ui/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -673,6 +673,19 @@ void ScreenRecoveryUI::SetTitle(const std::vector<std::string>& lines) {
title_lines_ = lines;
}
+std::vector<std::string> ScreenRecoveryUI::GetMenuHelpMessage() const {
+ // clang-format off
+ static std::vector<std::string> REGULAR_HELP{
+ "Use volume up/down and power.",
+ };
+ static std::vector<std::string> LONG_PRESS_HELP{
+ "Any button cycles highlight.",
+ "Long-press activates.",
+ };
+ // clang-format on
+ return HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP;
+}
+
// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
// locked.
void ScreenRecoveryUI::draw_screen_locked() {
@@ -685,16 +698,7 @@ void ScreenRecoveryUI::draw_screen_locked() {
gr_color(0, 0, 0, 255);
gr_clear();
- // clang-format off
- static std::vector<std::string> REGULAR_HELP{
- "Use volume up/down and power.",
- };
- static std::vector<std::string> LONG_PRESS_HELP{
- "Any button cycles highlight.",
- "Long-press activates.",
- };
- // clang-format on
- draw_menu_and_text_buffer_locked(HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
+ draw_menu_and_text_buffer_locked(GetMenuHelpMessage());
}
// Draws the menu and text buffer on the screen. Should only be called with updateMutex locked.
@@ -817,12 +821,22 @@ std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadBitmap(const std::string& filen
std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
GRSurface* surface;
- if (auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
- result < 0) {
- LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
- return nullptr;
+ auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
+ if (result == 0) {
+ return std::unique_ptr<GRSurface>(surface);
}
- return std::unique_ptr<GRSurface>(surface);
+ // TODO(xunchang) create a error code enum to refine the retry condition.
+ LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
+ << result << "). Falling back to use default locale.";
+
+ result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
+ if (result == 0) {
+ return std::unique_ptr<GRSurface>(surface);
+ }
+
+ LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
+ << " (error " << result << ")";
+ return nullptr;
}
static char** Alloc2d(size_t rows, size_t cols) {
@@ -1253,7 +1267,7 @@ size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
return initial_selection;
}
- return ShowMenu(CreateMenu(headers, items, initial_selection), menu_only, key_handler);
+ return ShowMenu(std::move(menu), menu_only, key_handler);
}
size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
diff --git a/recovery_ui/stub_ui.cpp b/recovery_ui/stub_ui.cpp
new file mode 100644
index 000000000..a56b3f725
--- /dev/null
+++ b/recovery_ui/stub_ui.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "recovery_ui/stub_ui.h"
+
+#include <android-base/logging.h>
+
+#include "recovery_ui/device.h"
+
+size_t StubRecoveryUI::ShowMenu(const std::vector<std::string>& /* headers */,
+ const std::vector<std::string>& /* items */,
+ size_t /* initial_selection */, bool /* menu_only */,
+ const std::function<int(int, bool)>& /*key_handler*/) {
+ while (true) {
+ int key = WaitKey();
+ // Exit the loop in the case of interruption or time out.
+ if (key == static_cast<int>(KeyError::INTERRUPTED) ||
+ key == static_cast<int>(KeyError::TIMED_OUT)) {
+ return static_cast<size_t>(key);
+ }
+ }
+ LOG(FATAL) << "Unreachable key selected in ShowMenu of stub UI";
+}
diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp
index b7107ff21..6f5cbbca6 100644
--- a/recovery_ui/ui.cpp
+++ b/recovery_ui/ui.cpp
@@ -70,7 +70,6 @@ RecoveryUI::RecoveryUI()
key_down_count(0),
enable_reboot(true),
consecutive_power_keys(0),
- last_key(-1),
has_power_key(false),
has_up_key(false),
has_down_key(false),
@@ -346,7 +345,7 @@ void RecoveryUI::ProcessKey(int key_code, int updown) {
bool long_press = false;
{
- std::lock_guard<std::mutex> lg(key_queue_mutex);
+ std::lock_guard<std::mutex> lg(key_press_mutex);
key_pressed[key_code] = updown;
if (updown) {
++key_down_count;
@@ -375,7 +374,7 @@ void RecoveryUI::ProcessKey(int key_code, int updown) {
case RecoveryUI::REBOOT:
if (reboot_enabled) {
- reboot("reboot,");
+ Reboot("userrequested,recovery,ui");
while (true) {
pause();
}
@@ -393,7 +392,7 @@ void RecoveryUI::TimeKey(int key_code, int count) {
std::this_thread::sleep_for(750ms); // 750 ms == "long"
bool long_press = false;
{
- std::lock_guard<std::mutex> lg(key_queue_mutex);
+ std::lock_guard<std::mutex> lg(key_press_mutex);
if (key_last_down == key_code && key_down_count == count) {
long_press = key_long_press = true;
}
@@ -419,7 +418,7 @@ void RecoveryUI::SetScreensaverState(ScreensaverState state) {
LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_
<< "%)";
} else {
- LOG(ERROR) << "Unable to set brightness to normal";
+ LOG(WARNING) << "Unable to set brightness to normal";
}
break;
case ScreensaverState::DIMMED:
@@ -429,7 +428,7 @@ void RecoveryUI::SetScreensaverState(ScreensaverState state) {
<< "%)";
screensaver_state_ = ScreensaverState::DIMMED;
} else {
- LOG(ERROR) << "Unable to set brightness to dim";
+ LOG(WARNING) << "Unable to set brightness to dim";
}
break;
case ScreensaverState::OFF:
@@ -437,7 +436,7 @@ void RecoveryUI::SetScreensaverState(ScreensaverState state) {
LOG(INFO) << "Brightness: 0 (off)";
screensaver_state_ = ScreensaverState::OFF;
} else {
- LOG(ERROR) << "Unable to set brightness to off";
+ LOG(WARNING) << "Unable to set brightness to off";
}
break;
default:
@@ -518,18 +517,18 @@ bool RecoveryUI::IsUsbConnected() {
}
bool RecoveryUI::IsKeyPressed(int key) {
- std::lock_guard<std::mutex> lg(key_queue_mutex);
+ std::lock_guard<std::mutex> lg(key_press_mutex);
int pressed = key_pressed[key];
return pressed;
}
bool RecoveryUI::IsLongPress() {
- std::lock_guard<std::mutex> lg(key_queue_mutex);
+ std::lock_guard<std::mutex> lg(key_press_mutex);
bool result = key_long_press;
return result;
}
-bool RecoveryUI::HasThreeButtons() {
+bool RecoveryUI::HasThreeButtons() const {
return has_power_key && has_up_key && has_down_key;
}
@@ -548,7 +547,7 @@ void RecoveryUI::FlushKeys() {
RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) {
{
- std::lock_guard<std::mutex> lg(key_queue_mutex);
+ std::lock_guard<std::mutex> lg(key_press_mutex);
key_long_press = false;
}
@@ -585,13 +584,12 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) {
consecutive_power_keys = 0;
}
- last_key = key;
return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
}
void RecoveryUI::KeyLongPress(int) {}
void RecoveryUI::SetEnableReboot(bool enabled) {
- std::lock_guard<std::mutex> lg(key_queue_mutex);
+ std::lock_guard<std::mutex> lg(key_press_mutex);
enable_reboot = enabled;
}
diff --git a/recovery_utils/Android.bp b/recovery_utils/Android.bp
new file mode 100644
index 000000000..bf79a2e87
--- /dev/null
+++ b/recovery_utils/Android.bp
@@ -0,0 +1,81 @@
+// 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_defaults {
+ name: "librecovery_utils_defaults",
+
+ defaults: [
+ "recovery_defaults",
+ ],
+
+ shared_libs: [
+ "android.hardware.health@2.0",
+ "libbase",
+ "libext4_utils",
+ "libfs_mgr",
+ "libhidlbase",
+ "libselinux",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libotautil",
+
+ // External dependencies.
+ "libfstab",
+ "libhealthhalutils",
+ ],
+}
+
+// A utility lib that's local to recovery (in contrast, libotautil is exposed to device-specific
+// recovery_ui lib as well as device-specific updater).
+cc_library_static {
+ name: "librecovery_utils",
+
+ recovery_available: true,
+
+ defaults: [
+ "librecovery_utils_defaults",
+ ],
+
+ srcs: [
+ "battery_utils.cpp",
+ "logging.cpp",
+ "parse_install_logs.cpp",
+ "roots.cpp",
+ "thermalutil.cpp",
+ ],
+
+ header_libs: [
+ "libvold_headers",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ export_static_lib_headers: [
+ // roots.h includes <fstab/fstab.h>.
+ "libfstab",
+ ],
+
+ // Should avoid exposing to the libs that might be used in device-specific codes (e.g.
+ // libedify, libotautil, librecovery_ui).
+ visibility: [
+ "//bootable/recovery",
+ "//bootable/recovery/install",
+ "//bootable/recovery/minadbd",
+ "//bootable/recovery/tests",
+ ],
+}
diff --git a/recovery_utils/battery_utils.cpp b/recovery_utils/battery_utils.cpp
new file mode 100644
index 000000000..323f52537
--- /dev/null
+++ b/recovery_utils/battery_utils.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "recovery_utils/battery_utils.h"
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <healthhalutils/HealthHalUtils.h>
+
+BatteryInfo GetBatteryInfo() {
+ using android::hardware::health::V1_0::BatteryStatus;
+ using android::hardware::health::V2_0::get_health_service;
+ using android::hardware::health::V2_0::IHealth;
+ using android::hardware::health::V2_0::Result;
+ using android::hardware::health::V2_0::toString;
+
+ android::sp<IHealth> health = get_health_service();
+
+ int wait_second = 0;
+ while (true) {
+ auto charge_status = BatteryStatus::UNKNOWN;
+
+ if (health == nullptr) {
+ LOG(WARNING) << "No health implementation is found; assuming defaults";
+ } else {
+ health
+ ->getChargeStatus([&charge_status](auto res, auto out_status) {
+ if (res == Result::SUCCESS) {
+ charge_status = out_status;
+ }
+ })
+ .isOk(); // should not have transport error
+ }
+
+ // Treat unknown status as on charger. See hardware/interfaces/health/1.0/types.hal for the
+ // meaning of the return values.
+ bool charging = (charge_status != BatteryStatus::DISCHARGING &&
+ charge_status != BatteryStatus::NOT_CHARGING);
+
+ Result res = Result::UNKNOWN;
+ int32_t capacity = INT32_MIN;
+ if (health != nullptr) {
+ health
+ ->getCapacity([&res, &capacity](auto out_res, auto out_capacity) {
+ res = out_res;
+ capacity = out_capacity;
+ })
+ .isOk(); // should not have transport error
+ }
+
+ LOG(INFO) << "charge_status " << toString(charge_status) << ", charging " << charging
+ << ", status " << toString(res) << ", capacity " << capacity;
+
+ constexpr int BATTERY_READ_TIMEOUT_IN_SEC = 10;
+ // At startup, the battery drivers in devices like N5X/N6P take some time to load
+ // the battery profile. Before the load finishes, it reports value 50 as a fake
+ // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected
+ // to finish loading the battery profile earlier than 10 seconds after kernel startup.
+ if (res == Result::SUCCESS && capacity == 50) {
+ if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) {
+ sleep(1);
+ wait_second++;
+ continue;
+ }
+ }
+ // If we can't read battery percentage, it may be a device without battery. In this
+ // situation, use 100 as a fake battery percentage.
+ if (res != Result::SUCCESS) {
+ capacity = 100;
+ }
+
+ return BatteryInfo{ charging, capacity };
+ }
+}
diff --git a/recovery_utils/include/recovery_utils/battery_utils.h b/recovery_utils/include/recovery_utils/battery_utils.h
new file mode 100644
index 000000000..a95f71dca
--- /dev/null
+++ b/recovery_utils/include/recovery_utils/battery_utils.h
@@ -0,0 +1,33 @@
+/*
+ * 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>
+
+struct BatteryInfo {
+ // Whether the device is on charger. Note that the value will be `true` if the battery status is
+ // unknown (BATTERY_STATUS_UNKNOWN).
+ bool charging;
+
+ // The remaining battery capacity percentage (i.e. between 0 and 100). See getCapacity in
+ // hardware/interfaces/health/2.0/IHealth.hal. Returns 100 in case it fails to read a value from
+ // the health HAL.
+ int32_t capacity;
+};
+
+// Returns the battery status for OTA installation purpose.
+BatteryInfo GetBatteryInfo();
diff --git a/otautil/include/otautil/logging.h b/recovery_utils/include/recovery_utils/logging.h
index 608349785..4462eca6e 100644
--- a/otautil/include/otautil/logging.h
+++ b/recovery_utils/include/recovery_utils/logging.h
@@ -53,7 +53,7 @@ void rotate_logs(const char* last_log_file, const char* last_kmsg_file);
void check_and_fclose(FILE* fp, const std::string& name);
void copy_log_file_to_pmsg(const std::string& source, const std::string& destination);
-void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle);
+void copy_logs(bool save_current_log);
void reset_tmplog_offset();
void save_kernel_log(const char* destination);
diff --git a/otautil/include/otautil/parse_install_logs.h b/recovery_utils/include/recovery_utils/parse_install_logs.h
index 135d29ccf..135d29ccf 100644
--- a/otautil/include/otautil/parse_install_logs.h
+++ b/recovery_utils/include/recovery_utils/parse_install_logs.h
diff --git a/otautil/include/otautil/roots.h b/recovery_utils/include/recovery_utils/roots.h
index 482f3d050..92ee756f0 100644
--- a/otautil/include/otautil/roots.h
+++ b/recovery_utils/include/recovery_utils/roots.h
@@ -54,6 +54,5 @@ int format_volume(const std::string& volume, const std::string& directory);
// mounted (/tmp and /cache) are mounted. Returns 0 on success.
int setup_install_mounts();
-bool logical_partitions_mapped();
-
-std::string get_system_root();
+// Returns true if there is /cache in the volumes.
+bool HasCache();
diff --git a/otautil/include/otautil/thermalutil.h b/recovery_utils/include/recovery_utils/thermalutil.h
index 43ab55940..43ab55940 100644
--- a/otautil/include/otautil/thermalutil.h
+++ b/recovery_utils/include/recovery_utils/thermalutil.h
diff --git a/otautil/logging.cpp b/recovery_utils/logging.cpp
index 484f1150f..52f12a8d8 100644
--- a/otautil/logging.cpp
+++ b/recovery_utils/logging.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "otautil/logging.h"
+#include "recovery_utils/logging.h"
#include <dirent.h>
#include <errno.h>
@@ -38,7 +38,7 @@
#include "otautil/dirutil.h"
#include "otautil/paths.h"
-#include "otautil/roots.h"
+#include "recovery_utils/roots.h"
constexpr const char* LOG_FILE = "/cache/recovery/log";
constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install";
@@ -178,9 +178,8 @@ void reset_tmplog_offset() {
tmplog_offset = 0;
}
-static void copy_log_file(const std::string& source, const std::string& destination, bool append,
- const selabel_handle* sehandle) {
- FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", sehandle);
+static void copy_log_file(const std::string& source, const std::string& destination, bool append) {
+ FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", logging_sehandle);
if (dest_fp == nullptr) {
PLOG(ERROR) << "Can't open " << destination;
} else {
@@ -203,7 +202,7 @@ static void copy_log_file(const std::string& source, const std::string& destinat
}
}
-void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle) {
+void copy_logs(bool save_current_log) {
// We only rotate and record the log of the current session if explicitly requested. This usually
// happens after wipes, installation from BCB or menu selections. This is to avoid unnecessary
// rotation (and possible deletion) of log files, if it does not do anything loggable.
@@ -216,7 +215,7 @@ void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* seha
copy_log_file_to_pmsg(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE);
// We can do nothing for now if there's no /cache partition.
- if (!has_cache) {
+ if (!HasCache()) {
return;
}
@@ -225,9 +224,9 @@ void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* seha
rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE);
// Copy logs to cache so the system can find out what happened.
- copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true, sehandle);
- copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false, sehandle);
- copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false, sehandle);
+ copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true);
+ copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false);
+ copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false);
save_kernel_log(LAST_KMSG_FILE);
chmod(LOG_FILE, 0600);
chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM);
@@ -319,7 +318,7 @@ bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files) {
// Reset the pointer so we copy from the beginning of the temp
// log.
reset_tmplog_offset();
- copy_logs(true /* save_current_log */, true /* has_cache */, logging_sehandle);
+ copy_logs(true /* save_current_log */);
return true;
}
diff --git a/otautil/parse_install_logs.cpp b/recovery_utils/parse_install_logs.cpp
index 13a729921..c86317623 100644
--- a/otautil/parse_install_logs.cpp
+++ b/recovery_utils/parse_install_logs.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "otautil/parse_install_logs.h"
+#include "recovery_utils/parse_install_logs.h"
#include <unistd.h>
diff --git a/otautil/roots.cpp b/recovery_utils/roots.cpp
index 815d644e5..58a313973 100644
--- a/otautil/roots.cpp
+++ b/recovery_utils/roots.cpp
@@ -14,15 +14,12 @@
* limitations under the License.
*/
-#include "otautil/roots.h"
+#include "recovery_utils/roots.h"
-#include <ctype.h>
#include <fcntl.h>
-#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -40,9 +37,7 @@
#include <ext4_utils/wipe.h>
#include <fs_mgr.h>
#include <fs_mgr/roots.h>
-#include <fs_mgr_dm_linear.h>
-#include "otautil/mounts.h"
#include "otautil/sysutil.h"
using android::fs_mgr::Fstab;
@@ -51,6 +46,8 @@ using android::fs_mgr::ReadDefaultFstab;
static Fstab fstab;
+constexpr const char* CACHE_ROOT = "/cache";
+
void load_volume_table() {
if (!ReadDefaultFstab(&fstab)) {
LOG(ERROR) << "Failed to read default fstab";
@@ -58,7 +55,11 @@ void load_volume_table() {
}
fstab.emplace_back(FstabEntry{
- .mount_point = "/tmp", .fs_type = "ramdisk", .blk_device = "ramdisk", .length = 0 });
+ .blk_device = "ramdisk",
+ .mount_point = "/tmp",
+ .fs_type = "ramdisk",
+ .length = 0,
+ });
std::cout << "recovery filesystem table" << std::endl << "=========================" << std::endl;
for (size_t i = 0; i < fstab.size(); ++i) {
@@ -152,6 +153,14 @@ int format_volume(const std::string& volume, const std::string& directory) {
return -1;
}
+ bool needs_casefold = false;
+ bool needs_projid = false;
+
+ if (volume == "/data") {
+ needs_casefold = android::base::GetBoolProperty("ro.emulated_storage.casefold", false);
+ needs_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false);
+ }
+
// If there's a key_loc that looks like a path, it should be a block device for storing encryption
// metadata. Wipe it too.
if (!v->key_loc.empty() && v->key_loc[0] == '/') {
@@ -187,6 +196,21 @@ int format_volume(const std::string& volume, const std::string& directory) {
"/system/bin/mke2fs", "-F", "-t", "ext4", "-b", std::to_string(kBlockSize),
};
+ // Project ID's require wider inodes. The Quotas themselves are enabled by tune2fs on boot.
+ if (needs_projid) {
+ mke2fs_args.push_back("-I");
+ mke2fs_args.push_back("512");
+ }
+
+ if (v->fs_mgr_flags.ext_meta_csum) {
+ mke2fs_args.push_back("-O");
+ mke2fs_args.push_back("metadata_csum");
+ mke2fs_args.push_back("-O");
+ mke2fs_args.push_back("64bit");
+ mke2fs_args.push_back("-O");
+ mke2fs_args.push_back("extent");
+ }
+
int raid_stride = v->logical_blk_size / kBlockSize;
int raid_stripe_width = v->erase_blk_size / kBlockSize;
// stride should be the max of 8KB and logical block size
@@ -224,8 +248,18 @@ int format_volume(const std::string& volume, const std::string& directory) {
"/system/bin/make_f2fs",
"-g",
"android",
- v->blk_device,
};
+ if (needs_projid) {
+ make_f2fs_cmd.push_back("-O");
+ make_f2fs_cmd.push_back("project_quota,extra_attr");
+ }
+ if (needs_casefold) {
+ make_f2fs_cmd.push_back("-O");
+ make_f2fs_cmd.push_back("casefold");
+ make_f2fs_cmd.push_back("-C");
+ make_f2fs_cmd.push_back("utf8");
+ }
+ make_f2fs_cmd.push_back(v->blk_device);
if (length >= kSectorSize) {
make_f2fs_cmd.push_back(std::to_string(length / kSectorSize));
}
@@ -276,10 +310,8 @@ 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();
+bool HasCache() {
+ CHECK(!fstab.empty());
+ static bool has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr;
+ return has_cache;
}
diff --git a/otautil/thermalutil.cpp b/recovery_utils/thermalutil.cpp
index 4660e057e..5436355d6 100644
--- a/otautil/thermalutil.cpp
+++ b/recovery_utils/thermalutil.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "otautil/thermalutil.h"
+#include "recovery_utils/thermalutil.h"
#include <dirent.h>
#include <stdio.h>
diff --git a/tests/Android.bp b/tests/Android.bp
index a0d82d5fd..4c23255ad 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -39,6 +39,7 @@ cc_defaults {
android: {
shared_libs: [
"libutils",
+ "libvndksupport",
],
},
@@ -50,13 +51,11 @@ cc_defaults {
},
}
-// libapplypatch, libapplypatch_modes, libimgdiff, libimgpatch
+// libapplypatch, libapplypatch_modes
libapplypatch_static_libs = [
"libapplypatch_modes",
"libapplypatch",
"libedify",
- "libimgdiff",
- "libimgpatch",
"libotautil",
"libbsdiff",
"libbspatch",
@@ -66,7 +65,6 @@ libapplypatch_static_libs = [
"libbase",
"libbrotli",
"libbz",
- "libcrypto",
"libz",
"libziparchive",
]
@@ -79,25 +77,20 @@ librecovery_static_libs = [
"libinstall",
"librecovery_ui",
"libminui",
+ "libfusesideload",
+ "libbootloader_message",
"libotautil",
"libhealthhalutils",
- "libvintf_recovery",
"libvintf",
"android.hardware.health@2.0",
"android.hardware.health@1.0",
- "libbootloader_message",
"libext4_utils",
"libfs_mgr",
- "libfusesideload",
"libhidl-gen-utils",
"libhidlbase",
- "libhidltransport",
- "libhwbinder_noltopgo",
- "libbinderthreadstate",
"liblp",
- "libvndksupport",
"libtinyxml2",
"libc++fs",
]
@@ -105,9 +98,12 @@ librecovery_static_libs = [
cc_test {
name: "recovery_unit_test",
isolated: true,
+ require_root: true,
defaults: [
"recovery_test_defaults",
+ "libupdater_defaults",
+ "libupdater_device_defaults",
],
test_suites: ["device-tests"],
@@ -116,16 +112,24 @@ cc_test {
"unit/*.cpp",
],
- static_libs: libapplypatch_static_libs + [
- "libinstall",
+ static_libs: libapplypatch_static_libs + librecovery_static_libs + [
"librecovery_ui",
+ "libfusesideload",
"libminui",
+ "librecovery_utils",
"libotautil",
- "libupdater",
+ "libupdater_device",
+ "libupdater_core",
+ "libupdate_verifier",
+
"libgtest_prod",
+ "libprotobuf-cpp-lite",
],
- data: ["testdata/*"],
+ data: [
+ "testdata/*",
+ ":res-testdata",
+ ],
}
cc_test {
@@ -143,66 +147,37 @@ 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,
defaults: [
"recovery_test_defaults",
+ "libupdater_defaults",
],
srcs: [
- "component/imgdiff_test.cpp",
+ "unit/host/*",
],
static_libs: [
+ "libupdater_host",
+ "libupdater_core",
"libimgdiff",
- "libimgpatch",
- "libotautil",
"libbsdiff",
- "libbspatch",
- "libziparchive",
- "libutils",
- "libcrypto",
- "libbrotli",
- "libbz",
"libdivsufsort64",
"libdivsufsort",
- "libz",
+ "libfstab",
+ "libc++fs",
],
+ test_suites: ["general-tests"],
+
data: ["testdata/*"],
target: {
darwin: {
- // libimgdiff is not available on the Mac.
+ // libapplypatch in "libupdater_defaults" is not available on the Mac.
enabled: false,
},
},
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
deleted file mode 100644
index 6b86085aa..000000000
--- a/tests/AndroidTest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.
--->
-<configuration description="Config for recovery_component_test and recovery_unit_test">
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="recovery_component_test->/data/local/tmp/recovery_component_test/recovery_component_test" />
- <option name="push" value="testdata->/data/local/tmp/recovery_component_test/testdata" />
- <option name="push" value="recovery_unit_test->/data/local/tmp/recovery_unit_test/recovery_unit_test" />
- <option name="push" value="testdata->/data/local/tmp/recovery_unit_test/testdata" />
- </target_preparer>
- <option name="test-suite-tag" value="apct" />
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp/recovery_component_test" />
- <option name="module-name" value="recovery_component_test" />
- </test>
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp/recovery_unit_test" />
- <option name="module-name" value="recovery_unit_test" />
- </test>
-</configuration>
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/unit/applypatch_test.cpp b/tests/unit/applypatch_test.cpp
index 794f2c103..218a224f8 100644
--- a/tests/unit/applypatch_test.cpp
+++ b/tests/unit/applypatch_test.cpp
@@ -141,7 +141,7 @@ TEST_F(ApplyPatchTest, PatchPartition) {
ASSERT_TRUE(LoadFileContents(from_testdata_base("bonus.file"), &bonus_fc));
Value bonus(Value::Type::BLOB, std::string(bonus_fc.data.cbegin(), bonus_fc.data.cend()));
- ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, &bonus));
+ ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, &bonus, false));
}
// Tests patching an eMMC target without a separate bonus file (i.e. recovery-from-boot patch has
@@ -151,7 +151,7 @@ TEST_F(ApplyPatchTest, PatchPartitionWithoutBonusFile) {
ASSERT_TRUE(LoadFileContents(from_testdata_base("recovery-from-boot-with-bonus.p"), &patch_fc));
Value patch(Value::Type::BLOB, std::string(patch_fc.data.cbegin(), patch_fc.data.cend()));
- ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, nullptr));
+ ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, nullptr, false));
}
class FreeCacheTest : public ::testing::Test {
diff --git a/tests/unit/battery_utils_test.cpp b/tests/unit/battery_utils_test.cpp
new file mode 100644
index 000000000..55639fdb5
--- /dev/null
+++ b/tests/unit/battery_utils_test.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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 agree 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 <android-base/logging.h>
+#include <gtest/gtest.h>
+
+#include "recovery_utils/battery_utils.h"
+
+TEST(BatteryInfoTest, GetBatteryInfo) {
+ auto info = GetBatteryInfo();
+ // 0 <= capacity <= 100
+ ASSERT_LE(0, info.capacity);
+ ASSERT_LE(info.capacity, 100);
+}
diff --git a/tests/component/bootloader_message_test.cpp b/tests/unit/bootloader_message_test.cpp
index 95d875e69..731c8feb7 100644
--- a/tests/component/bootloader_message_test.cpp
+++ b/tests/unit/bootloader_message_test.cpp
@@ -118,37 +118,3 @@ TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_long) {
ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'),
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..37f99f92e
--- /dev/null
+++ b/tests/unit/fuse_provider_test.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 } }),
+ static_cast<FuseBlockDataProvider*>(block_map_data.get())->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..ea895038c 100644
--- a/tests/component/sideload_test.cpp
+++ b/tests/unit/fuse_sideload_test.cpp
@@ -40,6 +40,10 @@ class FuseTestDataProvider : public FuseDataProvider {
bool ReadBlockAlignedData(uint8_t*, uint32_t, uint32_t) const override {
return true;
}
+
+ bool Valid() const override {
+ return true;
+ }
};
TEST(SideloadTest, run_fuse_sideload_wrong_parameters) {
diff --git a/tests/component/imgdiff_test.cpp b/tests/unit/host/imgdiff_test.cpp
index e76ccbdfb..e76ccbdfb 100644
--- a/tests/component/imgdiff_test.cpp
+++ b/tests/unit/host/imgdiff_test.cpp
diff --git a/tests/unit/host/update_simulator_test.cpp b/tests/unit/host/update_simulator_test.cpp
new file mode 100644
index 000000000..fb1217877
--- /dev/null
+++ b/tests/unit/host/update_simulator_test.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <bsdiff/bsdiff.h>
+#include <gtest/gtest.h>
+#include <ziparchive/zip_writer.h>
+
+#include "otautil/paths.h"
+#include "otautil/print_sha1.h"
+#include "updater/blockimg.h"
+#include "updater/build_info.h"
+#include "updater/install.h"
+#include "updater/simulator_runtime.h"
+#include "updater/target_files.h"
+#include "updater/updater.h"
+
+using std::string;
+
+// echo -n "system.img" > system.img && img2simg system.img sparse_system_string_.img 4096 &&
+// hexdump -v -e '" " 12/1 "0x%02x, " "\n"' sparse_system_string_.img
+// The total size of the result sparse image is 4136 bytes; and we can append 0s in the end to get
+// the full image.
+constexpr uint8_t SPARSE_SYSTEM_HEADER[] = {
+ 0x3a, 0xff, 0x26, 0xed, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x0c, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xca,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x10, 0x00, 0x00, 0x73, 0x79, 0x73, 0x74, 0x65,
+ 0x6d, 0x2e, 0x69, 0x6d, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static void AddZipEntries(int fd, const std::map<string, string>& entries) {
+ FILE* zip_file = fdopen(fd, "w");
+ ZipWriter writer(zip_file);
+ for (const auto& pair : entries) {
+ ASSERT_EQ(0, writer.StartEntry(pair.first.c_str(), 0));
+ ASSERT_EQ(0, writer.WriteBytes(pair.second.data(), pair.second.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+ }
+ ASSERT_EQ(0, writer.Finish());
+ ASSERT_EQ(0, fclose(zip_file));
+}
+
+static string CalculateSha1(const string& data) {
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest);
+ return print_sha1(digest);
+}
+
+static void CreateBsdiffPatch(const string& src, const string& tgt, string* patch) {
+ TemporaryFile patch_file;
+ ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src.data()), src.size(),
+ reinterpret_cast<const uint8_t*>(tgt.data()), tgt.size(),
+ patch_file.path, nullptr));
+ ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, patch));
+}
+
+static void RunSimulation(std::string_view src_tf, std::string_view ota_package, bool expected) {
+ TemporaryFile cmd_pipe;
+ TemporaryFile temp_saved_source;
+ TemporaryFile temp_last_command;
+ TemporaryDir temp_stash_base;
+
+ Paths::Get().set_cache_temp_source(temp_saved_source.path);
+ Paths::Get().set_last_command_file(temp_last_command.path);
+ Paths::Get().set_stash_directory_base(temp_stash_base.path);
+
+ // Configure edify's functions.
+ RegisterBuiltins();
+ RegisterInstallFunctions();
+ RegisterBlockImageFunctions();
+
+ // Run the update simulation and check the result.
+ TemporaryDir work_dir;
+ BuildInfo build_info(work_dir.path, false);
+ ASSERT_TRUE(build_info.ParseTargetFile(src_tf, false));
+ Updater updater(std::make_unique<SimulatorRuntime>(&build_info));
+ ASSERT_TRUE(updater.Init(cmd_pipe.release(), ota_package, false));
+ ASSERT_EQ(expected, updater.RunUpdate());
+ // TODO(xunchang) check the recovery&system has the expected contents.
+}
+
+class UpdateSimulatorTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ std::vector<string> props = {
+ "import /oem/oem.prop oem*",
+ "# begin build properties",
+ "# autogenerated by buildinfo.sh",
+ "ro.build.id=OPR1.170510.001",
+ "ro.build.display.id=OPR1.170510.001 dev-keys",
+ "ro.build.version.incremental=3993052",
+ "ro.build.version.release=O",
+ "ro.build.date=Wed May 10 11:10:29 UTC 2017",
+ "ro.build.date.utc=1494414629",
+ "ro.build.type=user",
+ "ro.build.tags=dev-keys",
+ "ro.build.flavor=angler-user",
+ "ro.product.system.brand=google",
+ "ro.product.system.name=angler",
+ "ro.product.system.device=angler",
+ };
+ build_prop_string_ = android::base::Join(props, "\n");
+
+ fstab_content_ = R"(
+#<src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
+# More comments.....
+
+/dev/block/by-name/system /system ext4 ro,barrier=1 wait
+/dev/block/by-name/vendor /vendor ext4 ro wait,verify=/dev/metadata
+/dev/block/by-name/cache /cache ext4 noatime,errors=panic wait,check
+/dev/block/by-name/modem /firmware vfat ro,uid=1000,gid=1000, wait
+/dev/block/by-name/boot /boot emmc defaults defaults
+/dev/block/by-name/recovery /recovery emmc defaults defaults
+/dev/block/by-name/misc /misc emmc defaults
+/dev/block/by-name/modem /modem emmc defaults defaults)";
+
+ raw_system_string_ = "system.img" + string(4086, '\0'); // raw image is 4096 bytes in total
+ sparse_system_string_ = string(SPARSE_SYSTEM_HEADER, std::end(SPARSE_SYSTEM_HEADER)) +
+ string(4136 - sizeof(SPARSE_SYSTEM_HEADER), '\0');
+ }
+
+ string build_prop_string_;
+ string fstab_content_;
+ string raw_system_string_;
+ string sparse_system_string_;
+};
+
+TEST_F(UpdateSimulatorTest, TargetFile_ExtractImage) {
+ TemporaryFile zip_file;
+ AddZipEntries(zip_file.release(), { { "META/misc_info.txt", "extfs_sparse_flag=-s" },
+ { "IMAGES/system.img", sparse_system_string_ } });
+ TargetFile target_file(zip_file.path, false);
+ ASSERT_TRUE(target_file.Open());
+
+ TemporaryDir temp_dir;
+ TemporaryFile raw_image;
+ ASSERT_TRUE(target_file.ExtractImage(
+ "IMAGES/system.img", FstabInfo("/dev/system", "system", "ext4"), temp_dir.path, &raw_image));
+
+ // Check the raw image has expected contents.
+ string content;
+ ASSERT_TRUE(android::base::ReadFileToString(raw_image.path, &content));
+ string expected_content = "system.img" + string(4086, '\0');
+ ASSERT_EQ(expected_content, content);
+}
+
+TEST_F(UpdateSimulatorTest, TargetFile_ParseFstabInfo) {
+ TemporaryFile zip_file;
+ AddZipEntries(zip_file.release(),
+ { { "META/misc_info.txt", "" },
+ { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ } });
+ TargetFile target_file(zip_file.path, false);
+ ASSERT_TRUE(target_file.Open());
+
+ std::vector<FstabInfo> fstab_info;
+ EXPECT_TRUE(target_file.ParseFstabInfo(&fstab_info));
+
+ std::vector<std::vector<string>> transformed;
+ std::transform(fstab_info.begin(), fstab_info.end(), std::back_inserter(transformed),
+ [](const FstabInfo& info) {
+ return std::vector<string>{ info.blockdev_name, info.mount_point, info.fs_type };
+ });
+
+ std::vector<std::vector<string>> expected = {
+ { "/dev/block/by-name/system", "/system", "ext4" },
+ { "/dev/block/by-name/vendor", "/vendor", "ext4" },
+ { "/dev/block/by-name/cache", "/cache", "ext4" },
+ { "/dev/block/by-name/boot", "/boot", "emmc" },
+ { "/dev/block/by-name/recovery", "/recovery", "emmc" },
+ { "/dev/block/by-name/misc", "/misc", "emmc" },
+ { "/dev/block/by-name/modem", "/modem", "emmc" },
+ };
+ EXPECT_EQ(expected, transformed);
+}
+
+TEST_F(UpdateSimulatorTest, BuildInfo_ParseTargetFile) {
+ std::map<string, string> entries = {
+ { "META/misc_info.txt", "" },
+ { "SYSTEM/build.prop", build_prop_string_ },
+ { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ },
+ { "IMAGES/recovery.img", "" },
+ { "IMAGES/boot.img", "" },
+ { "IMAGES/misc.img", "" },
+ { "IMAGES/system.map", "" },
+ { "IMAGES/system.img", sparse_system_string_ },
+ };
+
+ TemporaryFile zip_file;
+ AddZipEntries(zip_file.release(), entries);
+
+ TemporaryDir temp_dir;
+ BuildInfo build_info(temp_dir.path, false);
+ ASSERT_TRUE(build_info.ParseTargetFile(zip_file.path, false));
+
+ std::map<string, string> expected_result = {
+ { "ro.build.id", "OPR1.170510.001" },
+ { "ro.build.display.id", "OPR1.170510.001 dev-keys" },
+ { "ro.build.version.incremental", "3993052" },
+ { "ro.build.version.release", "O" },
+ { "ro.build.date", "Wed May 10 11:10:29 UTC 2017" },
+ { "ro.build.date.utc", "1494414629" },
+ { "ro.build.type", "user" },
+ { "ro.build.tags", "dev-keys" },
+ { "ro.build.flavor", "angler-user" },
+ { "ro.product.brand", "google" },
+ { "ro.product.name", "angler" },
+ { "ro.product.device", "angler" },
+ };
+
+ for (const auto& [key, value] : expected_result) {
+ ASSERT_EQ(value, build_info.GetProperty(key, ""));
+ }
+
+ // Check that the temp files for each block device are created successfully.
+ for (auto name : { "/dev/block/by-name/system", "/dev/block/by-name/recovery",
+ "/dev/block/by-name/boot", "/dev/block/by-name/misc" }) {
+ ASSERT_EQ(0, access(build_info.FindBlockDeviceName(name).c_str(), R_OK));
+ }
+}
+
+TEST_F(UpdateSimulatorTest, RunUpdateSmoke) {
+ string recovery_img_string = "recovery.img";
+ string boot_img_string = "boot.img";
+
+ std::map<string, string> src_entries{
+ { "META/misc_info.txt", "extfs_sparse_flag=-s" },
+ { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
+ { "SYSTEM/build.prop", build_prop_string_ },
+ { "IMAGES/recovery.img", "" },
+ { "IMAGES/boot.img", boot_img_string },
+ { "IMAGES/system.img", sparse_system_string_ },
+ };
+
+ // Construct the source target-files.
+ TemporaryFile src_tf;
+ AddZipEntries(src_tf.release(), src_entries);
+
+ string recovery_from_boot;
+ CreateBsdiffPatch(boot_img_string, recovery_img_string, &recovery_from_boot);
+
+ // Set up the apply patch commands to patch the recovery image.
+ string recovery_sha1 = CalculateSha1(recovery_img_string);
+ string boot_sha1 = CalculateSha1(boot_img_string);
+ string apply_patch_source_string = android::base::StringPrintf(
+ "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str());
+ string apply_patch_target_string = android::base::StringPrintf(
+ "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str());
+ string check_command = android::base::StringPrintf(
+ R"(patch_partition_check("%s", "%s") || abort("check failed");)",
+ apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
+ string patch_command = android::base::StringPrintf(
+ R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("patch failed");)",
+ apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
+
+ // Add the commands to update the system image. Test common commands:
+ // * getprop
+ // * ui_print
+ // * patch_partition
+ // * package_extract_file (single argument)
+ // * block_image_verify, block_image_update
+ string tgt_system_string = string(4096, 'a');
+ string system_patch;
+ CreateBsdiffPatch(raw_system_string_, tgt_system_string, &system_patch);
+
+ string tgt_system_hash = CalculateSha1(tgt_system_string);
+ string src_system_hash = CalculateSha1(raw_system_string_);
+
+ std::vector<std::string> transfer_list = {
+ "4",
+ "1",
+ "0",
+ "0",
+ android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,1 1 2,0,1", system_patch.size(),
+ src_system_hash.c_str(), tgt_system_hash.c_str()),
+ };
+
+ // Construct the updater_script.
+ std::vector<string> updater_commands = {
+ R"(getprop("ro.product.device") == "angler" || abort("This package is for \"angler\"");)",
+ R"(ui_print("Source: angler/OPR1.170510.001");)",
+ check_command,
+ patch_command,
+ R"(block_image_verify("/dev/block/by-name/system", )"
+ R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )"
+ R"(abort("Failed to verify system.");)",
+ R"(block_image_update("/dev/block/by-name/system", )"
+ R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )"
+ R"(abort("Failed to verify system.");)",
+ };
+ string updater_script = android::base::Join(updater_commands, '\n');
+
+ // Construct the ota update package.
+ std::map<string, string> ota_entries{
+ { "system.new.dat", "" },
+ { "system.patch.dat", system_patch },
+ { "system.transfer.list", android::base::Join(transfer_list, '\n') },
+ { "META-INF/com/google/android/updater-script", updater_script },
+ { "patch.p", recovery_from_boot },
+ };
+
+ TemporaryFile ota_package;
+ AddZipEntries(ota_package.release(), ota_entries);
+
+ RunSimulation(src_tf.path, ota_package.path, true);
+}
+
+TEST_F(UpdateSimulatorTest, RunUpdateUnrecognizedFunction) {
+ std::map<string, string> src_entries{
+ { "META/misc_info.txt", "extfs_sparse_flag=-s" },
+ { "IMAGES/system.img", sparse_system_string_ },
+ { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
+ { "SYSTEM/build.prop", build_prop_string_ },
+ };
+
+ TemporaryFile src_tf;
+ AddZipEntries(src_tf.release(), src_entries);
+
+ std::map<string, string> ota_entries{
+ { "system.new.dat", "" },
+ { "system.patch.dat", "" },
+ { "system.transfer.list", "" },
+ { "META-INF/com/google/android/updater-script", R"(bad_function("");)" },
+ };
+
+ TemporaryFile ota_package;
+ AddZipEntries(ota_package.release(), ota_entries);
+
+ RunSimulation(src_tf.path, ota_package.path, false);
+}
+
+TEST_F(UpdateSimulatorTest, RunUpdateApplyPatchFailed) {
+ string recovery_img_string = "recovery.img";
+ string boot_img_string = "boot.img";
+
+ std::map<string, string> src_entries{
+ { "META/misc_info.txt", "extfs_sparse_flag=-s" },
+ { "IMAGES/recovery.img", "" },
+ { "IMAGES/boot.img", boot_img_string },
+ { "IMAGES/system.img", sparse_system_string_ },
+ { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
+ { "SYSTEM/build.prop", build_prop_string_ },
+ };
+
+ TemporaryFile src_tf;
+ AddZipEntries(src_tf.release(), src_entries);
+
+ string recovery_sha1 = CalculateSha1(recovery_img_string);
+ string boot_sha1 = CalculateSha1(boot_img_string);
+ string apply_patch_source_string = android::base::StringPrintf(
+ "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str());
+ string apply_patch_target_string = android::base::StringPrintf(
+ "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str());
+ string check_command = android::base::StringPrintf(
+ R"(patch_partition_check("%s", "%s") || abort("check failed");)",
+ apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
+ string patch_command = android::base::StringPrintf(
+ R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("failed");)",
+ apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
+
+ // Give an invalid recovery patch and expect the apply patch to fail.
+ // TODO(xunchang) check the cause code.
+ std::vector<string> updater_commands = {
+ R"(ui_print("Source: angler/OPR1.170510.001");)",
+ check_command,
+ patch_command,
+ };
+
+ string updater_script = android::base::Join(updater_commands, '\n');
+ std::map<string, string> ota_entries{
+ { "system.new.dat", "" },
+ { "system.patch.dat", "" },
+ { "system.transfer.list", "" },
+ { "META-INF/com/google/android/updater-script", updater_script },
+ { "patch.p", "random string" },
+ };
+
+ TemporaryFile ota_package;
+ AddZipEntries(ota_package.release(), ota_entries);
+
+ RunSimulation(src_tf.path, ota_package.path, false);
+}
diff --git a/tests/component/install_test.cpp b/tests/unit/install_test.cpp
index c1f0ca812..ee753494c 100644
--- a/tests/component/install_test.cpp
+++ b/tests/unit/install_test.cpp
@@ -28,14 +28,14 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <gtest/gtest.h>
-#include <vintf/VintfObjectRecovery.h>
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_writer.h>
#include "install/install.h"
+#include "install/wipe_device.h"
#include "otautil/paths.h"
-#include "otautil/roots.h"
#include "private/setup_commands.h"
+#include "recovery_utils/roots.h"
static void BuildZipArchive(const std::map<std::string, std::string>& file_map, int fd,
int compression_type) {
@@ -50,29 +50,6 @@ static void BuildZipArchive(const std::map<std::string, std::string>& file_map,
ASSERT_EQ(0, fclose(zip_file));
}
-TEST(InstallTest, verify_package_compatibility_no_entry) {
- TemporaryFile temp_file;
- // The archive must have something to be opened correctly.
- BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored);
-
- // Doesn't contain compatibility zip entry.
- ZipArchiveHandle zip;
- ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
- ASSERT_TRUE(verify_package_compatibility(zip));
- CloseArchive(zip);
-}
-
-TEST(InstallTest, verify_package_compatibility_invalid_entry) {
- TemporaryFile temp_file;
- BuildZipArchive({ { "compatibility.zip", "" } }, temp_file.release(), kCompressStored);
-
- // Empty compatibility zip entry.
- ZipArchiveHandle zip;
- ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
- ASSERT_FALSE(verify_package_compatibility(zip));
- CloseArchive(zip);
-}
-
TEST(InstallTest, read_metadata_from_package_smoke) {
TemporaryFile temp_file;
const std::string content("abc=defg");
@@ -134,64 +111,6 @@ TEST(InstallTest, read_wipe_ab_partition_list) {
ASSERT_EQ(expected, read_partition_list);
}
-TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) {
- TemporaryFile compatibility_zip_file;
- std::string malformed_xml = "malformed";
- BuildZipArchive({ { "system_manifest.xml", malformed_xml } }, compatibility_zip_file.release(),
- kCompressDeflated);
-
- TemporaryFile temp_file;
- std::string compatibility_zip_content;
- ASSERT_TRUE(
- android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content));
- BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(),
- kCompressStored);
-
- ZipArchiveHandle zip;
- ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
- std::vector<std::string> compatibility_info;
- compatibility_info.push_back(malformed_xml);
- // Malformed compatibility zip is expected to be rejected by libvintf. But we defer that to
- // libvintf.
- std::string err;
- bool result =
- android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err) == 0;
- ASSERT_EQ(result, verify_package_compatibility(zip));
- CloseArchive(zip);
-}
-
-TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml) {
- static constexpr const char* system_manifest_xml_path = "/system/manifest.xml";
- if (access(system_manifest_xml_path, R_OK) == -1) {
- GTEST_LOG_(INFO) << "Test skipped on devices w/o /system/manifest.xml.";
- return;
- }
- std::string system_manifest_xml_content;
- ASSERT_TRUE(
- android::base::ReadFileToString(system_manifest_xml_path, &system_manifest_xml_content));
- TemporaryFile compatibility_zip_file;
- BuildZipArchive({ { "system_manifest.xml", system_manifest_xml_content } },
- compatibility_zip_file.release(), kCompressDeflated);
-
- TemporaryFile temp_file;
- std::string compatibility_zip_content;
- ASSERT_TRUE(
- android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content));
- BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(),
- kCompressStored);
-
- ZipArchiveHandle zip;
- ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
- std::vector<std::string> compatibility_info;
- compatibility_info.push_back(system_manifest_xml_content);
- std::string err;
- bool result =
- android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err) == 0;
- // Make sure the result is consistent with libvintf library.
- ASSERT_EQ(result, verify_package_compatibility(zip));
- CloseArchive(zip);
-}
-
TEST(InstallTest, SetUpNonAbUpdateCommands) {
TemporaryFile temp_file;
static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
@@ -205,7 +124,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
@@ -217,7 +136,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
@@ -244,7 +163,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);
}
@@ -271,19 +190,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]);
@@ -291,7 +209,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);
}
@@ -326,7 +244,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);
}
@@ -359,8 +277,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(
{
@@ -388,7 +306,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(
@@ -398,9 +316,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) {
@@ -410,7 +328,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(
@@ -419,7 +337,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) {
@@ -433,7 +351,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(
@@ -443,7 +361,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);
@@ -454,7 +372,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) {
@@ -478,7 +396,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());
@@ -489,7 +407,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) {
@@ -507,7 +425,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>{
@@ -517,7 +435,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) {
@@ -535,7 +453,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>{
@@ -545,7 +463,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) {
@@ -559,7 +477,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(
@@ -569,7 +487,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(
@@ -580,7 +498,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);
@@ -594,7 +512,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) {
"ota-downgrade=yes",
},
"\n");
- test_check_package_metadata(metadata, OtaType::AB, 0);
+ TestCheckPackageMetadata(metadata, OtaType::AB, true);
}
TEST(InstallTest, SetupPackageMount_package_path) {
diff --git a/tests/unit/locale_test.cpp b/tests/unit/locale_test.cpp
index cdaba0e8b..c69434c12 100644
--- a/tests/unit/locale_test.cpp
+++ b/tests/unit/locale_test.cpp
@@ -27,7 +27,7 @@ TEST(LocaleTest, Misc) {
EXPECT_FALSE(matches_locale("en-GB", "en"));
EXPECT_FALSE(matches_locale("en-GB", "en-US"));
EXPECT_FALSE(matches_locale("en-US", ""));
- // Empty locale prefix in the PNG file will match the input locale.
- EXPECT_TRUE(matches_locale("", "en-US"));
+ // Empty locale prefix in the PNG file should not match the input locale.
+ EXPECT_FALSE(matches_locale("", "en-US"));
EXPECT_TRUE(matches_locale("sr-Latn", "sr-Latn-BA"));
}
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/parse_install_logs_test.cpp b/tests/unit/parse_install_logs_test.cpp
index 72169a0c6..052f71c98 100644
--- a/tests/unit/parse_install_logs_test.cpp
+++ b/tests/unit/parse_install_logs_test.cpp
@@ -22,7 +22,7 @@
#include <android-base/strings.h>
#include <gtest/gtest.h>
-#include "otautil/parse_install_logs.h"
+#include "recovery_utils/parse_install_logs.h"
TEST(ParseInstallLogsTest, EmptyFile) {
TemporaryFile last_install;
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/image_generator/ImageGenerator.java b/tools/image_generator/ImageGenerator.java
index fd8e54295..1da43e5c6 100644
--- a/tools/image_generator/ImageGenerator.java
+++ b/tools/image_generator/ImageGenerator.java
@@ -123,7 +123,7 @@ public class ImageGenerator {
put("hy", "NotoSansArmenian-Regular");
put("iw", "NotoSansHebrew-Regular");
put("ja", "NotoSansCJK-Regular");
- put("ka", "NotoSansGeorgian-Regular");
+ put("ka", "NotoSansGeorgian-VF");
put("ko", "NotoSansCJK-Regular");
put("km", "NotoSansKhmerUI-Regular");
put("kn", "NotoSansKannadaUI-Regular");
diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml
index a9cd2d133..69191287d 100644
--- a/tools/recovery_l10n/res/values-ar/strings.xml
+++ b/tools/recovery_l10n/res/values-ar/strings.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="recovery_installing" msgid="2013591905463558223">"جارٍ تثبيت تحديث النظام"</string>
+ <string name="recovery_installing" msgid="2013591905463558223">"جارٍ تثبيت إعادة تحميل النظام"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"جارٍ محو البيانات"</string>
<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_installing_security" msgid="9184031299717114342">"جارٍ تثبيت إعادة تحميل الأمان"</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>
diff --git a/tools/recovery_l10n/res/values-hy/strings.xml b/tools/recovery_l10n/res/values-hy/strings.xml
index 76c28a707..35a0ab113 100644
--- a/tools/recovery_l10n/res/values-hy/strings.xml
+++ b/tools/recovery_l10n/res/values-hy/strings.xml
@@ -9,6 +9,6 @@
<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>
+ <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Մաքրե՞լ օգտատիրոջ բոլոր տվյալները։\n\n ԱՅՍ ԳՈՐԾՈՂՈՒԹՅՈՒՆԸ ՀՆԱՐԱՎՈՐ ՉԻ ԼԻՆԻ ՀԵՏԱՐԿԵԼ"</string>
<string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Չեղարկել"</string>
</resources>
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index d04c455d8..a042f9008 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -371,6 +371,10 @@ int update_verifier(int argc, char** argv) {
return reboot_device();
}
LOG(INFO) << "Marked slot " << current_slot << " as booted successfully.";
+ // Clears the warm reset flag for next reboot.
+ if (!android::base::SetProperty("ota.warm_reset", "0")) {
+ LOG(WARNING) << "Failed to reset the warm reset flag";
+ }
} else {
LOG(INFO) << "Deferred marking slot " << current_slot << " as booted successfully.";
}
diff --git a/updater/Android.bp b/updater/Android.bp
index b80cdb3a0..cbef43099 100644
--- a/updater/Android.bp
+++ b/updater/Android.bp
@@ -13,11 +13,7 @@
// limitations under the License.
cc_defaults {
- name: "libupdater_defaults",
-
- defaults: [
- "recovery_defaults",
- ],
+ name: "libupdater_static_libs",
static_libs: [
"libapplypatch",
@@ -30,7 +26,6 @@ cc_defaults {
"libfec",
"libfec_rs",
"libverity_tree",
- "libfs_mgr",
"libgtest_prod",
"liblog",
"liblp",
@@ -42,10 +37,30 @@ cc_defaults {
"libziparchive",
"libz",
"libbase",
- "libcrypto",
"libcrypto_utils",
"libcutils",
"libutils",
+ ],
+}
+
+cc_defaults {
+ name: "libupdater_defaults",
+
+ defaults: [
+ "recovery_defaults",
+ "libupdater_static_libs",
+ ],
+
+ shared_libs: [
+ "libcrypto",
+ ],
+}
+
+cc_defaults {
+ name: "libupdater_device_defaults",
+
+ static_libs: [
+ "libfs_mgr",
"libtune2fs",
"libext2_com_err",
@@ -58,7 +73,9 @@ cc_defaults {
}
cc_library_static {
- name: "libupdater",
+ name: "libupdater_core",
+
+ host_supported: true,
defaults: [
"recovery_defaults",
@@ -68,8 +85,39 @@ cc_library_static {
srcs: [
"blockimg.cpp",
"commands.cpp",
- "dynamic_partitions.cpp",
"install.cpp",
+ "mounts.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",
+ "updater_runtime_dynamic_partitions.cpp",
+ ],
+
+ static_libs: [
+ "libupdater_core",
],
include_dirs: [
@@ -80,3 +128,61 @@ cc_library_static {
"include",
],
}
+
+cc_library_host_static {
+ name: "libupdater_host",
+
+ defaults: [
+ "recovery_defaults",
+ "libupdater_defaults",
+ ],
+
+ srcs: [
+ "build_info.cpp",
+ "dynamic_partitions.cpp",
+ "simulator_runtime.cpp",
+ "target_files.cpp",
+ ],
+
+ static_libs: [
+ "libupdater_core",
+ "libfstab",
+ "libc++fs",
+ ],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+
+ export_include_dirs: [
+ "include",
+ ],
+}
+
+cc_binary_host {
+ name: "update_host_simulator",
+ defaults: ["libupdater_static_libs"],
+
+ srcs: ["update_simulator_main.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ static_libs: [
+ "libupdater_host",
+ "libupdater_core",
+ "libcrypto_static",
+ "libfstab",
+ "libc++fs",
+ ],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/updater/Android.mk b/updater/Android.mk
index c7a6ba989..8a4cd86d9 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 \
@@ -45,12 +44,27 @@ updater_common_static_libraries := \
libziparchive \
libz \
libbase \
- libcrypto \
+ libcrypto_static \
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)
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/build_info.cpp b/updater/build_info.cpp
new file mode 100644
index 000000000..f168008ec
--- /dev/null
+++ b/updater/build_info.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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/build_info.h"
+
+#include <stdio.h>
+
+#include <set>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "updater/target_files.h"
+
+bool BuildInfo::ParseTargetFile(const std::string_view target_file_path, bool extracted_input) {
+ TargetFile target_file(std::string(target_file_path), extracted_input);
+ if (!target_file.Open()) {
+ return false;
+ }
+
+ if (!target_file.GetBuildProps(&build_props_)) {
+ return false;
+ }
+
+ std::vector<FstabInfo> fstab_info_list;
+ if (!target_file.ParseFstabInfo(&fstab_info_list)) {
+ return false;
+ }
+
+ for (const auto& fstab_info : fstab_info_list) {
+ for (const auto& directory : { "IMAGES", "RADIO" }) {
+ std::string entry_name = directory + fstab_info.mount_point + ".img";
+ if (!target_file.EntryExists(entry_name)) {
+ LOG(WARNING) << "Failed to find the image entry in the target file: " << entry_name;
+ continue;
+ }
+
+ temp_files_.emplace_back(work_dir_);
+ auto& image_file = temp_files_.back();
+ if (!target_file.ExtractImage(entry_name, fstab_info, work_dir_, &image_file)) {
+ LOG(ERROR) << "Failed to set up source image files.";
+ return false;
+ }
+
+ std::string mapped_path = image_file.path;
+ // Rename the images to more readable ones if we want to keep the image.
+ if (keep_images_) {
+ mapped_path = work_dir_ + fstab_info.mount_point + ".img";
+ image_file.release();
+ if (rename(image_file.path, mapped_path.c_str()) != 0) {
+ PLOG(ERROR) << "Failed to rename " << image_file.path << " to " << mapped_path;
+ return false;
+ }
+ }
+
+ LOG(INFO) << "Mounted " << fstab_info.mount_point << "\nMapping: " << fstab_info.blockdev_name
+ << " to " << mapped_path;
+
+ blockdev_map_.emplace(
+ fstab_info.blockdev_name,
+ FakeBlockDevice(fstab_info.blockdev_name, fstab_info.mount_point, mapped_path));
+ break;
+ }
+ }
+
+ return true;
+}
+
+std::string BuildInfo::GetProperty(const std::string_view key,
+ const std::string_view default_value) const {
+ // The logic to parse the ro.product properties should be in line with the generation script.
+ // More details in common.py BuildInfo.GetBuildProp.
+ // TODO(xunchang) handle the oem property and the source order defined in
+ // ro.product.property_source_order
+ const std::set<std::string, std::less<>> ro_product_props = {
+ "ro.product.brand", "ro.product.device", "ro.product.manufacturer", "ro.product.model",
+ "ro.product.name"
+ };
+ const std::vector<std::string> source_order = {
+ "product", "odm", "vendor", "system_ext", "system",
+ };
+ if (ro_product_props.find(key) != ro_product_props.end()) {
+ std::string_view key_suffix(key);
+ CHECK(android::base::ConsumePrefix(&key_suffix, "ro.product"));
+ for (const auto& source : source_order) {
+ std::string resolved_key = "ro.product." + source + std::string(key_suffix);
+ if (auto entry = build_props_.find(resolved_key); entry != build_props_.end()) {
+ return entry->second;
+ }
+ }
+ LOG(WARNING) << "Failed to find property: " << key;
+ return std::string(default_value);
+ } else if (key == "ro.build.fingerprint") {
+ // clang-format off
+ return android::base::StringPrintf("%s/%s/%s:%s/%s/%s:%s/%s",
+ GetProperty("ro.product.brand", "").c_str(),
+ GetProperty("ro.product.name", "").c_str(),
+ GetProperty("ro.product.device", "").c_str(),
+ GetProperty("ro.build.version.release", "").c_str(),
+ GetProperty("ro.build.id", "").c_str(),
+ GetProperty("ro.build.version.incremental", "").c_str(),
+ GetProperty("ro.build.type", "").c_str(),
+ GetProperty("ro.build.tags", "").c_str());
+ // clang-format on
+ }
+
+ auto entry = build_props_.find(key);
+ if (entry == build_props_.end()) {
+ LOG(WARNING) << "Failed to find property: " << key;
+ return std::string(default_value);
+ }
+
+ return entry->second;
+}
+
+std::string BuildInfo::FindBlockDeviceName(const std::string_view name) const {
+ auto entry = blockdev_map_.find(name);
+ if (entry == blockdev_map_.end()) {
+ LOG(WARNING) << "Failed to find path to block device " << name;
+ return "";
+ }
+
+ return entry->second.mounted_file_path;
+}
diff --git a/updater/dynamic_partitions.cpp b/updater/dynamic_partitions.cpp
index b50dd75f9..a340116fe 100644
--- a/updater/dynamic_partitions.cpp
+++ b/updater/dynamic_partitions.cpp
@@ -19,46 +19,20 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include <algorithm>
-#include <chrono>
-#include <iterator>
#include <memory>
-#include <optional>
#include <string>
-#include <type_traits>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/parseint.h>
#include <android-base/strings.h>
-#include <fs_mgr.h>
-#include <fs_mgr_dm_linear.h>
-#include <libdm/dm.h>
-#include <liblp/builder.h>
#include "edify/expr.h"
+#include "edify/updater_runtime_interface.h"
#include "otautil/error_code.h"
#include "otautil/paths.h"
#include "private/utils.h"
-using android::base::ParseUint;
-using android::dm::DeviceMapper;
-using android::dm::DmDeviceState;
-using android::fs_mgr::CreateLogicalPartition;
-using android::fs_mgr::DestroyLogicalPartition;
-using android::fs_mgr::LpMetadata;
-using android::fs_mgr::MetadataBuilder;
-using android::fs_mgr::Partition;
-using android::fs_mgr::PartitionOpener;
-
-static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
-static constexpr char kMetadataUpdatedMarker[] = "/dynamic_partition_metadata.UPDATED";
-
-static std::string GetSuperDevice() {
- return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
-}
-
static std::vector<std::string> ReadStringArgs(const char* name, State* state,
const std::vector<std::unique_ptr<Expr>>& argv,
const std::vector<std::string>& arg_names) {
@@ -89,40 +63,14 @@ static std::vector<std::string> ReadStringArgs(const char* name, State* state,
return ret;
}
-static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
- auto state = DeviceMapper::Instance().GetState(partition_name);
- if (state == DmDeviceState::INVALID) {
- return true;
- }
- if (state == DmDeviceState::ACTIVE) {
- return DestroyLogicalPartition(partition_name, kMapTimeout);
- }
- LOG(ERROR) << "Unknown device mapper state: "
- << static_cast<std::underlying_type_t<DmDeviceState>>(state);
- return false;
-}
-
-static bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) {
- auto state = DeviceMapper::Instance().GetState(partition_name);
- if (state == DmDeviceState::INVALID) {
- return CreateLogicalPartition(GetSuperDevice(), 0 /* metadata slot */, partition_name,
- true /* force writable */, kMapTimeout, path);
- }
-
- if (state == DmDeviceState::ACTIVE) {
- return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path);
- }
- LOG(ERROR) << "Unknown device mapper state: "
- << static_cast<std::underlying_type_t<DmDeviceState>>(state);
- return false;
-}
-
Value* UnmapPartitionFn(const char* name, State* state,
const std::vector<std::unique_ptr<Expr>>& argv) {
auto args = ReadStringArgs(name, state, argv, { "name" });
if (args.empty()) return StringValue("");
- return UnmapPartitionOnDeviceMapper(args[0]) ? StringValue("t") : StringValue("");
+ auto updater_runtime = state->updater->GetRuntime();
+ return updater_runtime->UnmapPartitionOnDeviceMapper(args[0]) ? StringValue("t")
+ : StringValue("");
}
Value* MapPartitionFn(const char* name, State* state,
@@ -131,207 +79,12 @@ Value* MapPartitionFn(const char* name, State* state,
if (args.empty()) return StringValue("");
std::string path;
- bool result = MapPartitionOnDeviceMapper(args[0], &path);
+ auto updater_runtime = state->updater->GetRuntime();
+ bool result = updater_runtime->MapPartitionOnDeviceMapper(args[0], &path);
return result ? StringValue(path) : StringValue("");
}
-namespace { // Ops
-
-struct OpParameters {
- std::vector<std::string> tokens;
- MetadataBuilder* builder;
-
- bool ExpectArgSize(size_t size) const {
- CHECK(!tokens.empty());
- auto actual = tokens.size() - 1;
- if (actual != size) {
- LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual;
- return false;
- }
- return true;
- }
- const std::string& op() const {
- CHECK(!tokens.empty());
- return tokens[0];
- }
- const std::string& arg(size_t pos) const {
- CHECK_LE(pos + 1, tokens.size());
- return tokens[pos + 1];
- }
- std::optional<uint64_t> uint_arg(size_t pos, const std::string& name) const {
- auto str = arg(pos);
- uint64_t ret;
- if (!ParseUint(str, &ret)) {
- LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str;
- return std::nullopt;
- }
- return ret;
- }
-};
-
-using OpFunction = std::function<bool(const OpParameters&)>;
-using OpMap = std::map<std::string, OpFunction>;
-
-bool PerformOpResize(const OpParameters& params) {
- if (!params.ExpectArgSize(2)) return false;
- const auto& partition_name = params.arg(0);
- auto size = params.uint_arg(1, "size");
- if (!size.has_value()) return false;
-
- auto partition = params.builder->FindPartition(partition_name);
- if (partition == nullptr) {
- LOG(ERROR) << "Failed to find partition " << partition_name
- << " in dynamic partition metadata.";
- return false;
- }
- if (!UnmapPartitionOnDeviceMapper(partition_name)) {
- LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing.";
- return false;
- }
- if (!params.builder->ResizePartition(partition, size.value())) {
- LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << ".";
- return false;
- }
- return true;
-}
-
-bool PerformOpRemove(const OpParameters& params) {
- if (!params.ExpectArgSize(1)) return false;
- const auto& partition_name = params.arg(0);
-
- if (!UnmapPartitionOnDeviceMapper(partition_name)) {
- LOG(ERROR) << "Cannot unmap " << partition_name << " before removing.";
- return false;
- }
- params.builder->RemovePartition(partition_name);
- return true;
-}
-
-bool PerformOpAdd(const OpParameters& params) {
- if (!params.ExpectArgSize(2)) return false;
- const auto& partition_name = params.arg(0);
- const auto& group_name = params.arg(1);
-
- if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) ==
- nullptr) {
- LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << ".";
- return false;
- }
- return true;
-}
-
-bool PerformOpMove(const OpParameters& params) {
- if (!params.ExpectArgSize(2)) return false;
- const auto& partition_name = params.arg(0);
- const auto& new_group = params.arg(1);
-
- auto partition = params.builder->FindPartition(partition_name);
- if (partition == nullptr) {
- LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group
- << " because it is not found.";
- return false;
- }
-
- auto old_group = partition->group_name();
- if (old_group != new_group) {
- if (!params.builder->ChangePartitionGroup(partition, new_group)) {
- LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group
- << " to group " << new_group << ".";
- return false;
- }
- }
- return true;
-}
-
-bool PerformOpAddGroup(const OpParameters& params) {
- if (!params.ExpectArgSize(2)) return false;
- const auto& group_name = params.arg(0);
- auto maximum_size = params.uint_arg(1, "maximum_size");
- if (!maximum_size.has_value()) return false;
-
- auto group = params.builder->FindGroup(group_name);
- if (group != nullptr) {
- LOG(ERROR) << "Cannot add group " << group_name << " because it already exists.";
- return false;
- }
-
- if (maximum_size.value() == 0) {
- LOG(WARNING) << "Adding group " << group_name << " with no size limits.";
- }
-
- if (!params.builder->AddGroup(group_name, maximum_size.value())) {
- LOG(ERROR) << "Failed to add group " << group_name << " with maximum size "
- << maximum_size.value() << ".";
- return false;
- }
- return true;
-}
-
-bool PerformOpResizeGroup(const OpParameters& params) {
- if (!params.ExpectArgSize(2)) return false;
- const auto& group_name = params.arg(0);
- auto new_size = params.uint_arg(1, "maximum_size");
- if (!new_size.has_value()) return false;
-
- auto group = params.builder->FindGroup(group_name);
- if (group == nullptr) {
- LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found.";
- return false;
- }
-
- auto old_size = group->maximum_size();
- if (old_size != new_size.value()) {
- if (!params.builder->ChangeGroupSize(group_name, new_size.value())) {
- LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to "
- << new_size.value() << ".";
- return false;
- }
- }
- return true;
-}
-
-std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
- const std::string& group_name) {
- auto partitions = builder->ListPartitionsInGroup(group_name);
- std::vector<std::string> partition_names;
- std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
- [](Partition* partition) { return partition->name(); });
- return partition_names;
-}
-
-bool PerformOpRemoveGroup(const OpParameters& params) {
- if (!params.ExpectArgSize(1)) return false;
- const auto& group_name = params.arg(0);
-
- auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
- if (!partition_names.empty()) {
- LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions ["
- << android::base::Join(partition_names, ", ") << "]";
- return false;
- }
- params.builder->RemoveGroupAndPartitions(group_name);
- return true;
-}
-
-bool PerformOpRemoveAllGroups(const OpParameters& params) {
- if (!params.ExpectArgSize(0)) return false;
-
- auto group_names = params.builder->ListGroups();
- for (const auto& group_name : group_names) {
- auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
- for (const auto& partition_name : partition_names) {
- if (!UnmapPartitionOnDeviceMapper(partition_name)) {
- LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name
- << ".";
- return false;
- }
- }
- params.builder->RemoveGroupAndPartitions(group_name);
- }
- return true;
-}
-
-} // namespace
+static constexpr char kMetadataUpdatedMarker[] = "/dynamic_partition_metadata.UPDATED";
Value* UpdateDynamicPartitionsFn(const char* name, State* state,
const std::vector<std::unique_ptr<Expr>>& argv) {
@@ -367,56 +120,8 @@ Value* UpdateDynamicPartitionsFn(const char* name, State* state,
}
}
- auto super_device = GetSuperDevice();
- auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0);
- if (builder == nullptr) {
- LOG(ERROR) << "Failed to load dynamic partition metadata.";
- return StringValue("");
- }
-
- static const OpMap op_map{
- // clang-format off
- {"resize", PerformOpResize},
- {"remove", PerformOpRemove},
- {"add", PerformOpAdd},
- {"move", PerformOpMove},
- {"add_group", PerformOpAddGroup},
- {"resize_group", PerformOpResizeGroup},
- {"remove_group", PerformOpRemoveGroup},
- {"remove_all_groups", PerformOpRemoveAllGroups},
- // clang-format on
- };
-
- std::vector<std::string> lines = android::base::Split(op_list_value->data, "\n");
- for (const auto& line : lines) {
- auto comment_idx = line.find('#');
- auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx);
- op_and_args = android::base::Trim(op_and_args);
- if (op_and_args.empty()) continue;
-
- auto tokens = android::base::Split(op_and_args, " ");
- const auto& op = tokens[0];
- auto it = op_map.find(op);
- if (it == op_map.end()) {
- LOG(ERROR) << "Unknown operation in op_list: " << op;
- return StringValue("");
- }
- OpParameters params;
- params.tokens = tokens;
- params.builder = builder.get();
- if (!it->second(params)) {
- return StringValue("");
- }
- }
-
- auto metadata = builder->Export();
- if (metadata == nullptr) {
- LOG(ERROR) << "Failed to export metadata.";
- return StringValue("");
- }
-
- if (!UpdatePartitionTable(super_device, *metadata, 0)) {
- LOG(ERROR) << "Failed to write metadata.";
+ auto updater_runtime = state->updater->GetRuntime();
+ if (!updater_runtime->UpdateDynamicPartitions(op_list_value->data)) {
return StringValue("");
}
diff --git a/updater/include/updater/build_info.h b/updater/include/updater/build_info.h
new file mode 100644
index 000000000..0073bfa4a
--- /dev/null
+++ b/updater/include/updater/build_info.h
@@ -0,0 +1,74 @@
+/*
+ * 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 <list>
+#include <map>
+#include <string>
+#include <string_view>
+
+#include <android-base/file.h>
+
+// This class serves as the aggregation of the fake block device information during update
+// simulation on host. In specific, it has the name of the block device, its mount point, and the
+// path to the temporary file that fakes this block device.
+class FakeBlockDevice {
+ public:
+ FakeBlockDevice(std::string block_device, std::string mount_point, std::string temp_file_path)
+ : blockdev_name(std::move(block_device)),
+ mount_point(std::move(mount_point)),
+ mounted_file_path(std::move(temp_file_path)) {}
+
+ std::string blockdev_name;
+ std::string mount_point;
+ std::string mounted_file_path; // path to the temp file that mocks the block device
+};
+
+// This class stores the information of the source build. For example, it creates and maintains
+// the temporary files to simulate the block devices on host. Therefore, the simulator runtime can
+// query the information and run the update on host.
+class BuildInfo {
+ public:
+ BuildInfo(const std::string_view work_dir, bool keep_images)
+ : work_dir_(work_dir), keep_images_(keep_images) {}
+ // Returns the value of the build properties.
+ std::string GetProperty(const std::string_view key, const std::string_view default_value) const;
+ // Returns the path to the mock block device.
+ std::string FindBlockDeviceName(const std::string_view name) const;
+ // Parses the given target-file, initializes the build properties and extracts the images.
+ bool ParseTargetFile(const std::string_view target_file_path, bool extracted_input);
+
+ std::string GetOemSettings() const {
+ return oem_settings_;
+ }
+ void SetOemSettings(const std::string_view oem_settings) {
+ oem_settings_ = oem_settings;
+ }
+
+ private:
+ // A map to store the system properties during simulation.
+ std::map<std::string, std::string, std::less<>> build_props_;
+ // A file that contains the oem properties.
+ std::string oem_settings_;
+ // A map from the blockdev_name to the FakeBlockDevice object, which contains the path to the
+ // temporary file.
+ std::map<std::string, FakeBlockDevice, std::less<>> blockdev_map_;
+
+ std::list<TemporaryFile> temp_files_;
+ std::string work_dir_; // A temporary directory to store the extracted image files
+ bool keep_images_;
+};
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..9f7847b4f
--- /dev/null
+++ b/updater/include/updater/simulator_runtime.h
@@ -0,0 +1,62 @@
+/*
+ * 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/build_info.h"
+
+class SimulatorRuntime : public UpdaterRuntimeInterface {
+ public:
+ explicit SimulatorRuntime(BuildInfo* 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;
+
+ bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override;
+ bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override;
+ bool UpdateDynamicPartitions(const std::string_view op_list_value) override;
+
+ private:
+ std::string FindBlockDeviceName(const std::string_view name) const override;
+
+ BuildInfo* 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..860d47a35
--- /dev/null
+++ b/updater/include/updater/target_files.h
@@ -0,0 +1,71 @@
+/*
+ * 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 <string>
+#include <string_view>
+#include <vector>
+
+#include <android-base/file.h>
+#include <ziparchive/zip_archive.h>
+
+// This class represents the mount information for each line in a fstab file.
+class FstabInfo {
+ public:
+ FstabInfo(std::string blockdev_name, std::string mount_point, std::string fs_type)
+ : blockdev_name(std::move(blockdev_name)),
+ mount_point(std::move(mount_point)),
+ fs_type(std::move(fs_type)) {}
+
+ std::string blockdev_name;
+ std::string mount_point;
+ std::string fs_type;
+};
+
+// This class parses a target file from a zip file or an extracted directory. It also provides the
+// function to read the its content for simulation.
+class TargetFile {
+ public:
+ TargetFile(std::string path, bool extracted_input)
+ : path_(std::move(path)), extracted_input_(extracted_input) {}
+
+ // Opens the input target file (or extracted directory) and parses the misc_info.txt.
+ bool Open();
+ // Parses the build properties in all possible locations and save them in |props_map|
+ bool GetBuildProps(std::map<std::string, std::string, std::less<>>* props_map) const;
+ // Parses the fstab and save the information about each partition to mount into |fstab_info_list|.
+ bool ParseFstabInfo(std::vector<FstabInfo>* fstab_info_list) const;
+ // Returns true if the given entry exists in the target file.
+ bool EntryExists(const std::string_view name) const;
+ // Extracts the image file |entry_name|. Returns true on success.
+ bool ExtractImage(const std::string_view entry_name, const FstabInfo& fstab_info,
+ const std::string_view work_dir, TemporaryFile* image_file) const;
+
+ private:
+ // Wrapper functions to read the entry from either the zipped target-file, or the extracted input
+ // directory.
+ bool ReadEntryToString(const std::string_view name, std::string* content) const;
+ bool ExtractEntryToTempFile(const std::string_view name, TemporaryFile* temp_file) const;
+
+ std::string path_; // Path to the zipped target-file or an extracted directory.
+ bool extracted_input_; // True if the target-file has been extracted.
+ ZipArchiveHandle handle_{ nullptr };
+
+ // The properties under META/misc_info.txt
+ std::map<std::string, std::string, std::less<>> misc_info_;
+};
diff --git a/updater/include/updater/updater.h b/updater/include/updater/updater.h
index f4a2fe874..8676b6038 100644
--- a/updater/include/updater/updater.h
+++ b/updater/include/updater/updater.h
@@ -14,22 +14,83 @@
* 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;
+ }
+ size_t GetMappedPackageLength() const override {
+ return mapped_package_.length;
+ }
+
+ 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..8fc066f6a
--- /dev/null
+++ b/updater/include/updater/updater_runtime.h
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+ bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override;
+ bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override;
+ bool UpdateDynamicPartitions(const std::string_view op_list_value) override;
+
+ private:
+ struct selabel_handle* sehandle_{ nullptr };
+};
diff --git a/updater/install.cpp b/updater/install.cpp
index 20a204a83..62ff87e76 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -53,45 +53,30 @@
#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 +88,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);
}
@@ -127,16 +112,22 @@ Value* PackageExtractFileFn(const char* name, State* state,
argv.size());
}
const std::string& zip_path = args[0];
- const std::string& dest_path = args[1];
+ 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("");
}
+ // Update the destination of package_extract_file if it's a block device. During simulation the
+ // destination will map to a fake file.
+ if (std::string block_device_name = state->updater->FindBlockDeviceName(dest_path);
+ !block_device_name.empty()) {
+ dest_path = block_device_name;
+ }
+
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)));
if (fd == -1) {
@@ -173,10 +164,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 +219,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,7 +265,12 @@ Value* PatchPartitionFn(const char* name, State* state,
return ErrorAbort(state, kArgsParsingFailure, "%s(): Invalid patch arg", name);
}
- bool result = PatchPartition(target, source, *values[0], nullptr);
+ if (!UpdateBlockDeviceNameForPartition(state->updater, &source) ||
+ !UpdateBlockDeviceNameForPartition(state->updater, &target)) {
+ return StringValue("");
+ }
+
+ bool result = PatchPartition(target, source, *values[0], nullptr, true);
return StringValue(result ? "t" : "");
}
@@ -313,26 +313,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 +340,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 +362,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 +420,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 +429,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 +454,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 +499,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 +522,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 +535,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 +562,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 +624,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 +639,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 +658,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 +688,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 +731,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 +819,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 +830,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 +846,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/otautil/mounts.cpp b/updater/mounts.cpp
index 951311bf3..943d35c75 100644
--- a/otautil/mounts.cpp
+++ b/updater/mounts.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "otautil/mounts.h"
+#include "mounts.h"
#include <errno.h>
#include <fcntl.h>
diff --git a/otautil/include/otautil/mounts.h b/updater/mounts.h
index 6786c8d2e..6786c8d2e 100644
--- a/otautil/include/otautil/mounts.h
+++ b/updater/mounts.h
diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp
new file mode 100644
index 000000000..3ed7bf337
--- /dev/null
+++ b/updater/simulator_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/simulator_runtime.h"
+
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <unordered_set>
+
+#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 "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 {
+ if (android::base::EndsWith(filename, "oem.prop")) {
+ return android::base::ReadFileToString(source_->GetOemSettings(), content);
+ }
+
+ 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;
+}
+
+bool SimulatorRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
+ std::string* path) {
+ *path = partition_name;
+ return true;
+}
+
+bool SimulatorRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
+ LOG(INFO) << "Skip unmapping " << partition_name;
+ return true;
+}
+
+bool SimulatorRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) {
+ const std::unordered_set<std::string> commands{
+ "resize", "remove", "add", "move",
+ "add_group", "resize_group", "remove_group", "remove_all_groups",
+ };
+
+ std::vector<std::string> lines = android::base::Split(std::string(op_list_value), "\n");
+ for (const auto& line : lines) {
+ if (line.empty() || line[0] == '#') continue;
+ auto tokens = android::base::Split(line, " ");
+ if (commands.find(tokens[0]) == commands.end()) {
+ LOG(ERROR) << "Unknown operation in op_list: " << line;
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/updater/target_files.cpp b/updater/target_files.cpp
new file mode 100644
index 000000000..919ec4e04
--- /dev/null
+++ b/updater/target_files.cpp
@@ -0,0 +1,287 @@
+/*
+ * 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"
+
+#include <unistd.h>
+
+#include <algorithm>
+#include <filesystem>
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <sparse/sparse.h>
+
+static bool SimgToImg(int input_fd, int output_fd) {
+ if (lseek64(input_fd, 0, SEEK_SET) == -1) {
+ PLOG(ERROR) << "Failed to lseek64 on the input sparse image";
+ return false;
+ }
+
+ if (lseek64(output_fd, 0, SEEK_SET) == -1) {
+ PLOG(ERROR) << "Failed to lseek64 on the output raw image";
+ return false;
+ }
+
+ std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> s_file(
+ sparse_file_import(input_fd, true, false), sparse_file_destroy);
+ if (!s_file) {
+ LOG(ERROR) << "Failed to import the sparse image.";
+ return false;
+ }
+
+ if (sparse_file_write(s_file.get(), output_fd, false, false, false) < 0) {
+ PLOG(ERROR) << "Failed to output the raw image file.";
+ return false;
+ }
+
+ return true;
+}
+
+static bool ParsePropertyFile(const std::string_view prop_content,
+ std::map<std::string, std::string, std::less<>>* props_map) {
+ LOG(INFO) << "Start parsing build property\n";
+ std::vector<std::string> lines = android::base::Split(std::string(prop_content), "\n");
+ for (const auto& line : lines) {
+ if (line.empty() || line[0] == '#') continue;
+ auto pos = line.find('=');
+ if (pos == std::string::npos) continue;
+ std::string key = line.substr(0, pos);
+ std::string value = line.substr(pos + 1);
+ LOG(INFO) << key << ": " << value;
+ props_map->emplace(key, value);
+ }
+
+ return true;
+}
+
+static bool ParseFstab(const std::string_view fstab, std::vector<FstabInfo>* fstab_info_list) {
+ LOG(INFO) << "parsing fstab\n";
+ std::vector<std::string> lines = android::base::Split(std::string(fstab), "\n");
+ for (const auto& line : lines) {
+ if (line.empty() || line[0] == '#') continue;
+
+ // <block_device> <mount_point> <fs_type> <mount_flags> optional:<fs_mgr_flags>
+ std::vector<std::string> tokens = android::base::Split(line, " ");
+ tokens.erase(std::remove(tokens.begin(), tokens.end(), ""), tokens.end());
+ if (tokens.size() != 4 && tokens.size() != 5) {
+ LOG(ERROR) << "Unexpected token size: " << tokens.size() << std::endl
+ << "Error parsing fstab line: " << line;
+ return false;
+ }
+
+ const auto& blockdev = tokens[0];
+ const auto& mount_point = tokens[1];
+ const auto& fs_type = tokens[2];
+ if (!android::base::StartsWith(mount_point, "/")) {
+ LOG(WARNING) << "mount point '" << mount_point << "' does not start with '/'";
+ continue;
+ }
+
+ // The simulator only supports ext4 and emmc for now.
+ if (fs_type != "ext4" && fs_type != "emmc") {
+ LOG(WARNING) << "Unsupported fs_type in " << line;
+ continue;
+ }
+
+ fstab_info_list->emplace_back(blockdev, mount_point, fs_type);
+ }
+
+ return true;
+}
+
+bool TargetFile::EntryExists(const std::string_view name) const {
+ if (extracted_input_) {
+ std::string entry_path = path_ + "/" + std::string(name);
+ if (access(entry_path.c_str(), O_RDONLY) != 0) {
+ PLOG(WARNING) << "Failed to access " << entry_path;
+ return false;
+ }
+ return true;
+ }
+
+ CHECK(handle_);
+ ZipEntry img_entry;
+ return FindEntry(handle_, name, &img_entry) == 0;
+}
+
+bool TargetFile::ReadEntryToString(const std::string_view name, std::string* content) const {
+ if (extracted_input_) {
+ std::string entry_path = path_ + "/" + std::string(name);
+ return android::base::ReadFileToString(entry_path, content);
+ }
+
+ CHECK(handle_);
+ ZipEntry entry;
+ if (auto find_err = FindEntry(handle_, name, &entry); find_err != 0) {
+ LOG(ERROR) << "failed to find " << name << " in the package: " << ErrorCodeString(find_err);
+ return false;
+ }
+
+ if (entry.uncompressed_length == 0) {
+ content->clear();
+ return true;
+ }
+
+ content->resize(entry.uncompressed_length);
+ if (auto extract_err = ExtractToMemory(
+ handle_, &entry, reinterpret_cast<uint8_t*>(&content->at(0)), entry.uncompressed_length);
+ extract_err != 0) {
+ LOG(ERROR) << "failed to read " << name << " from package: " << ErrorCodeString(extract_err);
+ return false;
+ }
+
+ return true;
+}
+
+bool TargetFile::ExtractEntryToTempFile(const std::string_view name,
+ TemporaryFile* temp_file) const {
+ if (extracted_input_) {
+ std::string entry_path = path_ + "/" + std::string(name);
+ return std::filesystem::copy_file(entry_path, temp_file->path,
+ std::filesystem::copy_options::overwrite_existing);
+ }
+
+ CHECK(handle_);
+ ZipEntry entry;
+ if (auto find_err = FindEntry(handle_, name, &entry); find_err != 0) {
+ LOG(ERROR) << "failed to find " << name << " in the package: " << ErrorCodeString(find_err);
+ return false;
+ }
+
+ if (auto status = ExtractEntryToFile(handle_, &entry, temp_file->fd); status != 0) {
+ LOG(ERROR) << "Failed to extract zip entry " << name << " : " << ErrorCodeString(status);
+ return false;
+ }
+ return true;
+}
+
+bool TargetFile::Open() {
+ if (!extracted_input_) {
+ if (auto ret = OpenArchive(path_.c_str(), &handle_); ret != 0) {
+ LOG(ERROR) << "failed to open source target file " << path_ << ": " << ErrorCodeString(ret);
+ return false;
+ }
+ }
+
+ // Parse the misc info.
+ std::string misc_info_content;
+ if (!ReadEntryToString("META/misc_info.txt", &misc_info_content)) {
+ return false;
+ }
+ if (!ParsePropertyFile(misc_info_content, &misc_info_)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool TargetFile::GetBuildProps(std::map<std::string, std::string, std::less<>>* props_map) const {
+ props_map->clear();
+ // Parse the source zip to mock the system props and block devices. We try all the possible
+ // locations for build props.
+ constexpr std::string_view kPropLocations[] = {
+ "SYSTEM/build.prop",
+ "VENDOR/build.prop",
+ "PRODUCT/build.prop",
+ "SYSTEM_EXT/build.prop",
+ "SYSTEM/vendor/build.prop",
+ "SYSTEM/product/build.prop",
+ "SYSTEM/system_ext/build.prop",
+ "ODM/build.prop", // legacy
+ "ODM/etc/build.prop",
+ "VENDOR/odm/build.prop", // legacy
+ "VENDOR/odm/etc/build.prop",
+ };
+ for (const auto& name : kPropLocations) {
+ std::string build_prop_content;
+ if (!ReadEntryToString(name, &build_prop_content)) {
+ continue;
+ }
+ std::map<std::string, std::string, std::less<>> props;
+ if (!ParsePropertyFile(build_prop_content, &props)) {
+ LOG(ERROR) << "Failed to parse build prop in " << name;
+ return false;
+ }
+ for (const auto& [key, value] : props) {
+ if (auto it = props_map->find(key); it != props_map->end() && it->second != value) {
+ LOG(WARNING) << "Property " << key << " has different values in property files, we got "
+ << it->second << " and " << value;
+ }
+ props_map->emplace(key, value);
+ }
+ }
+
+ return true;
+}
+
+bool TargetFile::ExtractImage(const std::string_view entry_name, const FstabInfo& fstab_info,
+ const std::string_view work_dir, TemporaryFile* image_file) const {
+ if (!EntryExists(entry_name)) {
+ return false;
+ }
+
+ // We don't need extra work for 'emmc'; use the image file as the block device.
+ if (fstab_info.fs_type == "emmc" || misc_info_.find("extfs_sparse_flag") == misc_info_.end()) {
+ if (!ExtractEntryToTempFile(entry_name, image_file)) {
+ return false;
+ }
+ } else { // treated as ext4 sparse image
+ TemporaryFile sparse_image{ std::string(work_dir) };
+ if (!ExtractEntryToTempFile(entry_name, &sparse_image)) {
+ return false;
+ }
+
+ // Convert the sparse image to raw.
+ if (!SimgToImg(sparse_image.fd, image_file->fd)) {
+ LOG(ERROR) << "Failed to convert " << fstab_info.mount_point << " to raw.";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool TargetFile::ParseFstabInfo(std::vector<FstabInfo>* fstab_info_list) const {
+ // Parse the fstab file and extract the image files. The location of the fstab actually depends
+ // on some flags e.g. "no_recovery", "recovery_as_boot". Here we just try all possibilities.
+ constexpr std::string_view kRecoveryFstabLocations[] = {
+ "RECOVERY/RAMDISK/system/etc/recovery.fstab",
+ "RECOVERY/RAMDISK/etc/recovery.fstab",
+ "BOOT/RAMDISK/system/etc/recovery.fstab",
+ "BOOT/RAMDISK/etc/recovery.fstab",
+ };
+ std::string fstab_content;
+ for (const auto& name : kRecoveryFstabLocations) {
+ if (std::string content; ReadEntryToString(name, &content)) {
+ fstab_content = std::move(content);
+ break;
+ }
+ }
+ if (fstab_content.empty()) {
+ LOG(ERROR) << "Failed to parse the recovery fstab file";
+ return false;
+ }
+
+ // Extract the images and convert them to raw.
+ if (!ParseFstab(fstab_content, fstab_info_list)) {
+ LOG(ERROR) << "Failed to mount the block devices for source build.";
+ return false;
+ }
+
+ return true;
+}
diff --git a/updater/update_simulator_main.cpp b/updater/update_simulator_main.cpp
new file mode 100644
index 000000000..6c6989bac
--- /dev/null
+++ b/updater/update_simulator_main.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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 <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "edify/expr.h"
+#include "otautil/error_code.h"
+#include "otautil/paths.h"
+#include "updater/blockimg.h"
+#include "updater/build_info.h"
+#include "updater/dynamic_partitions.h"
+#include "updater/install.h"
+#include "updater/simulator_runtime.h"
+#include "updater/updater.h"
+
+using namespace std::string_literals;
+
+void Usage(std::string_view name) {
+ LOG(INFO) << "Usage: " << name << "[--oem_settings <oem_property_file>]"
+ << "[--skip_functions <skip_function_file>]"
+ << " --source <source_target_file>"
+ << " --ota_package <ota_package>";
+}
+
+Value* SimulatorPlaceHolderFn(const char* name, State* /* state */,
+ const std::vector<std::unique_ptr<Expr>>& /* argv */) {
+ LOG(INFO) << "Skip function " << name << " in host simulation";
+ return StringValue("t");
+}
+
+int main(int argc, char** argv) {
+ // Write the logs to stdout.
+ android::base::InitLogging(argv, &android::base::StderrLogger);
+
+ std::string oem_settings;
+ std::string skip_function_file;
+ std::string source_target_file;
+ std::string package_name;
+ std::string work_dir;
+ bool keep_images = false;
+
+ constexpr struct option OPTIONS[] = {
+ { "keep_images", no_argument, nullptr, 0 },
+ { "oem_settings", required_argument, nullptr, 0 },
+ { "ota_package", required_argument, nullptr, 0 },
+ { "skip_functions", required_argument, nullptr, 0 },
+ { "source", required_argument, nullptr, 0 },
+ { "work_dir", required_argument, nullptr, 0 },
+ { nullptr, 0, nullptr, 0 },
+ };
+
+ int arg;
+ int option_index;
+ while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
+ if (arg != 0) {
+ LOG(ERROR) << "Invalid command argument";
+ Usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ auto option_name = OPTIONS[option_index].name;
+ // The same oem property file used during OTA generation. It's needed for file_getprop() to
+ // return the correct value for the source build.
+ if (option_name == "oem_settings"s) {
+ oem_settings = optarg;
+ } else if (option_name == "skip_functions"s) {
+ skip_function_file = optarg;
+ } else if (option_name == "source"s) {
+ source_target_file = optarg;
+ } else if (option_name == "ota_package"s) {
+ package_name = optarg;
+ } else if (option_name == "keep_images"s) {
+ keep_images = true;
+ } else if (option_name == "work_dir"s) {
+ work_dir = optarg;
+ } else {
+ Usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (source_target_file.empty() || package_name.empty()) {
+ Usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ // Configure edify's functions.
+ RegisterBuiltins();
+ RegisterInstallFunctions();
+ RegisterBlockImageFunctions();
+ RegisterDynamicPartitionsFunctions();
+
+ if (!skip_function_file.empty()) {
+ std::string content;
+ if (!android::base::ReadFileToString(skip_function_file, &content)) {
+ PLOG(ERROR) << "Failed to read " << skip_function_file;
+ return EXIT_FAILURE;
+ }
+
+ auto lines = android::base::Split(content, "\n");
+ for (const auto& line : lines) {
+ if (line.empty() || android::base::StartsWith(line, "#")) {
+ continue;
+ }
+ RegisterFunction(line, SimulatorPlaceHolderFn);
+ }
+ }
+
+ 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;
+ if (work_dir.empty()) {
+ work_dir = source_temp_dir.path;
+ }
+
+ BuildInfo source_build_info(work_dir, keep_images);
+ if (!source_build_info.ParseTargetFile(source_target_file, false)) {
+ LOG(ERROR) << "Failed to parse the target file " << source_target_file;
+ return EXIT_FAILURE;
+ }
+
+ if (!oem_settings.empty()) {
+ CHECK_EQ(0, access(oem_settings.c_str(), R_OK));
+ source_build_info.SetOemSettings(oem_settings);
+ }
+
+ Updater updater(std::make_unique<SimulatorRuntime>(&source_build_info));
+ if (!updater.Init(cmd_pipe.release(), package_name, false)) {
+ return EXIT_FAILURE;
+ }
+
+ if (!updater.RunUpdate()) {
+ return EXIT_FAILURE;
+ }
+
+ 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..33d5b5b47
--- /dev/null
+++ b/updater/updater_main.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 <openssl/crypto.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);
+
+ // Run the libcrypto KAT(known answer tests) based self tests.
+ if (BORINGSSL_self_test() != 1) {
+ LOG(ERROR) << "Failed to run the boringssl self tests";
+ return EXIT_FAILURE;
+ }
+
+ if (argc != 4 && argc != 5) {
+ LOG(ERROR) << "unexpected number of arguments: " << argc;
+ return EXIT_FAILURE;
+ }
+
+ 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 EXIT_FAILURE;
+ }
+
+ int fd;
+ if (!android::base::ParseInt(argv[2], &fd)) {
+ LOG(ERROR) << "Failed to parse fd in " << argv[2];
+ return EXIT_FAILURE;
+ }
+
+ 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 EXIT_FAILURE;
+ }
+ }
+
+ // 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 EXIT_FAILURE;
+ }
+
+ if (!updater.RunUpdate()) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/updater/updater_runtime.cpp b/updater/updater_runtime.cpp
new file mode 100644
index 000000000..c4222a56e
--- /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 "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());
+}
diff --git a/updater/updater_runtime_dynamic_partitions.cpp b/updater/updater_runtime_dynamic_partitions.cpp
new file mode 100644
index 000000000..be9250a81
--- /dev/null
+++ b/updater/updater_runtime_dynamic_partitions.cpp
@@ -0,0 +1,343 @@
+/*
+ * 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 <algorithm>
+#include <chrono>
+#include <iterator>
+#include <optional>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <libdm/dm.h>
+#include <liblp/builder.h>
+
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
+using android::fs_mgr::CreateLogicalPartition;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::LpMetadata;
+using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::Partition;
+using android::fs_mgr::PartitionOpener;
+
+static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
+
+static std::string GetSuperDevice() {
+ return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
+}
+
+static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
+ auto state = DeviceMapper::Instance().GetState(partition_name);
+ if (state == DmDeviceState::INVALID) {
+ return true;
+ }
+ if (state == DmDeviceState::ACTIVE) {
+ return DestroyLogicalPartition(partition_name);
+ }
+ LOG(ERROR) << "Unknown device mapper state: "
+ << static_cast<std::underlying_type_t<DmDeviceState>>(state);
+ return false;
+}
+
+bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
+ std::string* path) {
+ auto state = DeviceMapper::Instance().GetState(partition_name);
+ if (state == DmDeviceState::INVALID) {
+ CreateLogicalPartitionParams params = {
+ .block_device = GetSuperDevice(),
+ .metadata_slot = 0,
+ .partition_name = partition_name,
+ .force_writable = true,
+ .timeout_ms = kMapTimeout,
+ };
+ return CreateLogicalPartition(params, path);
+ }
+
+ if (state == DmDeviceState::ACTIVE) {
+ return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path);
+ }
+ LOG(ERROR) << "Unknown device mapper state: "
+ << static_cast<std::underlying_type_t<DmDeviceState>>(state);
+ return false;
+}
+
+bool UpdaterRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
+ return ::UnmapPartitionOnDeviceMapper(partition_name);
+}
+
+namespace { // Ops
+
+struct OpParameters {
+ std::vector<std::string> tokens;
+ MetadataBuilder* builder;
+
+ bool ExpectArgSize(size_t size) const {
+ CHECK(!tokens.empty());
+ auto actual = tokens.size() - 1;
+ if (actual != size) {
+ LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual;
+ return false;
+ }
+ return true;
+ }
+ const std::string& op() const {
+ CHECK(!tokens.empty());
+ return tokens[0];
+ }
+ const std::string& arg(size_t pos) const {
+ CHECK_LE(pos + 1, tokens.size());
+ return tokens[pos + 1];
+ }
+ std::optional<uint64_t> uint_arg(size_t pos, const std::string& name) const {
+ auto str = arg(pos);
+ uint64_t ret;
+ if (!android::base::ParseUint(str, &ret)) {
+ LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str;
+ return std::nullopt;
+ }
+ return ret;
+ }
+};
+
+using OpFunction = std::function<bool(const OpParameters&)>;
+using OpMap = std::map<std::string, OpFunction>;
+
+bool PerformOpResize(const OpParameters& params) {
+ if (!params.ExpectArgSize(2)) return false;
+ const auto& partition_name = params.arg(0);
+ auto size = params.uint_arg(1, "size");
+ if (!size.has_value()) return false;
+
+ auto partition = params.builder->FindPartition(partition_name);
+ if (partition == nullptr) {
+ LOG(ERROR) << "Failed to find partition " << partition_name
+ << " in dynamic partition metadata.";
+ return false;
+ }
+ if (!UnmapPartitionOnDeviceMapper(partition_name)) {
+ LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing.";
+ return false;
+ }
+ if (!params.builder->ResizePartition(partition, size.value())) {
+ LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << ".";
+ return false;
+ }
+ return true;
+}
+
+bool PerformOpRemove(const OpParameters& params) {
+ if (!params.ExpectArgSize(1)) return false;
+ const auto& partition_name = params.arg(0);
+
+ if (!UnmapPartitionOnDeviceMapper(partition_name)) {
+ LOG(ERROR) << "Cannot unmap " << partition_name << " before removing.";
+ return false;
+ }
+ params.builder->RemovePartition(partition_name);
+ return true;
+}
+
+bool PerformOpAdd(const OpParameters& params) {
+ if (!params.ExpectArgSize(2)) return false;
+ const auto& partition_name = params.arg(0);
+ const auto& group_name = params.arg(1);
+
+ if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) ==
+ nullptr) {
+ LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << ".";
+ return false;
+ }
+ return true;
+}
+
+bool PerformOpMove(const OpParameters& params) {
+ if (!params.ExpectArgSize(2)) return false;
+ const auto& partition_name = params.arg(0);
+ const auto& new_group = params.arg(1);
+
+ auto partition = params.builder->FindPartition(partition_name);
+ if (partition == nullptr) {
+ LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group
+ << " because it is not found.";
+ return false;
+ }
+
+ auto old_group = partition->group_name();
+ if (old_group != new_group) {
+ if (!params.builder->ChangePartitionGroup(partition, new_group)) {
+ LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group
+ << " to group " << new_group << ".";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool PerformOpAddGroup(const OpParameters& params) {
+ if (!params.ExpectArgSize(2)) return false;
+ const auto& group_name = params.arg(0);
+ auto maximum_size = params.uint_arg(1, "maximum_size");
+ if (!maximum_size.has_value()) return false;
+
+ auto group = params.builder->FindGroup(group_name);
+ if (group != nullptr) {
+ LOG(ERROR) << "Cannot add group " << group_name << " because it already exists.";
+ return false;
+ }
+
+ if (maximum_size.value() == 0) {
+ LOG(WARNING) << "Adding group " << group_name << " with no size limits.";
+ }
+
+ if (!params.builder->AddGroup(group_name, maximum_size.value())) {
+ LOG(ERROR) << "Failed to add group " << group_name << " with maximum size "
+ << maximum_size.value() << ".";
+ return false;
+ }
+ return true;
+}
+
+bool PerformOpResizeGroup(const OpParameters& params) {
+ if (!params.ExpectArgSize(2)) return false;
+ const auto& group_name = params.arg(0);
+ auto new_size = params.uint_arg(1, "maximum_size");
+ if (!new_size.has_value()) return false;
+
+ auto group = params.builder->FindGroup(group_name);
+ if (group == nullptr) {
+ LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found.";
+ return false;
+ }
+
+ auto old_size = group->maximum_size();
+ if (old_size != new_size.value()) {
+ if (!params.builder->ChangeGroupSize(group_name, new_size.value())) {
+ LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to "
+ << new_size.value() << ".";
+ return false;
+ }
+ }
+ return true;
+}
+
+std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
+ const std::string& group_name) {
+ auto partitions = builder->ListPartitionsInGroup(group_name);
+ std::vector<std::string> partition_names;
+ std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
+ [](Partition* partition) { return partition->name(); });
+ return partition_names;
+}
+
+bool PerformOpRemoveGroup(const OpParameters& params) {
+ if (!params.ExpectArgSize(1)) return false;
+ const auto& group_name = params.arg(0);
+
+ auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
+ if (!partition_names.empty()) {
+ LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions ["
+ << android::base::Join(partition_names, ", ") << "]";
+ return false;
+ }
+ params.builder->RemoveGroupAndPartitions(group_name);
+ return true;
+}
+
+bool PerformOpRemoveAllGroups(const OpParameters& params) {
+ if (!params.ExpectArgSize(0)) return false;
+
+ auto group_names = params.builder->ListGroups();
+ for (const auto& group_name : group_names) {
+ auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
+ for (const auto& partition_name : partition_names) {
+ if (!UnmapPartitionOnDeviceMapper(partition_name)) {
+ LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name
+ << ".";
+ return false;
+ }
+ }
+ params.builder->RemoveGroupAndPartitions(group_name);
+ }
+ return true;
+}
+
+} // namespace
+
+bool UpdaterRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) {
+ auto super_device = GetSuperDevice();
+ auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0);
+ if (builder == nullptr) {
+ LOG(ERROR) << "Failed to load dynamic partition metadata.";
+ return false;
+ }
+
+ static const OpMap op_map{
+ // clang-format off
+ {"resize", PerformOpResize},
+ {"remove", PerformOpRemove},
+ {"add", PerformOpAdd},
+ {"move", PerformOpMove},
+ {"add_group", PerformOpAddGroup},
+ {"resize_group", PerformOpResizeGroup},
+ {"remove_group", PerformOpRemoveGroup},
+ {"remove_all_groups", PerformOpRemoveAllGroups},
+ // clang-format on
+ };
+
+ std::vector<std::string> lines = android::base::Split(std::string(op_list_value), "\n");
+ for (const auto& line : lines) {
+ auto comment_idx = line.find('#');
+ auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx);
+ op_and_args = android::base::Trim(op_and_args);
+ if (op_and_args.empty()) continue;
+
+ auto tokens = android::base::Split(op_and_args, " ");
+ const auto& op = tokens[0];
+ auto it = op_map.find(op);
+ if (it == op_map.end()) {
+ LOG(ERROR) << "Unknown operation in op_list: " << op;
+ return false;
+ }
+ OpParameters params;
+ params.tokens = tokens;
+ params.builder = builder.get();
+ if (!it->second(params)) {
+ return false;
+ }
+ }
+
+ auto metadata = builder->Export();
+ if (metadata == nullptr) {
+ LOG(ERROR) << "Failed to export metadata.";
+ return false;
+ }
+
+ if (!UpdatePartitionTable(super_device, *metadata, 0)) {
+ LOG(ERROR) << "Failed to write metadata.";
+ return false;
+ }
+
+ return true;
+}
diff --git a/updater_sample/Android.bp b/updater_sample/Android.bp
index 845e07b70..a014248b0 100644
--- a/updater_sample/Android.bp
+++ b/updater_sample/Android.bp
@@ -15,7 +15,6 @@
android_app {
name: "SystemUpdaterSample",
sdk_version: "system_current",
- privileged: true,
srcs: ["src/**/*.java"],
diff --git a/updater_sample/AndroidManifest.xml b/updater_sample/AndroidManifest.xml
index 0a2511617..981cd8eba 100644
--- a/updater_sample/AndroidManifest.xml
+++ b/updater_sample/AndroidManifest.xml
@@ -20,6 +20,7 @@
<uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27" />
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
+ <uses-permission android:name="android.permission.INTERNET"/>
<application
android:icon="@mipmap/ic_launcher"
diff --git a/updater_sample/OWNERS b/updater_sample/OWNERS
deleted file mode 100644
index 5c1c3706c..000000000
--- a/updater_sample/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-zhaojiac@google.com
-zhomart@google.com
diff --git a/updater_sample/README.md b/updater_sample/README.md
index 2070ebc21..2e12a2fb9 100644
--- a/updater_sample/README.md
+++ b/updater_sample/README.md
@@ -191,6 +191,8 @@ privileged system app, so it's granted the required permissions to access
</privapp-permissions>
```
to `frameworks/base/data/etc/privapp-permissions-platform.xml`
+4. Add `privileged: true` to SystemUpdaterSample
+ [building rule](https://android.googlesource.com/platform/bootable/recovery/+/refs/heads/master/updater_sample/Android.bp).
5. Build sample app `make -j SystemUpdaterSample`.
6. Build Android `make -j`
7. [Flash the device](https://source.android.com/setup/build/running)
@@ -229,9 +231,9 @@ The commands are expected to be run from `$ANDROID_BUILD_TOP`.
1. Build `make -j SystemUpdaterSample` and `make -j SystemUpdaterSampleTests`.
2. Install app
- `adb install $OUT/system/priv-app/SystemUpdaterSample/SystemUpdaterSample.apk`
+ `adb install $OUT/system/app/SystemUpdaterSample/SystemUpdaterSample.apk`
3. Install tests
- `adb install $OUT/testcases/SystemUpdaterSampleTests/SystemUpdaterSampleTests.apk`
+ `adb install $OUT/testcases/SystemUpdaterSampleTests/arm64/SystemUpdaterSampleTests.apk`
4. Run tests
`adb shell am instrument -w com.example.android.systemupdatersample.tests/android.support.test.runner.AndroidJUnitRunner`
5. Run a test file