Merge "Snap for 5475808 from 3aff98aee837ac4a416498cfa26c8cc903e1226e to sdk-release" into sdk-release
diff --git a/Android.bp b/Android.bp
index afa0337..0eb5fd9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -26,77 +26,6 @@
],
}
-cc_library {
- name: "librecovery_ui",
- recovery_available: true,
-
- defaults: [
- "recovery_defaults",
- ],
-
- srcs: [
- "device.cpp",
- "screen_ui.cpp",
- "ui.cpp",
- "vr_ui.cpp",
- "wear_ui.cpp"
- ],
-
- static_libs: [
- "libminui",
- "libotautil",
- "libfstab",
- ],
-
- shared_libs: [
- "libbase",
- "libpng",
- "libz",
- ],
-}
-
-// Generic device that uses ScreenRecoveryUI.
-cc_library_static {
- name: "librecovery_ui_default",
- recovery_available: true,
-
- defaults: [
- "recovery_defaults",
- ],
-
- srcs: [
- "default_device.cpp",
- ],
-}
-
-// The default wear device that uses WearRecoveryUI.
-cc_library_static {
- name: "librecovery_ui_wear",
- recovery_available: true,
-
- defaults: [
- "recovery_defaults",
- ],
-
- srcs: [
- "wear_device.cpp",
- ],
-}
-
-// The default VR device that uses VrRecoveryUI.
-cc_library_static {
- name: "librecovery_ui_vr",
- recovery_available: true,
-
- defaults: [
- "recovery_defaults",
- ],
-
- srcs: [
- "vr_device.cpp",
- ],
-}
-
cc_library_static {
name: "librecovery_fastboot",
recovery_available: true,
@@ -113,6 +42,7 @@
"libbootloader_message",
"libcutils",
"liblog",
+ "librecovery_ui",
],
static_libs: [
@@ -133,32 +63,19 @@
"libbootloader_message",
"libcrypto",
"libcutils",
- "libext4_utils",
"libfs_mgr",
- "libfusesideload",
- "libhidl-gen-utils",
- "libhidlbase",
- "libhidltransport",
"liblog",
- "libpng",
- "libselinux",
- "libtinyxml2",
- "libutils",
- "libz",
"libziparchive",
],
static_libs: [
+ "libinstall",
"librecovery_fastboot",
"libminui",
- "libverifier",
"libotautil",
// external dependencies
"libhealthhalutils",
- "libvintf_recovery",
- "libvintf",
- "libfstab",
],
}
@@ -171,41 +88,12 @@
],
srcs: [
- "adb_install.cpp",
"fsck_unshare_blocks.cpp",
- "fuse_sdcard_provider.cpp",
- "install.cpp",
"recovery.cpp",
- "roots.cpp",
- ],
-
- include_dirs: [
- "system/vold",
- ],
-}
-
-cc_library_static {
- name: "libverifier",
- recovery_available: true,
-
- defaults: [
- "recovery_defaults",
- ],
-
- srcs: [
- "asn1_decoder.cpp",
- "verifier.cpp",
],
shared_libs: [
- "libbase",
- "libcrypto",
- "libcrypto_utils",
- "libziparchive",
- ],
-
- static_libs: [
- "libotautil",
+ "librecovery_ui",
],
}
@@ -214,16 +102,15 @@
recovery: true,
defaults: [
+ "libinstall_defaults",
"librecovery_defaults",
],
srcs: [
- "logging.cpp",
"recovery_main.cpp",
],
shared_libs: [
- "libminadbd_services",
"librecovery_ui",
],
@@ -235,6 +122,7 @@
required: [
"e2fsdroid.recovery",
"librecovery_ui_ext",
+ "minadbd",
"mke2fs.conf.recovery",
"mke2fs.recovery",
"recovery_deps",
@@ -250,7 +138,6 @@
],
srcs: [
- "logging.cpp",
"recovery-persist.cpp",
],
@@ -262,7 +149,6 @@
static_libs: [
"libotautil",
- "libfstab",
],
init_rc: [
@@ -279,7 +165,6 @@
],
srcs: [
- "logging.cpp",
"recovery-refresh.cpp",
],
@@ -290,7 +175,6 @@
static_libs: [
"libotautil",
- "libfstab",
],
init_rc: [
diff --git a/CleanSpec.mk b/CleanSpec.mk
index fec823e..a7ab0d9 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -44,9 +44,13 @@
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libminui_intermediates/import_includes)
$(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)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/adb_install.cpp b/adb_install.cpp
deleted file mode 100644
index 438da9f..0000000
--- a/adb_install.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2012 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 "adb_install.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-
-#include "common.h"
-#include "fuse_sideload.h"
-#include "install.h"
-#include "ui.h"
-
-int apply_from_adb(bool* wipe_cache) {
- modified_flash = true;
- // 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.
- if (usb_state != "none" && !SetUsbConfig("none")) {
- LOG(ERROR) << "Failed to clear USB config";
- return INSTALL_ERROR;
- }
-
- ui->Print(
- "\n\nNow send the package you want to apply\n"
- "to the device with \"adb sideload <filename>\"...\n");
-
- pid_t child;
- if ((child = fork()) == 0) {
- execl("/system/bin/recovery", "recovery", "--adbd", nullptr);
- _exit(EXIT_FAILURE);
- }
-
- if (!SetUsbConfig("sideload")) {
- LOG(ERROR) << "Failed to set usb config to sideload";
- return INSTALL_ERROR;
- }
-
- // How long (in seconds) we wait for the host to start sending us a package, before timing out.
- static constexpr int ADB_INSTALL_TIMEOUT = 300;
-
- // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host connects and starts serving a
- // package. Poll for its appearance. (Note that inotify doesn't work with FUSE.)
- int result = INSTALL_ERROR;
- int status;
- bool waited = false;
- for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
- if (waitpid(child, &status, WNOHANG) != 0) {
- result = INSTALL_ERROR;
- waited = true;
- break;
- }
-
- struct stat st;
- if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
- if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) {
- sleep(1);
- continue;
- } else {
- ui->Print("\nTimed out waiting for package.\n\n");
- result = INSTALL_ERROR;
- kill(child, SIGKILL);
- break;
- }
- }
- result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0);
- break;
- }
-
- if (!waited) {
- // Calling stat() on this magic filename signals the minadbd subprocess to shut down.
- struct stat st;
- stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
-
- // TODO: there should be a way to cancel waiting for a package (by pushing some button combo on
- // the device). For now you just have to 'adb sideload' a file that's not a valid package, like
- // "/dev/null".
- waitpid(child, &status, 0);
- }
-
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- if (WEXITSTATUS(status) == 3) {
- ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n");
- } else if (!WIFSIGNALED(status)) {
- ui->Print("\n(adbd status %d)\n", WEXITSTATUS(status));
- }
- }
-
- // Clean up before switching to the older state, for example setting the state
- // to none sets sys/class/android_usb/android0/enable to 0.
- if (!SetUsbConfig("none")) {
- LOG(ERROR) << "Failed to clear USB config";
- }
-
- if (usb_state != "none") {
- if (!SetUsbConfig(usb_state)) {
- LOG(ERROR) << "Failed to set USB config to " << usb_state;
- }
- }
-
- return result;
-}
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index f9383dd..90d8e86 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -76,7 +76,7 @@
}
android::base::unique_fd dev(open(partition.name.c_str(), O_RDONLY));
- if (!dev) {
+ if (dev == -1) {
PLOG(ERROR) << "Failed to open eMMC partition \"" << partition << "\"";
} else {
std::vector<unsigned char> buffer(partition.size);
diff --git a/common.h b/common.h
index 38bdd52..a524a41 100644
--- a/common.h
+++ b/common.h
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-#ifndef RECOVERY_COMMON_H
-#define RECOVERY_COMMON_H
-
-#include <stdarg.h>
-#include <stdio.h>
+#pragma once
#include <string>
@@ -31,7 +27,6 @@
extern struct selabel_handle* sehandle;
extern RecoveryUI* ui;
-extern bool modified_flash;
extern bool has_cache;
// The current stage, e.g. "1/2".
@@ -40,9 +35,4 @@
// The reason argument provided in "--reason=".
extern const char* reason;
-void ui_print(const char* format, ...) __printflike(1, 2);
-
bool is_ro_debuggable();
-
-bool SetUsbConfig(const std::string& state);
-#endif // RECOVERY_COMMON_H
diff --git a/etc/init.rc b/etc/init.rc
index fa8fe26..0822aba 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -73,7 +73,7 @@
critical
seclabel u:r:ueventd:s0
-service charger /charger -r
+service charger /system/bin/charger
critical
seclabel u:r:charger:s0
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 8458c99..14f5e4b 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -27,8 +27,7 @@
#include <android-base/properties.h>
#include <bootloader_message/bootloader_message.h>
-#include "device.h"
-#include "ui.h"
+#include "recovery_ui/ui.h"
static const std::vector<std::pair<std::string, Device::BuiltinAction>> kFastbootMenuActions{
{ "Reboot system now", Device::REBOOT },
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 53a2adc..1aa7de6 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -19,6 +19,6 @@
#include <string>
#include <vector>
-#include "device.h"
+#include "recovery_ui/device.h"
Device::BuiltinAction StartFastboot(Device* device, const std::vector<std::string>& args);
diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp
index ce6940a..0f8fffa 100644
--- a/fsck_unshare_blocks.cpp
+++ b/fsck_unshare_blocks.cpp
@@ -34,9 +34,8 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/unique_fd.h>
-#include <fstab/fstab.h>
-#include "roots.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";
diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp
deleted file mode 100644
index 46bdf17..0000000
--- a/fuse_sdcard_provider.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2014 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 "fuse_sdcard_provider.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <functional>
-
-#include <android-base/file.h>
-
-#include "fuse_sideload.h"
-
-struct file_data {
- int fd; // the underlying sdcard file
-
- uint64_t file_size;
- uint32_t block_size;
-};
-
-static int read_block_file(const file_data& fd, uint32_t block, uint8_t* buffer,
- uint32_t fetch_size) {
- off64_t offset = static_cast<off64_t>(block) * fd.block_size;
- if (TEMP_FAILURE_RETRY(lseek64(fd.fd, offset, SEEK_SET)) == -1) {
- fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno));
- return -EIO;
- }
-
- if (!android::base::ReadFully(fd.fd, buffer, fetch_size)) {
- fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
- return -EIO;
- }
-
- return 0;
-}
-
-bool start_sdcard_fuse(const char* path) {
- struct stat sb;
- if (stat(path, &sb) == -1) {
- fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno));
- return false;
- }
-
- file_data fd;
- fd.fd = open(path, O_RDONLY);
- if (fd.fd == -1) {
- fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
- return false;
- }
- fd.file_size = sb.st_size;
- fd.block_size = 65536;
-
- provider_vtab vtab;
- vtab.read_block = std::bind(&read_block_file, fd, std::placeholders::_1, std::placeholders::_2,
- std::placeholders::_3);
- vtab.close = [&fd]() { close(fd.fd); };
-
- // 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);
-
- return run_fuse_sideload(vtab, fd.file_size, fd.block_size) == 0;
-}
diff --git a/fuse_sdcard_provider.h b/fuse_sdcard_provider.h
deleted file mode 100644
index bdc60f2..0000000
--- a/fuse_sdcard_provider.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#ifndef __FUSE_SDCARD_PROVIDER_H
-#define __FUSE_SDCARD_PROVIDER_H
-
-bool start_sdcard_fuse(const char* path);
-
-#endif
diff --git a/fuse_sideload/Android.bp b/fuse_sideload/Android.bp
index 90c4c22..8548548 100644
--- a/fuse_sideload/Android.bp
+++ b/fuse_sideload/Android.bp
@@ -16,14 +16,17 @@
name: "libfusesideload",
recovery_available: true,
+ defaults: [
+ "recovery_defaults",
+ ],
+
cflags: [
"-D_XOPEN_SOURCE",
"-D_GNU_SOURCE",
- "-Wall",
- "-Werror",
],
srcs: [
+ "fuse_provider.cpp",
"fuse_sideload.cpp",
],
diff --git a/fuse_sideload/fuse_provider.cpp b/fuse_sideload/fuse_provider.cpp
new file mode 100644
index 0000000..58786f5
--- /dev/null
+++ b/fuse_sideload/fuse_provider.cpp
@@ -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.
+ */
+
+#include "fuse_provider.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include <android-base/file.h>
+
+#include "fuse_sideload.h"
+
+FuseFileDataProvider::FuseFileDataProvider(const std::string& path, uint32_t block_size) {
+ struct stat sb;
+ if (stat(path.c_str(), &sb) == -1) {
+ fprintf(stderr, "failed to stat %s: %s\n", path.c_str(), strerror(errno));
+ return;
+ }
+
+ fd_.reset(open(path.c_str(), O_RDONLY));
+ if (fd_ == -1) {
+ fprintf(stderr, "failed to open %s: %s\n", path.c_str(), strerror(errno));
+ return;
+ }
+ file_size_ = sb.st_size;
+ fuse_block_size_ = 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_;
+ if (fetch_size > file_size_ || offset > file_size_ - fetch_size) {
+ fprintf(stderr,
+ "Out of bound read, start block: %" PRIu32 ", fetch size: %" PRIu32
+ ", file size %" PRIu64 "\n",
+ start_block, fetch_size, file_size_);
+ return false;
+ }
+
+ if (!android::base::ReadFullyAtOffset(fd_, buffer, fetch_size, offset)) {
+ fprintf(stderr, "Failed to read fetch size: %" PRIu32 " bytes data at offset %" PRIu64 ": %s\n",
+ fetch_size, offset, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+void FuseFileDataProvider::Close() {
+ fd_.reset();
+}
diff --git a/fuse_sideload/fuse_sideload.cpp b/fuse_sideload/fuse_sideload.cpp
index 1c7e98f..3d94803 100644
--- a/fuse_sideload/fuse_sideload.cpp
+++ b/fuse_sideload/fuse_sideload.cpp
@@ -76,7 +76,7 @@
struct fuse_data {
android::base::unique_fd ffd; // file descriptor for the fuse socket
- provider_vtab vtab;
+ FuseDataProvider* provider; // Provider of the source data.
uint64_t file_size; // bytes
@@ -236,7 +236,7 @@
return 0;
}
- size_t fetch_size = fd->block_size;
+ uint32_t fetch_size = fd->block_size;
if (block * fd->block_size + fetch_size > fd->file_size) {
// If we're reading the last (partial) block of the file, expect a shorter response from the
// host, and pad the rest of the block with zeroes.
@@ -244,8 +244,9 @@
memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size);
}
- int result = fd->vtab.read_block(block, fd->block_data, fetch_size);
- if (result < 0) return result;
+ if (!fd->provider->ReadBlockAlignedData(fd->block_data, fetch_size, block)) {
+ return -EIO;
+ }
fd->curr_block = block;
@@ -340,12 +341,14 @@
return NO_STATUS;
}
-int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size,
- const char* mount_point) {
+int run_fuse_sideload(std::unique_ptr<FuseDataProvider>&& provider, const char* mount_point) {
// If something's already mounted on our mountpoint, try to remove it. (Mostly in case of a
// previous abnormal exit.)
umount2(mount_point, MNT_FORCE);
+ uint64_t file_size = provider->file_size();
+ uint32_t block_size = provider->fuse_block_size();
+
// fs/fuse/inode.c in kernel code uses the greater of 4096 and the passed-in max_read.
if (block_size < 4096) {
fprintf(stderr, "block size (%u) is too small\n", block_size);
@@ -357,7 +360,7 @@
}
fuse_data fd = {};
- fd.vtab = vtab;
+ fd.provider = provider.get();
fd.file_size = file_size;
fd.block_size = block_size;
fd.file_blocks = (file_size == 0) ? 0 : (((file_size - 1) / block_size) + 1);
@@ -389,7 +392,7 @@
}
fd.ffd.reset(open("/dev/fuse", O_RDWR));
- if (!fd.ffd) {
+ if (fd.ffd == -1) {
perror("open /dev/fuse");
result = -1;
goto done;
@@ -479,7 +482,7 @@
}
done:
- fd.vtab.close();
+ provider->Close();
if (umount2(mount_point, MNT_DETACH) == -1) {
fprintf(stderr, "fuse_sideload umount failed: %s\n", strerror(errno));
diff --git a/fuse_sideload/include/fuse_provider.h b/fuse_sideload/include/fuse_provider.h
new file mode 100644
index 0000000..59059cf
--- /dev/null
+++ b/fuse_sideload/include/fuse_provider.h
@@ -0,0 +1,72 @@
+/*
+ * 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 <android-base/unique_fd.h>
+
+// This is the base class to read data from source and provide the data to FUSE.
+class FuseDataProvider {
+ public:
+ FuseDataProvider(uint64_t file_size, uint32_t block_size)
+ : file_size_(file_size), fuse_block_size_(block_size) {}
+
+ virtual ~FuseDataProvider() = default;
+
+ uint64_t file_size() const {
+ return file_size_;
+ }
+ uint32_t fuse_block_size() const {
+ return fuse_block_size_;
+ }
+
+ // Reads |fetch_size| bytes data starting from |start_block|. Puts the result in |buffer|.
+ virtual bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
+ uint32_t start_block) const = 0;
+
+ virtual void Close() {}
+
+ protected:
+ FuseDataProvider() = default;
+
+ // Size in bytes of the file to read.
+ uint64_t file_size_ = 0;
+ // Block size passed to the fuse, this is different from the block size of the block device.
+ uint32_t fuse_block_size_ = 0;
+};
+
+// This class reads data from a file.
+class FuseFileDataProvider : public FuseDataProvider {
+ public:
+ FuseFileDataProvider(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 {
+ return fd_ != -1;
+ }
+
+ void Close() override;
+
+ private:
+ // The underlying source to read data from.
+ android::base::unique_fd fd_;
+};
diff --git a/fuse_sideload/include/fuse_sideload.h b/fuse_sideload/include/fuse_sideload.h
index 1b34cbd..1b7759a 100644
--- a/fuse_sideload/include/fuse_sideload.h
+++ b/fuse_sideload/include/fuse_sideload.h
@@ -17,7 +17,9 @@
#ifndef __FUSE_SIDELOAD_H
#define __FUSE_SIDELOAD_H
-#include <functional>
+#include <memory>
+
+#include "fuse_provider.h"
// Define the filenames created by the sideload FUSE filesystem.
static constexpr const char* FUSE_SIDELOAD_HOST_MOUNTPOINT = "/sideload";
@@ -26,15 +28,7 @@
static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_FLAG = "exit";
static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_PATHNAME = "/sideload/exit";
-struct provider_vtab {
- // read a block
- std::function<int(uint32_t block, uint8_t* buffer, uint32_t fetch_size)> read_block;
-
- // close down
- std::function<void(void)> close;
-};
-
-int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size,
+int run_fuse_sideload(std::unique_ptr<FuseDataProvider>&& provider,
const char* mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT);
#endif
diff --git a/install/Android.bp b/install/Android.bp
new file mode 100644
index 0000000..b18e290
--- /dev/null
+++ b/install/Android.bp
@@ -0,0 +1,82 @@
+// 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: "libinstall_defaults",
+
+ defaults: [
+ "recovery_defaults",
+ ],
+
+ header_libs: [
+ "libminadbd_headers",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libbootloader_message",
+ "libcrypto",
+ "libext4_utils",
+ "libfs_mgr",
+ "libfusesideload",
+ "libhidl-gen-utils",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libselinux",
+ "libtinyxml2",
+ "libutils",
+ "libz",
+ "libziparchive",
+ ],
+
+ static_libs: [
+ "libotautil",
+
+ // external dependencies
+ "libvintf_recovery",
+ "libvintf",
+ ],
+}
+
+cc_library_static {
+ name: "libinstall",
+ recovery_available: true,
+
+ defaults: [
+ "libinstall_defaults",
+ ],
+
+ srcs: [
+ "adb_install.cpp",
+ "asn1_decoder.cpp",
+ "fuse_sdcard_install.cpp",
+ "install.cpp",
+ "package.cpp",
+ "verifier.cpp",
+ "wipe_data.cpp",
+ ],
+
+ shared_libs: [
+ "librecovery_ui",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ export_shared_lib_headers: [
+ "librecovery_ui",
+ ],
+}
diff --git a/install/adb_install.cpp b/install/adb_install.cpp
new file mode 100644
index 0000000..548b6e5
--- /dev/null
+++ b/install/adb_install.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2012 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/adb_install.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <functional>
+#include <map>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/memory.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "fuse_sideload.h"
+#include "install/install.h"
+#include "minadbd_types.h"
+#include "recovery_ui/ui.h"
+
+using CommandFunction = std::function<bool()>;
+
+static bool SetUsbConfig(const std::string& state) {
+ android::base::SetProperty("sys.usb.config", state);
+ return android::base::WaitForProperty("sys.usb.state", state);
+}
+
+// Parses the minadbd command in |message|; returns MinadbdCommands::kError upon errors.
+static MinadbdCommands ParseMinadbdCommands(const std::string& message) {
+ if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) {
+ LOG(ERROR) << "Failed to parse command in message " << message;
+ return MinadbdCommands::kError;
+ }
+
+ auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix));
+ auto cmd_code = android::base::get_unaligned<uint32_t>(cmd_code_string.c_str());
+ if (cmd_code >= static_cast<uint32_t>(MinadbdCommands::kError)) {
+ LOG(ERROR) << "Unsupported command code: " << cmd_code;
+ return MinadbdCommands::kError;
+ }
+
+ return static_cast<MinadbdCommands>(cmd_code);
+}
+
+static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) {
+ char message[kMinadbdMessageSize];
+ memcpy(message, kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix));
+ android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), status);
+
+ if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) {
+ PLOG(ERROR) << "Failed to write message " << message;
+ return false;
+ }
+ return true;
+}
+
+// Installs the package from FUSE. Returns true if the installation succeeds, and false otherwise.
+static bool AdbInstallPackageHandler(RecoveryUI* ui, int* 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
+ // appearance. (Note that inotify doesn't work with FUSE.)
+ constexpr int ADB_INSTALL_TIMEOUT = 15;
+ *result = INSTALL_ERROR;
+ for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
+ struct stat st;
+ if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
+ if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) {
+ sleep(1);
+ continue;
+ } else {
+ ui->Print("\nTimed out waiting for fuse to be ready.\n\n");
+ break;
+ }
+ }
+ *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui);
+ break;
+ }
+
+ // Calling stat() on this magic filename signals the FUSE to exit.
+ struct stat st;
+ stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
+ return *result == INSTALL_SUCCESS;
+}
+
+// Parses and executes the command from minadbd. Returns false if we enter an invalid state so that
+// the caller can kill the minadbd service properly.
+static bool HandleMessageFromMinadbd(
+ int socket_fd, const std::map<MinadbdCommands, CommandFunction>& command_map) {
+ char buffer[kMinadbdMessageSize];
+ if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) {
+ PLOG(ERROR) << "Failed to read message from minadbd";
+ return false;
+ }
+
+ std::string message(buffer, buffer + kMinadbdMessageSize);
+ auto command_type = ParseMinadbdCommands(message);
+ if (command_type == MinadbdCommands::kError) {
+ return false;
+ }
+ if (command_map.find(command_type) == command_map.end()) {
+ LOG(ERROR) << "Unsupported command: "
+ << android::base::get_unaligned<unsigned int>(
+ message.substr(strlen(kMinadbdCommandPrefix)).c_str());
+ return false;
+ }
+
+ // We have received a valid command, execute the corresponding function.
+ const auto& command_func = command_map.at(command_type);
+ if (!command_func()) {
+ LOG(ERROR) << "Failed to execute command " << static_cast<unsigned int>(command_type);
+ return WriteStatusToFd(MinadbdCommandStatus::kFailure, socket_fd);
+ }
+ return WriteStatusToFd(MinadbdCommandStatus::kSuccess, socket_fd);
+}
+
+// TODO(xunchang) add a wrapper function and kill the minadbd service there.
+static void ListenAndExecuteMinadbdCommands(
+ pid_t minadbd_pid, android::base::unique_fd&& socket_fd,
+ const std::map<MinadbdCommands, CommandFunction>& command_map) {
+ android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC));
+ if (epoll_fd == -1) {
+ PLOG(ERROR) << "Failed to create epoll";
+ kill(minadbd_pid, SIGKILL);
+ return;
+ }
+
+ constexpr int EPOLL_MAX_EVENTS = 10;
+ struct epoll_event ev = {};
+ ev.events = EPOLLIN | EPOLLHUP;
+ ev.data.fd = socket_fd.get();
+ struct epoll_event events[EPOLL_MAX_EVENTS];
+ if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, socket_fd.get(), &ev) == -1) {
+ PLOG(ERROR) << "Failed to add socket fd to epoll";
+ kill(minadbd_pid, SIGKILL);
+ return;
+ }
+
+ // Set the timeout to be 300s when waiting for minadbd commands.
+ constexpr int TIMEOUT_MILLIS = 300 * 1000;
+ while (true) {
+ // Poll for the status change of the socket_fd, and handle the message if the fd is ready to
+ // read.
+ int event_count =
+ TEMP_FAILURE_RETRY(epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, TIMEOUT_MILLIS));
+ if (event_count == -1) {
+ PLOG(ERROR) << "Failed to wait for epoll events";
+ kill(minadbd_pid, SIGKILL);
+ return;
+ }
+ if (event_count == 0) {
+ LOG(ERROR) << "Timeout waiting for messages from minadbd";
+ kill(minadbd_pid, SIGKILL);
+ return;
+ }
+
+ for (int n = 0; n < event_count; n++) {
+ if (events[n].events & EPOLLHUP) {
+ LOG(INFO) << "Socket has been closed";
+ kill(minadbd_pid, SIGKILL);
+ return;
+ }
+ if (!HandleMessageFromMinadbd(socket_fd.get(), command_map)) {
+ kill(minadbd_pid, SIGKILL);
+ return;
+ }
+ }
+ }
+}
+
+// Recovery starts minadbd service as a child process, and spawns another thread to listen for the
+// message from minadbd through a socket pair. Here is an example to execute one command from adb
+// host.
+// a. recovery b. listener thread c. minadbd service
+//
+// a1. create socket pair
+// a2. fork minadbd service
+// c3. wait for the adb commands
+// from host
+// c4. after receiving host commands:
+// 1) set up pre-condition (i.e.
+// start fuse for adb sideload)
+// 2) issue command through
+// socket.
+// 3) wait for result
+// a5. start listener thread
+// b6. listen for message from
+// minadbd in a loop.
+// b7. After receiving a minadbd
+// command from socket
+// 1) execute the command function
+// 2) send the result back to
+// minadbd
+// ......
+// c8. exit upon receiving the
+// result
+// a9. wait for listener thread
+// to exit.
+//
+// a10. wait for minadbd to
+// exit
+// b11. exit the listening loop
+//
+static void CreateMinadbdServiceAndExecuteCommands(
+ const std::map<MinadbdCommands, CommandFunction>& command_map) {
+ signal(SIGPIPE, SIG_IGN);
+
+ android::base::unique_fd recovery_socket;
+ android::base::unique_fd minadbd_socket;
+ if (!android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &recovery_socket, &minadbd_socket)) {
+ PLOG(ERROR) << "Failed to create socket";
+ return;
+ }
+
+ pid_t child = fork();
+ if (child == -1) {
+ PLOG(ERROR) << "Failed to fork child process";
+ return;
+ }
+ if (child == 0) {
+ recovery_socket.reset();
+ execl("/system/bin/minadbd", "minadbd", "--socket_fd",
+ std::to_string(minadbd_socket.release()).c_str(), nullptr);
+
+ _exit(EXIT_FAILURE);
+ }
+
+ minadbd_socket.reset();
+
+ // We need to call SetUsbConfig() after forking minadbd service. Because the function waits for
+ // the usb state to be updated, which depends on sys.usb.ffs.ready=1 set in the adb daemon.
+ if (!SetUsbConfig("sideload")) {
+ LOG(ERROR) << "Failed to set usb config to sideload";
+ return;
+ }
+
+ std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket),
+ std::ref(command_map));
+
+ if (listener_thread.joinable()) {
+ listener_thread.join();
+ }
+
+ int status;
+ waitpid(child, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ if (WEXITSTATUS(status) == MinadbdErrorCode::kMinadbdAdbVersionError) {
+ LOG(ERROR) << "\nYou need adb 1.0.32 or newer to sideload\nto this device.\n";
+ } else if (!WIFSIGNALED(status)) {
+ LOG(ERROR) << "\n(adbd status " << WEXITSTATUS(status) << ")";
+ }
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+}
+
+int apply_from_adb(RecoveryUI* ui) {
+ // 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.
+ if (usb_state != "none" && !SetUsbConfig("none")) {
+ LOG(ERROR) << "Failed to clear USB config";
+ return INSTALL_ERROR;
+ }
+
+ ui->Print(
+ "\n\nNow send the package you want to apply\n"
+ "to the device with \"adb sideload <filename>\"...\n");
+
+ int install_result = INSTALL_ERROR;
+ std::map<MinadbdCommands, CommandFunction> command_map{
+ { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) },
+ };
+
+ CreateMinadbdServiceAndExecuteCommands(command_map);
+
+ // Clean up before switching to the older state, for example setting the state
+ // to none sets sys/class/android_usb/android0/enable to 0.
+ if (!SetUsbConfig("none")) {
+ LOG(ERROR) << "Failed to clear USB config";
+ }
+
+ if (usb_state != "none") {
+ if (!SetUsbConfig(usb_state)) {
+ LOG(ERROR) << "Failed to set USB config to " << usb_state;
+ }
+ }
+
+ return install_result;
+}
diff --git a/asn1_decoder.cpp b/install/asn1_decoder.cpp
similarity index 98%
rename from asn1_decoder.cpp
rename to install/asn1_decoder.cpp
index 285214f..2d81a6e 100644
--- a/asn1_decoder.cpp
+++ b/install/asn1_decoder.cpp
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-#include "asn1_decoder.h"
-
-#include <stdint.h>
+#include "private/asn1_decoder.h"
int asn1_context::peek_byte() const {
if (length_ == 0) {
diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_sdcard_install.cpp
new file mode 100644
index 0000000..1aa8768
--- /dev/null
+++ b/install/fuse_sdcard_install.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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/fuse_sdcard_install.h"
+
+#include <dirent.h>
+#include <signal.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "bootloader_message/bootloader_message.h"
+#include "fuse_provider.h"
+#include "fuse_sideload.h"
+#include "install/install.h"
+#include "otautil/roots.h"
+
+static constexpr const char* SDCARD_ROOT = "/sdcard";
+// How long (in seconds) we wait for the fuse-provided package file to
+// appear, before timing out.
+static constexpr int SDCARD_INSTALL_TIMEOUT = 10;
+
+// Set the BCB to reboot back into recovery (it won't resume the install from
+// sdcard though).
+static void SetSdcardUpdateBootloaderMessage() {
+ std::vector<std::string> options;
+ std::string err;
+ if (!update_bootloader_message(options, &err)) {
+ LOG(ERROR) << "Failed to set BCB message: " << err;
+ }
+}
+
+// Returns the selected filename, or an empty string.
+static std::string BrowseDirectory(const std::string& path, Device* device, RecoveryUI* ui) {
+ ensure_path_mounted(path);
+
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
+ if (!d) {
+ PLOG(ERROR) << "error opening " << path;
+ return "";
+ }
+
+ std::vector<std::string> dirs;
+ std::vector<std::string> entries{ "../" }; // "../" is always the first entry.
+
+ dirent* de;
+ while ((de = readdir(d.get())) != nullptr) {
+ std::string name(de->d_name);
+
+ if (de->d_type == DT_DIR) {
+ // Skip "." and ".." entries.
+ if (name == "." || name == "..") continue;
+ dirs.push_back(name + "/");
+ } else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) {
+ entries.push_back(name);
+ }
+ }
+
+ std::sort(dirs.begin(), dirs.end());
+ std::sort(entries.begin(), entries.end());
+
+ // Append dirs to the entries list.
+ entries.insert(entries.end(), dirs.begin(), dirs.end());
+
+ std::vector<std::string> headers{ "Choose a package to install:", path };
+
+ size_t chosen_item = 0;
+ while (true) {
+ chosen_item = ui->ShowMenu(
+ headers, entries, chosen_item, true,
+ std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
+
+ // Return if WaitKey() was interrupted.
+ if (chosen_item == static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED)) {
+ return "";
+ }
+
+ const std::string& item = entries[chosen_item];
+ if (chosen_item == 0) {
+ // Go up but continue browsing (if the caller is BrowseDirectory).
+ return "";
+ }
+
+ std::string new_path = path + "/" + item;
+ if (new_path.back() == '/') {
+ // Recurse down into a subdirectory.
+ new_path.pop_back();
+ std::string result = BrowseDirectory(new_path, device, ui);
+ if (!result.empty()) return result;
+ } else {
+ // Selected a zip file: return the path to the caller.
+ return new_path;
+ }
+ }
+
+ // Unreachable.
+}
+
+static bool StartSdcardFuse(const std::string& path) {
+ auto file_data_reader = std::make_unique<FuseFileDataProvider>(path, 65536);
+
+ if (!file_data_reader->Valid()) {
+ 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);
+
+ 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;
+ }
+
+ 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;
+ }
+
+ ui->Print("\n-- Install %s ...\n", path.c_str());
+ SetSdcardUpdateBootloaderMessage();
+
+ // 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);
+
+ _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;
+ int status;
+ bool waited = false;
+ for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
+ if (waitpid(child, &status, WNOHANG) == -1) {
+ result = INSTALL_ERROR;
+ waited = true;
+ break;
+ }
+
+ struct stat sb;
+ if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &sb) == -1) {
+ if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT - 1) {
+ sleep(1);
+ continue;
+ } else {
+ LOG(ERROR) << "Timed out waiting for the fuse-provided package.";
+ result = INSTALL_ERROR;
+ kill(child, SIGKILL);
+ break;
+ }
+ }
+
+ result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui);
+ break;
+ }
+
+ if (!waited) {
+ // Calling stat() on this magic filename signals the fuse
+ // filesystem to shut down.
+ struct stat sb;
+ stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &sb);
+
+ waitpid(child, &status, 0);
+ }
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ LOG(ERROR) << "Error exit from the fuse process: " << WEXITSTATUS(status);
+ }
+
+ ensure_path_unmounted(SDCARD_ROOT);
+ return result;
+}
diff --git a/adb_install.h b/install/include/install/adb_install.h
similarity index 86%
rename from adb_install.h
rename to install/include/install/adb_install.h
index cc3ca26..f7b065b 100644
--- a/adb_install.h
+++ b/install/include/install/adb_install.h
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-#ifndef _ADB_INSTALL_H
-#define _ADB_INSTALL_H
+#pragma once
-int apply_from_adb(bool* wipe_cache);
+#include <recovery_ui/ui.h>
-#endif
+int apply_from_adb(RecoveryUI* ui);
diff --git a/default_device.cpp b/install/include/install/fuse_sdcard_install.h
similarity index 75%
copy from default_device.cpp
copy to install/include/install/fuse_sdcard_install.h
index a971866..d9214ca 100644
--- a/default_device.cpp
+++ b/install/include/install/fuse_sdcard_install.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 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,9 @@
* limitations under the License.
*/
-#include "device.h"
-#include "screen_ui.h"
+#pragma once
-Device* make_device() {
- return new Device(new ScreenRecoveryUI);
-}
+#include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
+
+int ApplyFromSdcard(Device* device, RecoveryUI* ui);
diff --git a/install.h b/install/include/install/install.h
similarity index 68%
rename from install.h
rename to install/include/install/install.h
index c6db1d1..1e41b48 100644
--- a/install.h
+++ b/install/include/install/install.h
@@ -14,16 +14,19 @@
* limitations under the License.
*/
-#ifndef RECOVERY_INSTALL_H_
-#define RECOVERY_INSTALL_H_
+#pragma once
#include <stddef.h>
#include <map>
#include <string>
+#include <vector>
#include <ziparchive/zip_archive.h>
+#include "package.h"
+#include "recovery_ui/ui.h"
+
enum InstallResult {
INSTALL_SUCCESS,
INSTALL_ERROR,
@@ -40,19 +43,23 @@
BRICK,
};
-// Installs the given update package. If INSTALL_SUCCESS is returned and *wipe_cache is true on
-// exit, caller should wipe the cache partition.
-int install_package(const std::string& package, bool* wipe_cache, bool needs_mount,
- int retry_count);
+// 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);
-// Verify the package by ota keys. Return true if the package is verified successfully,
-// otherwise return false.
-bool verify_package(const unsigned char* package_data, size_t package_size);
+// Verifies the package by ota keys. Returns true if the package is verified successfully,
+// otherwise returns false.
+bool verify_package(Package* package, RecoveryUI* ui);
// Reads meta data file of the package; parses each line in the format "key=value"; and writes the
// 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);
@@ -61,5 +68,3 @@
// 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);
-
-#endif // RECOVERY_INSTALL_H_
diff --git a/install/include/install/package.h b/install/include/install/package.h
new file mode 100644
index 0000000..cd44d10
--- /dev/null
+++ b/install/include/install/package.h
@@ -0,0 +1,53 @@
+/*
+ * 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 <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <ziparchive/zip_archive.h>
+
+#include "verifier.h"
+
+// 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 {
+ public:
+ static std::unique_ptr<Package> CreateMemoryPackage(
+ const std::string& path, const std::function<void(float)>& set_progress);
+ static std::unique_ptr<Package> CreateMemoryPackage(
+ std::vector<uint8_t> content, const std::function<void(float)>& set_progress);
+ static std::unique_ptr<Package> CreateFilePackage(const std::string& path,
+ const std::function<void(float)>& set_progress);
+
+ virtual ~Package() = default;
+
+ // Opens the package as a zip file and returns the ZipArchiveHandle.
+ virtual ZipArchiveHandle GetZipArchiveHandle() = 0;
+
+ // Updates the progress in fraction during package verification.
+ void SetProgress(float progress) override;
+
+ protected:
+ // An optional function to update the progress.
+ std::function<void(float)> set_progress_;
+};
diff --git a/install/include/install/verifier.h b/install/include/install/verifier.h
new file mode 100644
index 0000000..f9e9475
--- /dev/null
+++ b/install/include/install/verifier.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2008 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 <functional>
+#include <memory>
+#include <vector>
+
+#include <openssl/ec_key.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+constexpr size_t MiB = 1024 * 1024;
+
+using HasherUpdateCallback = std::function<void(const uint8_t* addr, uint64_t size)>;
+
+struct RSADeleter {
+ void operator()(RSA* rsa) const {
+ RSA_free(rsa);
+ }
+};
+
+struct ECKEYDeleter {
+ void operator()(EC_KEY* ec_key) const {
+ EC_KEY_free(ec_key);
+ }
+};
+
+struct Certificate {
+ typedef enum {
+ KEY_TYPE_RSA,
+ KEY_TYPE_EC,
+ } KeyType;
+
+ Certificate(int hash_len_, KeyType key_type_, std::unique_ptr<RSA, RSADeleter>&& rsa_,
+ std::unique_ptr<EC_KEY, ECKEYDeleter>&& ec_)
+ : hash_len(hash_len_), key_type(key_type_), rsa(std::move(rsa_)), ec(std::move(ec_)) {}
+
+ // SHA_DIGEST_LENGTH (SHA-1) or SHA256_DIGEST_LENGTH (SHA-256)
+ int hash_len;
+ KeyType key_type;
+ std::unique_ptr<RSA, RSADeleter> rsa;
+ std::unique_ptr<EC_KEY, ECKEYDeleter> ec;
+};
+
+class VerifierInterface {
+ public:
+ virtual ~VerifierInterface() = default;
+
+ // Returns the package size in bytes.
+ virtual uint64_t GetPackageSize() const = 0;
+
+ // Reads |byte_count| data starting from |offset|, and puts the result in |buffer|.
+ virtual bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) = 0;
+
+ // Updates the hash contexts for |length| bytes data starting from |start|.
+ virtual bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
+ uint64_t length) = 0;
+
+ // Updates the progress in fraction during package verification.
+ virtual void SetProgress(float progress) = 0;
+};
+
+// Looks for an RSA signature embedded in the .ZIP file comment given the path to the zip.
+// Verifies that it matches one of the given public keys. Returns VERIFY_SUCCESS or
+// VERIFY_FAILURE (if any error is encountered or no key matches the signature).
+int verify_file(VerifierInterface* package, const std::vector<Certificate>& keys);
+
+// Checks that the RSA key has a modulus of 2048 or 4096 bits long, and public exponent is 3 or
+// 65537.
+bool CheckRSAKey(const std::unique_ptr<RSA, RSADeleter>& rsa);
+
+// Checks that the field size of the curve for the EC key is 256 bits.
+bool CheckECKey(const std::unique_ptr<EC_KEY, ECKEYDeleter>& ec_key);
+
+// Parses a PEM-encoded x509 certificate from the given buffer and saves it into |cert|. Returns
+// false if there is a parsing failure or the signature's encryption algorithm is not supported.
+bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert);
+
+// Iterates over the zip entries with the suffix "x509.pem" and returns a list of recognized
+// certificates. Returns an empty list if we fail to parse any of the entries.
+std::vector<Certificate> LoadKeysFromZipfile(const std::string& zip_name);
+
+#define VERIFY_SUCCESS 0
+#define VERIFY_FAILURE 1
diff --git a/default_device.cpp b/install/include/install/wipe_data.h
similarity index 61%
copy from default_device.cpp
copy to install/include/install/wipe_data.h
index a971866..b34891f 100644
--- a/default_device.cpp
+++ b/install/include/install/wipe_data.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 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,17 @@
* limitations under the License.
*/
-#include "device.h"
-#include "screen_ui.h"
+#pragma once
-Device* make_device() {
- return new Device(new ScreenRecoveryUI);
-}
+#include <functional>
+
+#include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
+
+struct selabel_handle;
+
+// Returns true on success.
+bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm);
+
+// Returns true on success.
+bool WipeData(Device* device, bool convert_fbe);
diff --git a/asn1_decoder.h b/install/include/private/asn1_decoder.h
similarity index 98%
rename from asn1_decoder.h
rename to install/include/private/asn1_decoder.h
index 3e99211..e5337d9 100644
--- a/asn1_decoder.h
+++ b/install/include/private/asn1_decoder.h
@@ -17,6 +17,7 @@
#ifndef ASN1_DECODER_H_
#define ASN1_DECODER_H_
+#include <stddef.h>
#include <stdint.h>
class asn1_context {
diff --git a/private/install.h b/install/include/private/setup_commands.h
similarity index 100%
rename from private/install.h
rename to install/include/private/setup_commands.h
diff --git a/install.cpp b/install/install.cpp
similarity index 85%
rename from install.cpp
rename to install/install.cpp
index 9d8943f..e2d4700 100644
--- a/install.cpp
+++ b/install/install.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "install.h"
+#include "install/install.h"
#include <ctype.h>
#include <errno.h>
@@ -46,18 +46,23 @@
#include <android-base/unique_fd.h>
#include <vintf/VintfObjectRecovery.h>
-#include "common.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/install.h"
-#include "roots.h"
-#include "ui.h"
-#include "verifier.h"
+#include "private/setup_commands.h"
+#include "recovery_ui/ui.h"
using namespace std::chrono_literals;
+static constexpr int kRecoveryApiVersion = 3;
+// 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
static constexpr int VERIFICATION_PROGRESS_TIME = 60;
static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
@@ -292,6 +297,11 @@
return INSTALL_ERROR;
}
+ // When executing the update binary contained in the package, the arguments passed are:
+ // - the version number for this interface
+ // - an FD to which the program can write in order to update the progress bar.
+ // - the name of the package zip file.
+ // - an optional argument "retry" if this update is a retry of a failed update attempt.
*cmd = {
binary_path,
std::to_string(kRecoveryApiVersion),
@@ -317,7 +327,7 @@
// 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) {
+ int* max_temperature, RecoveryUI* ui) {
std::map<std::string, std::string> metadata;
if (!ReadMetadataFromPackage(zip, &metadata)) {
LOG(ERROR) << "Failed to parse metadata in the zip file";
@@ -334,75 +344,58 @@
ReadSourceTargetBuild(metadata, log_buffer);
- int pipefd[2];
- pipe(pipefd);
+ // The updater in child process writes to the pipe to communicate with recovery.
+ android::base::unique_fd pipe_read, pipe_write;
+ // Explicitly disable O_CLOEXEC using 0 as the flags (last) parameter to Pipe
+ // so that the child updater process will recieve a non-closed fd.
+ if (!android::base::Pipe(&pipe_read, &pipe_write, 0)) {
+ PLOG(ERROR) << "Failed to create pipe for updater-recovery communication";
+ return INSTALL_CORRUPT;
+ }
+
+ // The updater-recovery communication protocol.
+ //
+ // progress <frac> <secs>
+ // fill up the next <frac> part of of the progress bar over <secs> seconds. If <secs> is
+ // zero, use `set_progress` commands to manually control the progress of this segment of the
+ // bar.
+ //
+ // set_progress <frac>
+ // <frac> should be between 0.0 and 1.0; sets the progress bar within the segment defined by
+ // the most recent progress command.
+ //
+ // ui_print <string>
+ // display <string> on the screen.
+ //
+ // wipe_cache
+ // a wipe of cache will be performed following a successful installation.
+ //
+ // clear_display
+ // turn off the text display.
+ //
+ // enable_reboot
+ // packages can explicitly request that they want the user to be able to reboot during
+ // installation (useful for debugging packages that don't exit).
+ //
+ // retry_update
+ // updater encounters some issue during the update. It requests a reboot to retry the same
+ // package automatically.
+ //
+ // log <string>
+ // updater requests logging the string (e.g. cause of the failure).
+ //
+
std::vector<std::string> args;
if (int update_status =
- is_ab ? SetUpAbUpdateCommands(package, zip, pipefd[1], &args)
- : SetUpNonAbUpdateCommands(package, zip, retry_count, pipefd[1], &args);
+ is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args)
+ : SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args);
update_status != 0) {
- close(pipefd[0]);
- close(pipefd[1]);
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
return update_status;
}
- // When executing the update binary contained in the package, the
- // arguments passed are:
- //
- // - the version number for this interface
- //
- // - an FD to which the program can write in order to update the
- // progress bar. The program can write single-line commands:
- //
- // progress <frac> <secs>
- // fill up the next <frac> part of of the progress bar
- // over <secs> seconds. If <secs> is zero, use
- // set_progress commands to manually control the
- // progress of this segment of the bar.
- //
- // set_progress <frac>
- // <frac> should be between 0.0 and 1.0; sets the
- // progress bar within the segment defined by the most
- // recent progress command.
- //
- // ui_print <string>
- // display <string> on the screen.
- //
- // wipe_cache
- // a wipe of cache will be performed following a successful
- // installation.
- //
- // clear_display
- // turn off the text display.
- //
- // enable_reboot
- // packages can explicitly request that they want the user
- // to be able to reboot during installation (useful for
- // debugging packages that don't exit).
- //
- // retry_update
- // updater encounters some issue during the update. It requests
- // a reboot to retry the same package automatically.
- //
- // log <string>
- // updater requests logging the string (e.g. cause of the
- // failure).
- //
- // - the name of the package zip file.
- //
- // - an optional argument "retry" if this update is a retry of a failed
- // update attempt.
- //
-
- // Convert the std::string vector to a NULL-terminated char* vector suitable for execv.
- auto chr_args = StringVectorToNullTerminatedArray(args);
-
pid_t pid = fork();
-
if (pid == -1) {
- close(pipefd[0]);
- close(pipefd[1]);
PLOG(ERROR) << "Failed to fork update binary";
log_buffer->push_back(android::base::StringPrintf("error: %d", kForkUpdateBinaryFailure));
return INSTALL_ERROR;
@@ -410,16 +403,18 @@
if (pid == 0) {
umask(022);
- close(pipefd[0]);
+ pipe_read.reset();
+
+ // Convert the std::string vector to a NULL-terminated char* vector suitable for execv.
+ auto chr_args = StringVectorToNullTerminatedArray(args);
execv(chr_args[0], chr_args.data());
- // Bug: 34769056
- // We shouldn't use LOG/PLOG in the forked process, since they may cause
- // the child process to hang. This deadlock results from an improperly
- // copied mutex in the ui functions.
+ // We shouldn't use LOG/PLOG in the forked process, since they may cause the child process to
+ // hang. This deadlock results from an improperly copied mutex in the ui functions.
+ // (Bug: 34769056)
fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
_exit(EXIT_FAILURE);
}
- close(pipefd[1]);
+ pipe_write.reset();
std::atomic<bool> logger_finished(false);
std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));
@@ -428,7 +423,7 @@
bool retry_update = false;
char buffer[1024];
- FILE* from_child = fdopen(pipefd[0], "r");
+ FILE* from_child = android::base::Fdopen(std::move(pipe_read), "r");
while (fgets(buffer, sizeof(buffer), from_child) != nullptr) {
std::string line(buffer);
size_t space = line.find_first_of(" \n");
@@ -578,7 +573,7 @@
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) {
+ int* max_temperature, RecoveryUI* ui) {
ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
ui->Print("Finding update package...\n");
// Give verification half the progress bar...
@@ -597,34 +592,29 @@
}
}
- MemMapping map;
- if (!map.MapFile(path)) {
- LOG(ERROR) << "failed to map file";
+ 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(map.addr, map.length)) {
+ if (!verify_package(package.get(), ui)) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
return INSTALL_CORRUPT;
}
// Try to open the package.
- ZipArchiveHandle zip;
- int err = OpenArchiveFromMemory(map.addr, map.length, path.c_str(), &zip);
- if (err != 0) {
- LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err);
+ ZipArchiveHandle zip = package->GetZipArchiveHandle();
+ if (!zip) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
-
- CloseArchive(zip);
return INSTALL_CORRUPT;
}
- // Additionally verify the compatibility of the package.
- if (!verify_package_compatibility(zip)) {
+ // 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));
- CloseArchive(zip);
return INSTALL_CORRUPT;
}
@@ -634,19 +624,18 @@
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);
+ int result =
+ try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui);
ui->SetEnableReboot(true);
ui->Print("\n");
- CloseArchive(zip);
return result;
}
-int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, int retry_count) {
+int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount,
+ int retry_count, RecoveryUI* ui) {
CHECK(!path.empty());
- CHECK(wipe_cache != nullptr);
- modified_flash = true;
auto start = std::chrono::system_clock::now();
int start_temperature = GetMaxValueFromThermalZone();
@@ -658,8 +647,10 @@
LOG(ERROR) << "failed to set up expected mounts for install; aborting";
result = INSTALL_ERROR;
} else {
- result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count,
- &max_temperature);
+ bool updater_wipe_cache = false;
+ result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer,
+ retry_count, &max_temperature, ui);
+ should_wipe_cache = should_wipe_cache || updater_wipe_cache;
}
// Measure the time spent to apply OTA update in seconds.
@@ -714,10 +705,16 @@
// Write a copy into last_log.
LOG(INFO) << log_content;
+ if (result == INSTALL_SUCCESS && should_wipe_cache) {
+ if (!WipeCache(ui, nullptr)) {
+ result = INSTALL_ERROR;
+ }
+ }
+
return result;
}
-bool verify_package(const unsigned char* package_data, size_t package_size) {
+bool verify_package(Package* package, RecoveryUI* ui) {
static constexpr const char* CERTIFICATE_ZIP_FILE = "/system/etc/security/otacerts.zip";
std::vector<Certificate> loaded_keys = LoadKeysFromZipfile(CERTIFICATE_ZIP_FILE);
if (loaded_keys.empty()) {
@@ -729,8 +726,7 @@
// Verify package.
ui->Print("Verifying update package...\n");
auto t0 = std::chrono::system_clock::now();
- int err = verify_file(package_data, package_size, loaded_keys,
- std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
+ int err = verify_file(package, loaded_keys);
std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0;
ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err);
if (err != VERIFY_SUCCESS) {
diff --git a/install/package.cpp b/install/package.cpp
new file mode 100644
index 0000000..4402f48
--- /dev/null
+++ b/install/package.cpp
@@ -0,0 +1,262 @@
+/*
+ * 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/package.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+#include "otautil/error_code.h"
+#include "otautil/sysutil.h"
+
+// This class wraps the package in memory, i.e. a memory mapped package, or a package loaded
+// to a string/vector.
+class MemoryPackage : public Package {
+ public:
+ // Constructs the class from a file. We will memory maps the file later.
+ MemoryPackage(const std::string& path, std::unique_ptr<MemMapping> map,
+ const std::function<void(float)>& set_progress);
+
+ // Constructs the class from the package bytes in |content|.
+ MemoryPackage(std::vector<uint8_t> content, const std::function<void(float)>& set_progress);
+
+ ~MemoryPackage() override;
+
+ // Memory maps the package file if necessary. Initializes the start address and size of the
+ // package.
+ uint64_t GetPackageSize() const override {
+ return package_size_;
+ }
+
+ bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
+
+ ZipArchiveHandle GetZipArchiveHandle() override;
+
+ bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
+ uint64_t length) override;
+
+ private:
+ const uint8_t* addr_; // Start address of the package in memory.
+ uint64_t package_size_; // Package size in bytes.
+
+ // The memory mapped package.
+ std::unique_ptr<MemMapping> map_;
+ // A copy of the package content, valid only if we create the class with the exact bytes of
+ // the package.
+ std::vector<uint8_t> package_content_;
+ // The physical path to the package, empty if we create the class with the package content.
+ std::string path_;
+
+ // The ZipArchiveHandle of the package.
+ ZipArchiveHandle zip_handle_;
+};
+
+void Package::SetProgress(float progress) {
+ if (set_progress_) {
+ set_progress_(progress);
+ }
+}
+
+class FilePackage : public Package {
+ public:
+ FilePackage(android::base::unique_fd&& fd, uint64_t file_size, const std::string& path,
+ const std::function<void(float)>& set_progress);
+
+ ~FilePackage() override;
+
+ uint64_t GetPackageSize() const override {
+ return package_size_;
+ }
+
+ bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
+
+ ZipArchiveHandle GetZipArchiveHandle() override;
+
+ bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
+ uint64_t length) override;
+
+ private:
+ android::base::unique_fd fd_; // The underlying fd to the open package.
+ uint64_t package_size_;
+ std::string path_; // The physical path to the package.
+
+ ZipArchiveHandle zip_handle_;
+};
+
+std::unique_ptr<Package> Package::CreateMemoryPackage(
+ const std::string& path, const std::function<void(float)>& set_progress) {
+ std::unique_ptr<MemMapping> mmap = std::make_unique<MemMapping>();
+ if (!mmap->MapFile(path)) {
+ LOG(ERROR) << "failed to map file";
+ return nullptr;
+ }
+
+ return std::make_unique<MemoryPackage>(path, std::move(mmap), set_progress);
+}
+
+std::unique_ptr<Package> Package::CreateFilePackage(
+ const std::string& path, const std::function<void(float)>& set_progress) {
+ android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << path;
+ return nullptr;
+ }
+
+ off64_t file_size = lseek64(fd.get(), 0, SEEK_END);
+ if (file_size == -1) {
+ PLOG(ERROR) << "Failed to get the package size";
+ return nullptr;
+ }
+
+ return std::make_unique<FilePackage>(std::move(fd), file_size, path, set_progress);
+}
+
+std::unique_ptr<Package> Package::CreateMemoryPackage(
+ std::vector<uint8_t> content, const std::function<void(float)>& set_progress) {
+ return std::make_unique<MemoryPackage>(std::move(content), set_progress);
+}
+
+MemoryPackage::MemoryPackage(const std::string& path, std::unique_ptr<MemMapping> map,
+ const std::function<void(float)>& set_progress)
+ : map_(std::move(map)), path_(path), zip_handle_(nullptr) {
+ addr_ = map_->addr;
+ package_size_ = map_->length;
+ set_progress_ = set_progress;
+}
+
+MemoryPackage::MemoryPackage(std::vector<uint8_t> content,
+ const std::function<void(float)>& set_progress)
+ : package_content_(std::move(content)), zip_handle_(nullptr) {
+ CHECK(!package_content_.empty());
+ addr_ = package_content_.data();
+ package_size_ = package_content_.size();
+ set_progress_ = set_progress;
+}
+
+MemoryPackage::~MemoryPackage() {
+ if (zip_handle_) {
+ CloseArchive(zip_handle_);
+ }
+}
+
+bool MemoryPackage::ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) {
+ if (byte_count > package_size_ || offset > package_size_ - byte_count) {
+ LOG(ERROR) << "Out of bound read, offset: " << offset << ", size: " << byte_count
+ << ", total package_size: " << package_size_;
+ return false;
+ }
+ memcpy(buffer, addr_ + offset, byte_count);
+ return true;
+}
+
+bool MemoryPackage::UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers,
+ uint64_t start, uint64_t length) {
+ if (length > package_size_ || start > package_size_ - length) {
+ LOG(ERROR) << "Out of bound read, offset: " << start << ", size: " << length
+ << ", total package_size: " << package_size_;
+ return false;
+ }
+
+ for (const auto& hasher : hashers) {
+ hasher(addr_ + start, length);
+ }
+ return true;
+}
+
+ZipArchiveHandle MemoryPackage::GetZipArchiveHandle() {
+ if (zip_handle_) {
+ return zip_handle_;
+ }
+
+ if (auto err = OpenArchiveFromMemory(const_cast<uint8_t*>(addr_), package_size_, path_.c_str(),
+ &zip_handle_);
+ err != 0) {
+ LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err);
+ return nullptr;
+ }
+
+ return zip_handle_;
+}
+
+FilePackage::FilePackage(android::base::unique_fd&& fd, uint64_t file_size, const std::string& path,
+ const std::function<void(float)>& set_progress)
+ : fd_(std::move(fd)), package_size_(file_size), path_(path), zip_handle_(nullptr) {
+ set_progress_ = set_progress;
+}
+
+FilePackage::~FilePackage() {
+ if (zip_handle_) {
+ CloseArchive(zip_handle_);
+ }
+}
+
+bool FilePackage::ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) {
+ if (byte_count > package_size_ || offset > package_size_ - byte_count) {
+ LOG(ERROR) << "Out of bound read, offset: " << offset << ", size: " << byte_count
+ << ", total package_size: " << package_size_;
+ return false;
+ }
+
+ if (!android::base::ReadFullyAtOffset(fd_.get(), buffer, byte_count, offset)) {
+ PLOG(ERROR) << "Failed to read " << byte_count << " bytes data at offset " << offset;
+ return false;
+ }
+
+ return true;
+}
+
+bool FilePackage::UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers,
+ uint64_t start, uint64_t length) {
+ if (length > package_size_ || start > package_size_ - length) {
+ LOG(ERROR) << "Out of bound read, offset: " << start << ", size: " << length
+ << ", total package_size: " << package_size_;
+ return false;
+ }
+
+ uint64_t so_far = 0;
+ while (so_far < length) {
+ uint64_t read_size = std::min<uint64_t>(length - so_far, 16 * MiB);
+ std::vector<uint8_t> buffer(read_size);
+ if (!ReadFullyAtOffset(buffer.data(), read_size, start + so_far)) {
+ return false;
+ }
+
+ for (const auto& hasher : hashers) {
+ hasher(buffer.data(), read_size);
+ }
+ so_far += read_size;
+ }
+
+ return true;
+}
+
+ZipArchiveHandle FilePackage::GetZipArchiveHandle() {
+ if (zip_handle_) {
+ return zip_handle_;
+ }
+
+ if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_); err != 0) {
+ LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err);
+ return nullptr;
+ }
+
+ return zip_handle_;
+}
diff --git a/verifier.cpp b/install/verifier.cpp
similarity index 83%
rename from verifier.cpp
rename to install/verifier.cpp
index 44bd4e1..6ba1d77 100644
--- a/verifier.cpp
+++ b/install/verifier.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "verifier.h"
+#include "install/verifier.h"
#include <errno.h>
#include <stdio.h>
@@ -36,10 +36,8 @@
#include <openssl/rsa.h>
#include <ziparchive/zip_archive.h>
-#include "asn1_decoder.h"
#include "otautil/print_sha1.h"
-
-static constexpr size_t MiB = 1024 * 1024;
+#include "private/asn1_decoder.h"
/*
* Simple version of PKCS#7 SignedData extraction. This extracts the
@@ -84,10 +82,8 @@
}
std::unique_ptr<asn1_context> signed_data_seq(signed_data_app->asn1_sequence_get());
- if (signed_data_seq == nullptr ||
- !signed_data_seq->asn1_sequence_next() ||
- !signed_data_seq->asn1_sequence_next() ||
- !signed_data_seq->asn1_sequence_next() ||
+ if (signed_data_seq == nullptr || !signed_data_seq->asn1_sequence_next() ||
+ !signed_data_seq->asn1_sequence_next() || !signed_data_seq->asn1_sequence_next() ||
!signed_data_seq->asn1_constructed_skip_all()) {
return false;
}
@@ -98,11 +94,8 @@
}
std::unique_ptr<asn1_context> sig_seq(sig_set->asn1_sequence_get());
- if (sig_seq == nullptr ||
- !sig_seq->asn1_sequence_next() ||
- !sig_seq->asn1_sequence_next() ||
- !sig_seq->asn1_sequence_next() ||
- !sig_seq->asn1_sequence_next()) {
+ if (sig_seq == nullptr || !sig_seq->asn1_sequence_next() || !sig_seq->asn1_sequence_next() ||
+ !sig_seq->asn1_sequence_next() || !sig_seq->asn1_sequence_next()) {
return false;
}
@@ -117,19 +110,9 @@
return true;
}
-/*
- * Looks for an RSA signature embedded in the .ZIP file comment given the path to the zip. Verifies
- * that it matches one of the given public keys. A callback function can be optionally provided for
- * posting the progress.
- *
- * Returns VERIFY_SUCCESS or VERIFY_FAILURE (if any error is encountered or no key matches the
- * signature).
- */
-int verify_file(const unsigned char* addr, size_t length, const std::vector<Certificate>& keys,
- const std::function<void(float)>& set_progress) {
- if (set_progress) {
- set_progress(0.0);
- }
+int verify_file(VerifierInterface* package, const std::vector<Certificate>& keys) {
+ CHECK(package);
+ package->SetProgress(0.0);
// An archive with a whole-file signature will end in six bytes:
//
@@ -140,13 +123,18 @@
// the whole comment.
#define FOOTER_SIZE 6
+ uint64_t length = package->GetPackageSize();
if (length < FOOTER_SIZE) {
LOG(ERROR) << "not big enough to contain footer";
return VERIFY_FAILURE;
}
- const unsigned char* footer = addr + length - FOOTER_SIZE;
+ uint8_t footer[FOOTER_SIZE];
+ if (!package->ReadFullyAtOffset(footer, FOOTER_SIZE, length - FOOTER_SIZE)) {
+ LOG(ERROR) << "Failed to read footer";
+ return VERIFY_FAILURE;
+ }
if (footer[2] != 0xff || footer[3] != 0xff) {
LOG(ERROR) << "footer is wrong";
@@ -159,8 +147,8 @@
<< " bytes from end";
if (signature_start > comment_size) {
- LOG(ERROR) << "signature start: " << signature_start << " is larger than comment size: "
- << comment_size;
+ LOG(ERROR) << "signature start: " << signature_start
+ << " is larger than comment size: " << comment_size;
return VERIFY_FAILURE;
}
@@ -182,9 +170,13 @@
// Determine how much of the file is covered by the signature. This is everything except the
// signature data and length, which includes all of the EOCD except for the comment length field
// (2 bytes) and the comment data.
- size_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
+ uint64_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
- const unsigned char* eocd = addr + length - eocd_size;
+ uint8_t eocd[eocd_size];
+ if (!package->ReadFullyAtOffset(eocd, eocd_size, length - eocd_size)) {
+ LOG(ERROR) << "Failed to read EOCD of " << eocd_size << " bytes";
+ return VERIFY_FAILURE;
+ }
// If this is really is the EOCD record, it will begin with the magic number $50 $4b $05 $06.
if (eocd[0] != 0x50 || eocd[1] != 0x4b || eocd[2] != 0x05 || eocd[3] != 0x06) {
@@ -192,8 +184,8 @@
return VERIFY_FAILURE;
}
- for (size_t i = 4; i < eocd_size-3; ++i) {
- if (eocd[i] == 0x50 && eocd[i+1] == 0x4b && eocd[i+2] == 0x05 && eocd[i+3] == 0x06) {
+ for (size_t i = 4; i < eocd_size - 3; ++i) {
+ if (eocd[i] == 0x50 && eocd[i + 1] == 0x4b && eocd[i + 2] == 0x05 && eocd[i + 3] == 0x06) {
// If the sequence $50 $4b $05 $06 appears anywhere after the real one, libziparchive will
// find the later (wrong) one, which could be exploitable. Fail the verification if this
// sequence occurs anywhere after the real one.
@@ -206,8 +198,12 @@
bool need_sha256 = false;
for (const auto& key : keys) {
switch (key.hash_len) {
- case SHA_DIGEST_LENGTH: need_sha1 = true; break;
- case SHA256_DIGEST_LENGTH: need_sha256 = true; break;
+ case SHA_DIGEST_LENGTH:
+ need_sha1 = true;
+ break;
+ case SHA256_DIGEST_LENGTH:
+ need_sha256 = true;
+ break;
}
}
@@ -216,24 +212,29 @@
SHA1_Init(&sha1_ctx);
SHA256_Init(&sha256_ctx);
+ std::vector<HasherUpdateCallback> hashers;
+ if (need_sha1) {
+ hashers.emplace_back(
+ std::bind(&SHA1_Update, &sha1_ctx, std::placeholders::_1, std::placeholders::_2));
+ }
+ if (need_sha256) {
+ hashers.emplace_back(
+ std::bind(&SHA256_Update, &sha256_ctx, std::placeholders::_1, std::placeholders::_2));
+ }
+
double frac = -1.0;
- size_t so_far = 0;
+ uint64_t so_far = 0;
while (so_far < signed_len) {
- // On a Nexus 5X, experiment showed 16MiB beat 1MiB by 6% faster for a
- // 1196MiB full OTA and 60% for an 89MiB incremental OTA.
- // http://b/28135231.
- size_t size = std::min(signed_len - so_far, 16 * MiB);
+ // On a Nexus 5X, experiment showed 16MiB beat 1MiB by 6% faster for a 1196MiB full OTA and
+ // 60% for an 89MiB incremental OTA. http://b/28135231.
+ uint64_t read_size = std::min<uint64_t>(signed_len - so_far, 16 * MiB);
+ package->UpdateHashAtOffset(hashers, so_far, read_size);
+ so_far += read_size;
- if (need_sha1) SHA1_Update(&sha1_ctx, addr + so_far, size);
- if (need_sha256) SHA256_Update(&sha256_ctx, addr + so_far, size);
- so_far += size;
-
- if (set_progress) {
- double f = so_far / (double)signed_len;
- if (f > frac + 0.02 || size == so_far) {
- set_progress(f);
- frac = f;
- }
+ double f = so_far / static_cast<double>(signed_len);
+ if (f > frac + 0.02 || read_size == so_far) {
+ package->SetProgress(f);
+ frac = f;
}
}
@@ -245,8 +246,8 @@
const uint8_t* signature = eocd + eocd_size - signature_start;
size_t signature_size = signature_start - FOOTER_SIZE;
- LOG(INFO) << "signature (offset: " << std::hex << (length - signature_start) << ", length: "
- << signature_size << "): " << print_hex(signature, signature_size);
+ LOG(INFO) << "signature (offset: " << std::hex << (length - signature_start)
+ << ", length: " << signature_size << "): " << print_hex(signature, signature_size);
std::vector<uint8_t> sig_der;
if (!read_pkcs7(signature, signature_size, &sig_der)) {
@@ -371,8 +372,8 @@
const BIGNUM* out_e;
RSA_get0_key(rsa.get(), &out_n, &out_e, nullptr /* private exponent */);
auto modulus_bits = BN_num_bits(out_n);
- if (modulus_bits != 2048) {
- LOG(ERROR) << "Modulus should be 2048 bits long, actual: " << modulus_bits;
+ if (modulus_bits != 2048 && modulus_bits != 4096) {
+ LOG(ERROR) << "Modulus should be 2048 or 4096 bits long, actual: " << modulus_bits;
return false;
}
diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp
new file mode 100644
index 0000000..765a815
--- /dev/null
+++ b/install/wipe_data.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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_data.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <functional>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include "otautil/dirutil.h"
+#include "otautil/logging.h"
+#include "otautil/roots.h"
+#include "recovery_ui/ui.h"
+
+constexpr const char* CACHE_ROOT = "/cache";
+constexpr const char* DATA_ROOT = "/data";
+constexpr const char* METADATA_ROOT = "/metadata";
+
+static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) {
+ bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
+ bool is_data = (strcmp(volume, DATA_ROOT) == 0);
+
+ ui->SetBackground(RecoveryUI::ERASING);
+ ui->SetProgressType(RecoveryUI::INDETERMINATE);
+
+ std::vector<saved_log_file> log_files;
+ if (is_cache) {
+ // If we're reformatting /cache, we load any past logs (i.e. "/cache/recovery/last_*") and the
+ // current log ("/cache/recovery/log") into memory, so we can restore them after the reformat.
+ log_files = ReadLogFilesToMemory();
+ }
+
+ ui->Print("Formatting %s...\n", volume);
+
+ ensure_path_unmounted(volume);
+
+ int result;
+ if (is_data && convert_fbe) {
+ constexpr const char* CONVERT_FBE_DIR = "/tmp/convert_fbe";
+ constexpr const char* CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe";
+ // Create convert_fbe breadcrumb file to signal init to convert to file based encryption, not
+ // full disk encryption.
+ if (mkdir(CONVERT_FBE_DIR, 0700) != 0) {
+ PLOG(ERROR) << "Failed to mkdir " << CONVERT_FBE_DIR;
+ return false;
+ }
+ FILE* f = fopen(CONVERT_FBE_FILE, "wbe");
+ if (!f) {
+ PLOG(ERROR) << "Failed to convert to file encryption";
+ return false;
+ }
+ fclose(f);
+ result = format_volume(volume, CONVERT_FBE_DIR);
+ remove(CONVERT_FBE_FILE);
+ rmdir(CONVERT_FBE_DIR);
+ } else {
+ result = format_volume(volume);
+ }
+
+ if (is_cache) {
+ RestoreLogFilesAfterFormat(log_files);
+ }
+
+ return (result == 0);
+}
+
+bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm_func) {
+ bool has_cache = volume_for_mount_point("/cache") != nullptr;
+ if (!has_cache) {
+ ui->Print("No /cache partition found.\n");
+ return false;
+ }
+
+ if (confirm_func && !confirm_func()) {
+ return false;
+ }
+
+ ui->Print("\n-- Wiping cache...\n");
+ bool success = EraseVolume("/cache", ui, false);
+ ui->Print("Cache wipe %s.\n", success ? "complete" : "failed");
+ return success;
+}
+
+bool WipeData(Device* device, bool convert_fbe) {
+ RecoveryUI* ui = device->GetUI();
+ ui->Print("\n-- Wiping data...\n");
+ bool success = device->PreWipeData();
+ if (success) {
+ success &= EraseVolume(DATA_ROOT, ui, convert_fbe);
+ bool has_cache = volume_for_mount_point("/cache") != nullptr;
+ if (has_cache) {
+ success &= EraseVolume(CACHE_ROOT, ui, false);
+ }
+ if (volume_for_mount_point(METADATA_ROOT) != nullptr) {
+ success &= EraseVolume(METADATA_ROOT, ui, false);
+ }
+ }
+ if (success) {
+ success &= device->PostWipeData();
+ }
+ ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
+ return success;
+}
\ No newline at end of file
diff --git a/minadbd/Android.bp b/minadbd/Android.bp
index a95d979..e4f7712 100644
--- a/minadbd/Android.bp
+++ b/minadbd/Android.bp
@@ -40,7 +40,6 @@
srcs: [
"fuse_adb_provider.cpp",
- "minadbd.cpp",
"minadbd_services.cpp",
],
@@ -52,6 +51,36 @@
],
}
+cc_library_headers {
+ name: "libminadbd_headers",
+ recovery_available: true,
+ // TODO create a include dir
+ export_include_dirs: [
+ ".",
+ ],
+}
+
+cc_binary {
+ name: "minadbd",
+ recovery: true,
+
+ defaults: [
+ "minadbd_defaults",
+ ],
+
+ srcs: [
+ "minadbd.cpp",
+ ],
+
+ shared_libs: [
+ "libadbd",
+ "libbase",
+ "libcrypto",
+ "libfusesideload",
+ "libminadbd_services",
+ ],
+}
+
cc_test {
name: "minadbd_test",
isolated: true,
@@ -66,6 +95,7 @@
static_libs: [
"libminadbd_services",
+ "libfusesideload",
"libadbd",
],
diff --git a/minadbd/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp
index 9bd3f23..9d19a1e 100644
--- a/minadbd/fuse_adb_provider.cpp
+++ b/minadbd/fuse_adb_provider.cpp
@@ -18,39 +18,26 @@
#include <errno.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-#include <functional>
-
#include "adb.h"
#include "adb_io.h"
-#include "fuse_sideload.h"
-int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
- if (!WriteFdFmt(ad.sfd, "%08u", block)) {
+bool FuseAdbDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
+ uint32_t start_block) const {
+ if (!WriteFdFmt(fd_, "%08u", start_block)) {
fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno));
- return -EIO;
+ return false;
}
- if (!ReadFdExactly(ad.sfd, buffer, fetch_size)) {
+ if (!ReadFdExactly(fd_, buffer, fetch_size)) {
fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno));
- return -EIO;
+ return false;
}
- return 0;
+ return true;
}
-int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) {
- adb_data ad;
- ad.sfd = sfd;
- ad.file_size = file_size;
- ad.block_size = block_size;
-
- provider_vtab vtab;
- vtab.read_block = std::bind(read_block_adb, ad, std::placeholders::_1, std::placeholders::_2,
- std::placeholders::_3);
- vtab.close = [&ad]() { WriteFdExactly(ad.sfd, "DONEDONE"); };
-
- return run_fuse_sideload(vtab, file_size, block_size);
+void FuseAdbDataProvider::Close() {
+ WriteFdExactly(fd_, "DONEDONE");
}
diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h
index 36d86d5..24a463d 100644
--- a/minadbd/fuse_adb_provider.h
+++ b/minadbd/fuse_adb_provider.h
@@ -14,19 +14,24 @@
* limitations under the License.
*/
-#ifndef __FUSE_ADB_PROVIDER_H
-#define __FUSE_ADB_PROVIDER_H
+#pragma once
#include <stdint.h>
-struct adb_data {
- int sfd; // file descriptor for the adb channel
+#include "fuse_provider.h"
- uint64_t file_size;
- uint32_t block_size;
+// This class reads data from adb server.
+class FuseAdbDataProvider : public FuseDataProvider {
+ public:
+ FuseAdbDataProvider(int fd, uint64_t file_size, uint32_t block_size)
+ : FuseDataProvider(file_size, block_size), fd_(fd) {}
+
+ bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
+ uint32_t start_block) const override;
+
+ void Close() override;
+
+ private:
+ // The underlying source to read data from (i.e. the one that talks to the host).
+ int fd_;
};
-
-int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size);
-int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size);
-
-#endif
diff --git a/minadbd/fuse_adb_provider_test.cpp b/minadbd/fuse_adb_provider_test.cpp
index 00250e5..0b09712 100644
--- a/minadbd/fuse_adb_provider_test.cpp
+++ b/minadbd/fuse_adb_provider_test.cpp
@@ -21,19 +21,19 @@
#include <string>
+#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include "adb_io.h"
#include "fuse_adb_provider.h"
TEST(fuse_adb_provider, read_block_adb) {
- adb_data data = {};
- int sockets[2];
+ android::base::unique_fd device_socket;
+ android::base::unique_fd host_socket;
- ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
- data.sfd = sockets[0];
+ ASSERT_TRUE(android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &device_socket, &host_socket));
+ FuseAdbDataProvider data(std::move(device_socket), 0, 0);
- int host_socket = sockets[1];
fcntl(host_socket, F_SETFL, O_NONBLOCK);
const char expected_data[] = "foobar";
@@ -46,8 +46,8 @@
uint32_t block = 1234U;
const char expected_block[] = "00001234";
- ASSERT_EQ(0, read_block_adb(data, block, reinterpret_cast<uint8_t*>(block_data),
- sizeof(expected_data) - 1));
+ ASSERT_TRUE(data.ReadBlockAlignedData(reinterpret_cast<uint8_t*>(block_data),
+ sizeof(expected_data) - 1, block));
// Check that read_block_adb requested the right block.
char block_req[sizeof(expected_block)] = {};
@@ -65,26 +65,21 @@
errno = 0;
ASSERT_EQ(-1, read(host_socket, &tmp, 1));
ASSERT_EQ(EWOULDBLOCK, errno);
-
- close(sockets[0]);
- close(sockets[1]);
}
TEST(fuse_adb_provider, read_block_adb_fail_write) {
- adb_data data = {};
- int sockets[2];
+ android::base::unique_fd device_socket;
+ android::base::unique_fd host_socket;
- ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
- data.sfd = sockets[0];
+ ASSERT_TRUE(android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &device_socket, &host_socket));
+ FuseAdbDataProvider data(std::move(device_socket), 0, 0);
- ASSERT_EQ(0, close(sockets[1]));
+ host_socket.reset();
// write(2) raises SIGPIPE since the reading end has been closed. Ignore the signal to avoid
// failing the test.
signal(SIGPIPE, SIG_IGN);
char buf[1];
- ASSERT_EQ(-EIO, read_block_adb(data, 0, reinterpret_cast<uint8_t*>(buf), 1));
-
- close(sockets[0]);
+ ASSERT_FALSE(data.ReadBlockAlignedData(reinterpret_cast<uint8_t*>(buf), 1, 0));
}
diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp
index 349189c..57158ad 100644
--- a/minadbd/minadbd.cpp
+++ b/minadbd/minadbd.cpp
@@ -14,30 +14,54 @@
* limitations under the License.
*/
-#include "minadbd.h"
-
#include <errno.h>
+#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <strings.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include "adb.h"
#include "adb_auth.h"
#include "transport.h"
-int minadbd_main() {
- adb_device_banner = "sideload";
+#include "minadbd_services.h"
+#include "minadbd_types.h"
- signal(SIGPIPE, SIG_IGN);
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::StderrLogger);
+ // TODO(xunchang) implement a command parser
+ if (argc != 3 || strcmp("--socket_fd", argv[1]) != 0) {
+ LOG(ERROR) << "minadbd has invalid arguments, argc: " << argc;
+ exit(kMinadbdArgumentsParsingError);
+ }
- // We can't require authentication for sideloading. http://b/22025550.
- auth_required = false;
+ int socket_fd;
+ if (!android::base::ParseInt(argv[2], &socket_fd)) {
+ LOG(ERROR) << "Failed to parse int in " << argv[2];
+ exit(kMinadbdArgumentsParsingError);
+ }
+ if (fcntl(socket_fd, F_GETFD, 0) == -1) {
+ PLOG(ERROR) << "Failed to get minadbd socket";
+ exit(kMinadbdSocketIOError);
+ }
+ SetMinadbdSocketFd(socket_fd);
- init_transport_registration();
- usb_init();
+ adb_device_banner = "sideload";
- VLOG(ADB) << "Event loop starting";
- fdevent_loop();
+ signal(SIGPIPE, SIG_IGN);
- return 0;
+ // We can't require authentication for sideloading. http://b/22025550.
+ auth_required = false;
+
+ init_transport_registration();
+ usb_init();
+
+ VLOG(ADB) << "Event loop starting";
+ fdevent_loop();
+
+ return 0;
}
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index 9309ed7..f2b65c0 100644
--- a/minadbd/minadbd_services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "minadbd_services.h"
+
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
@@ -22,39 +24,99 @@
#include <unistd.h>
#include <functional>
+#include <memory>
#include <string>
#include <string_view>
#include <thread>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/memory.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
#include "adb.h"
#include "adb_unique_fd.h"
#include "fdevent.h"
#include "fuse_adb_provider.h"
+#include "fuse_sideload.h"
+#include "minadbd_types.h"
#include "services.h"
#include "sysdeps.h"
+static int minadbd_socket = -1;
+void SetMinadbdSocketFd(int socket_fd) {
+ minadbd_socket = socket_fd;
+}
+
+static bool WriteCommandToFd(MinadbdCommands cmd, int fd) {
+ char message[kMinadbdMessageSize];
+ memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix));
+ android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), cmd);
+
+ if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) {
+ PLOG(ERROR) << "Failed to write message " << message;
+ return false;
+ }
+ return true;
+}
+
+// Blocks and reads the command status from |fd|. Returns false if the received message has a
+// format error.
+static bool WaitForCommandStatus(int fd, MinadbdCommandStatus* status) {
+ char buffer[kMinadbdMessageSize];
+ if (!android::base::ReadFully(fd, buffer, kMinadbdMessageSize)) {
+ PLOG(ERROR) << "Failed to response status from socket";
+ exit(kMinadbdSocketIOError);
+ }
+
+ std::string message(buffer, buffer + kMinadbdMessageSize);
+ if (!android::base::StartsWith(message, kMinadbdStatusPrefix)) {
+ LOG(ERROR) << "Failed to parse status in " << message;
+ return false;
+ }
+
+ *status = android::base::get_unaligned<MinadbdCommandStatus>(
+ message.substr(strlen(kMinadbdStatusPrefix)).c_str());
+ return true;
+}
+
static void sideload_host_service(unique_fd sfd, const std::string& args) {
int64_t file_size;
int block_size;
if ((sscanf(args.c_str(), "%" SCNd64 ":%d", &file_size, &block_size) != 2) || file_size <= 0 ||
block_size <= 0) {
- printf("bad sideload-host arguments: %s\n", args.c_str());
- exit(1);
+ LOG(ERROR) << "bad sideload-host arguments: " << args;
+ exit(kMinadbdPackageSizeError);
}
- printf("sideload-host file size %" PRId64 " block size %d\n", file_size, block_size);
+ LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size;
- int result = run_adb_fuse(sfd, file_size, block_size);
+ if (!WriteCommandToFd(MinadbdCommands::kInstall, minadbd_socket)) {
+ exit(kMinadbdSocketIOError);
+ }
- printf("sideload_host finished\n");
- exit(result == 0 ? 0 : 1);
+ auto adb_data_reader = std::make_unique<FuseAdbDataProvider>(sfd, file_size, block_size);
+ if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) {
+ LOG(ERROR) << "Failed to start fuse";
+ exit(kMinadbdFuseStartError);
+ }
+
+ MinadbdCommandStatus status;
+ if (!WaitForCommandStatus(minadbd_socket, &status)) {
+ exit(kMinadbdMessageFormatError);
+ }
+ LOG(INFO) << "Got command status: " << static_cast<unsigned int>(status);
+
+ LOG(INFO) << "sideload_host finished";
+ exit(kMinadbdSuccess);
}
unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) {
if (name.starts_with("sideload:")) {
// This exit status causes recovery to print a special error message saying to use a newer adb
// (that supports sideload-host).
- exit(3);
+ exit(kMinadbdAdbVersionError);
} else if (name.starts_with("sideload-host:")) {
std::string arg(name.substr(strlen("sideload-host:")));
return create_service_thread("sideload-host",
diff --git a/minadbd/minadbd.h b/minadbd/minadbd_services.h
similarity index 89%
rename from minadbd/minadbd.h
rename to minadbd/minadbd_services.h
index 3570a5d..6835bd7 100644
--- a/minadbd/minadbd.h
+++ b/minadbd/minadbd_services.h
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#ifndef MINADBD_H__
-#define MINADBD_H__
+#pragma once
-int minadbd_main();
-
-#endif
+void SetMinadbdSocketFd(int socket_fd);
diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h
new file mode 100644
index 0000000..7bd6909
--- /dev/null
+++ b/minadbd/minadbd_types.h
@@ -0,0 +1,53 @@
+/*
+ * 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>
+
+// The message between recovery and minadbd is 8 bytes in size unless the length is explicitly
+// specified. Both the command and status has the format |prefix(4 bytes) + encoded enum(4 bytes)|.
+constexpr size_t kMinadbdMessageSize = 8;
+constexpr char const kMinadbdCommandPrefix[] = "COMD";
+constexpr char const kMinadbdStatusPrefix[] = "STAT";
+
+enum MinadbdErrorCode : int {
+ kMinadbdSuccess = 0,
+ kMinadbdArgumentsParsingError = 1,
+ kMinadbdSocketIOError = 2,
+ kMinadbdMessageFormatError = 3,
+ kMinadbdAdbVersionError = 4,
+ kMinadbdPackageSizeError = 5,
+ kMinadbdFuseStartError = 6,
+ kMinadbdUnsupportedCommandError = 7,
+ kMinadbdCommandExecutionError = 8,
+ kMinadbdErrorUnknown = 9,
+};
+
+enum class MinadbdCommandStatus : uint32_t {
+ kSuccess = 0,
+ kFailure = 1,
+};
+
+enum class MinadbdCommands : uint32_t {
+ kInstall = 0,
+ kUiPrint = 1,
+ kError = 2,
+};
+
+static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommands));
+static_assert(kMinadbdMessageSize ==
+ sizeof(kMinadbdStatusPrefix) - 1 + sizeof(MinadbdCommandStatus));
diff --git a/minui/events.cpp b/minui/events.cpp
index d94e977..7d0250e 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -23,144 +23,148 @@
#include <string.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
+#include <sys/types.h>
#include <unistd.h>
#include <functional>
+#include <memory>
+
+#include <android-base/unique_fd.h>
#include "minui/minui.h"
-#define MAX_DEVICES 16
-#define MAX_MISC_FDS 16
+constexpr size_t MAX_DEVICES = 16;
+constexpr size_t MAX_MISC_FDS = 16;
-#define BITS_PER_LONG (sizeof(unsigned long) * 8)
-#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
+constexpr size_t BITS_PER_LONG = sizeof(unsigned long) * 8;
+constexpr size_t BITS_TO_LONGS(size_t bits) {
+ return ((bits + BITS_PER_LONG - 1) / BITS_PER_LONG);
+}
-struct fd_info {
- int fd;
+struct FdInfo {
+ android::base::unique_fd fd;
ev_callback cb;
};
-static int g_epoll_fd;
-static epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS];
-static int npolledevents;
+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;
-static fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
+static FdInfo ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
-static unsigned ev_count = 0;
-static unsigned ev_dev_count = 0;
-static unsigned ev_misc_count = 0;
+static size_t g_ev_count = 0;
+static size_t g_ev_dev_count = 0;
+static size_t g_ev_misc_count = 0;
static bool test_bit(size_t bit, unsigned long* array) { // NOLINT
- return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
+ return (array[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
}
int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
- g_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
- if (g_epoll_fd == -1) {
+ g_epoll_fd.reset();
+
+ android::base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
+ if (epoll_fd == -1) {
return -1;
}
- bool epollctlfail = false;
- DIR* dir = opendir("/dev/input");
- if (dir != nullptr) {
- dirent* de;
- while ((de = readdir(dir))) {
- if (strncmp(de->d_name, "event", 5)) continue;
- int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
- if (fd == -1) continue;
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/dev/input"), closedir);
+ if (!dir) {
+ return -1;
+ }
- // Use unsigned long to match ioctl's parameter type.
- unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
+ bool epoll_ctl_failed = false;
+ dirent* de;
+ while ((de = readdir(dir.get())) != nullptr) {
+ if (strncmp(de->d_name, "event", 5)) continue;
+ android::base::unique_fd fd(openat(dirfd(dir.get()), de->d_name, O_RDONLY | O_CLOEXEC));
+ if (fd == -1) continue;
- // Read the evbits of the input device.
- if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
- close(fd);
- continue;
- }
+ // Use unsigned long to match ioctl's parameter type.
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
- // 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)) {
- close(fd);
- continue;
- }
- }
-
- epoll_event ev;
- ev.events = EPOLLIN | EPOLLWAKEUP;
- ev.data.ptr = &ev_fdinfo[ev_count];
- if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
- close(fd);
- epollctlfail = true;
- continue;
- }
-
- ev_fdinfo[ev_count].fd = fd;
- ev_fdinfo[ev_count].cb = std::move(input_cb);
- ev_count++;
- ev_dev_count++;
- if (ev_dev_count == MAX_DEVICES) break;
+ // Read the evbits of the input device.
+ if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ continue;
}
- closedir(dir);
+ // 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];
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ epoll_ctl_failed = true;
+ continue;
+ }
+
+ ev_fdinfo[g_ev_count].fd.reset(fd.release());
+ ev_fdinfo[g_ev_count].cb = input_cb;
+ g_ev_count++;
+ g_ev_dev_count++;
+ if (g_ev_dev_count == MAX_DEVICES) break;
}
- if (epollctlfail && !ev_count) {
- close(g_epoll_fd);
- g_epoll_fd = -1;
+ if (epoll_ctl_failed && !g_ev_count) {
return -1;
}
+ g_epoll_fd.reset(epoll_fd.release());
return 0;
}
int ev_get_epollfd(void) {
- return g_epoll_fd;
+ return g_epoll_fd.get();
}
-int ev_add_fd(int fd, ev_callback cb) {
- if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
+int ev_add_fd(android::base::unique_fd&& fd, ev_callback cb) {
+ if (g_ev_misc_count == MAX_MISC_FDS || cb == nullptr) {
return -1;
}
epoll_event ev;
ev.events = EPOLLIN | EPOLLWAKEUP;
- ev.data.ptr = static_cast<void*>(&ev_fdinfo[ev_count]);
+ ev.data.ptr = static_cast<void*>(&ev_fdinfo[g_ev_count]);
int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
if (!ret) {
- ev_fdinfo[ev_count].fd = fd;
- ev_fdinfo[ev_count].cb = std::move(cb);
- ev_count++;
- ev_misc_count++;
+ ev_fdinfo[g_ev_count].fd.reset(fd.release());
+ ev_fdinfo[g_ev_count].cb = std::move(cb);
+ g_ev_count++;
+ g_ev_misc_count++;
}
return ret;
}
void ev_exit(void) {
- while (ev_count > 0) {
- close(ev_fdinfo[--ev_count].fd);
- }
- ev_misc_count = 0;
- ev_dev_count = 0;
- close(g_epoll_fd);
+ while (g_ev_count > 0) {
+ ev_fdinfo[--g_ev_count].fd.reset();
+ }
+ g_ev_misc_count = 0;
+ g_ev_dev_count = 0;
+ g_epoll_fd.reset();
}
int ev_wait(int timeout) {
- npolledevents = epoll_wait(g_epoll_fd, polledevents, ev_count, timeout);
- if (npolledevents <= 0) {
- return -1;
- }
- return 0;
+ g_polled_events_count = epoll_wait(g_epoll_fd, g_polled_events, g_ev_count, timeout);
+ if (g_polled_events_count <= 0) {
+ return -1;
+ }
+ return 0;
}
void ev_dispatch(void) {
- for (int n = 0; n < npolledevents; n++) {
- fd_info* fdi = static_cast<fd_info*>(polledevents[n].data.ptr);
+ for (int n = 0; n < g_polled_events_count; n++) {
+ FdInfo* fdi = static_cast<FdInfo*>(g_polled_events[n].data.ptr);
const ev_callback& cb = fdi->cb;
if (cb) {
- cb(fdi->fd, polledevents[n].events);
+ cb(fdi->fd, g_polled_events[n].events);
}
}
}
@@ -180,7 +184,7 @@
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT
- for (size_t i = 0; i < ev_dev_count; ++i) {
+ for (size_t i = 0; i < g_ev_dev_count; ++i) {
memset(ev_bits, 0, sizeof(ev_bits));
memset(key_bits, 0, sizeof(key_bits));
@@ -205,37 +209,36 @@
}
void ev_iterate_available_keys(const std::function<void(int)>& f) {
- // Use unsigned long to match ioctl's parameter type.
- unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
- unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT
+ // Use unsigned long to match ioctl's parameter type.
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
+ unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT
- for (size_t i = 0; i < ev_dev_count; ++i) {
- memset(ev_bits, 0, sizeof(ev_bits));
- memset(key_bits, 0, sizeof(key_bits));
+ for (size_t i = 0; i < g_ev_dev_count; ++i) {
+ memset(ev_bits, 0, sizeof(ev_bits));
+ memset(key_bits, 0, sizeof(key_bits));
- // Does this device even have keys?
- if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
- continue;
- }
- if (!test_bit(EV_KEY, ev_bits)) {
- continue;
- }
-
- int rc = ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_KEY, KEY_MAX), key_bits);
- if (rc == -1) {
- continue;
- }
-
- for (int key_code = 0; key_code <= KEY_MAX; ++key_code) {
- if (test_bit(key_code, key_bits)) {
- f(key_code);
- }
- }
+ // Does this device even have keys?
+ if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ continue;
}
+ if (!test_bit(EV_KEY, ev_bits)) {
+ continue;
+ }
+
+ if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_KEY, KEY_MAX), key_bits) == -1) {
+ continue;
+ }
+
+ for (int key_code = 0; key_code <= KEY_MAX; ++key_code) {
+ if (test_bit(key_code, key_bits)) {
+ f(key_code);
+ }
+ }
+ }
}
void ev_iterate_touch_inputs(const std::function<void(int)>& action) {
- for (size_t i = 0; i < ev_dev_count; ++i) {
+ for (size_t i = 0; i < g_ev_dev_count; ++i) {
// Use unsigned long to match ioctl's parameter type.
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)] = {}; // NOLINT
if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp
index 9eea497..10cd607 100644
--- a/minui/graphics_adf.cpp
+++ b/minui/graphics_adf.cpp
@@ -101,7 +101,7 @@
err = adf_device_attach(dev, eng_id, intf_id);
if (err < 0 && err != -EALREADY) return err;
- intf_fd = adf_interface_open(dev, intf_id, O_RDWR);
+ intf_fd = adf_interface_open(dev, intf_id, O_RDWR | O_CLOEXEC);
if (intf_fd < 0) return intf_fd;
err = InterfaceInit();
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp
index 765e262..7b2eed1 100644
--- a/minui/graphics_drm.cpp
+++ b/minui/graphics_drm.cpp
@@ -285,7 +285,7 @@
/* Consider DRM devices in order. */
for (int i = 0; i < DRM_MAX_MINOR; i++) {
auto dev_name = android::base::StringPrintf(DRM_DEV_NAME, DRM_DIR_NAME, i);
- android::base::unique_fd fd(open(dev_name.c_str(), O_RDWR));
+ android::base::unique_fd fd(open(dev_name.c_str(), O_RDWR | O_CLOEXEC));
if (fd == -1) continue;
/* We need dumb buffers. */
diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp
index 8d9c974..2584017 100644
--- a/minui/graphics_fbdev.cpp
+++ b/minui/graphics_fbdev.cpp
@@ -56,7 +56,7 @@
}
GRSurface* MinuiBackendFbdev::Init() {
- android::base::unique_fd fd(open("/dev/graphics/fb0", O_RDWR));
+ android::base::unique_fd fd(open("/dev/graphics/fb0", O_RDWR | O_CLOEXEC));
if (fd == -1) {
perror("cannot open fb0");
return nullptr;
diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h
index e49c6ac..36bdcf1 100644
--- a/minui/include/minui/minui.h
+++ b/minui/include/minui/minui.h
@@ -26,6 +26,7 @@
#include <vector>
#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
//
// Graphics.
@@ -153,7 +154,7 @@
int ev_init(ev_callback input_cb, bool allow_touch_inputs = false);
void ev_exit();
-int ev_add_fd(int fd, ev_callback cb);
+int ev_add_fd(android::base::unique_fd&& fd, ev_callback cb);
void ev_iterate_available_keys(const std::function<void(int)>& f);
void ev_iterate_touch_inputs(const std::function<void(int)>& action);
int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
diff --git a/otautil/Android.bp b/otautil/Android.bp
index 41018dd..73398c3 100644
--- a/otautil/Android.bp
+++ b/otautil/Android.bp
@@ -40,16 +40,32 @@
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",
],
+
+ export_static_lib_headers: [
+ "libfstab",
+ ],
},
},
}
diff --git a/logging.h b/otautil/include/otautil/logging.h
similarity index 79%
rename from logging.h
rename to otautil/include/otautil/logging.h
index 3cfbc7a..6083497 100644
--- a/logging.h
+++ b/otautil/include/otautil/logging.h
@@ -18,14 +18,26 @@
#define _LOGGING_H
#include <stddef.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <string>
+#include <vector>
#include <log/log_id.h>
static constexpr int KEEP_LOG_COUNT = 10;
+struct selabel_handle;
+
+struct saved_log_file {
+ std::string name;
+ struct stat sb;
+ std::string data;
+};
+
+void SetLoggingSehandle(selabel_handle* handle);
+
ssize_t logbasename(log_id_t id, char prio, const char* filename, const char* buf, size_t len,
void* arg);
@@ -41,10 +53,13 @@
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_log_file(const std::string& source, const std::string& destination, bool append);
-void copy_logs(bool modified_flash, bool has_cache);
+void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle);
void reset_tmplog_offset();
void save_kernel_log(const char* destination);
+std::vector<saved_log_file> ReadLogFilesToMemory();
+
+bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files);
+
#endif //_LOGGING_H
diff --git a/roots.h b/otautil/include/otautil/roots.h
similarity index 95%
rename from roots.h
rename to otautil/include/otautil/roots.h
index 7b031a1..482f3d0 100644
--- a/roots.h
+++ b/otautil/include/otautil/roots.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef RECOVERY_ROOTS_H_
-#define RECOVERY_ROOTS_H_
+#pragma once
#include <string>
@@ -58,5 +57,3 @@
bool logical_partitions_mapped();
std::string get_system_root();
-
-#endif // RECOVERY_ROOTS_H_
diff --git a/otautil/include/otautil/sysutil.h b/otautil/include/otautil/sysutil.h
index 2eeb7c3..692a99e 100644
--- a/otautil/include/otautil/sysutil.h
+++ b/otautil/include/otautil/sysutil.h
@@ -22,6 +22,57 @@
#include <string>
#include <vector>
+#include "rangeset.h"
+
+// This class holds the content of a block map file.
+class BlockMapData {
+ public:
+ // A "block map" which looks like this (from uncrypt/uncrypt.cpp):
+ //
+ // /dev/block/platform/msm_sdcc.1/by-name/userdata # block device
+ // 49652 4096 # file size in bytes, block size
+ // 3 # count of block ranges
+ // 1000 1008 # block range 0
+ // 2100 2102 # ... block range 1
+ // 30 33 # ... block range 2
+ //
+ // Each block range represents a half-open interval; the line "30 33" reprents the blocks
+ // [30, 31, 32].
+ static BlockMapData ParseBlockMapFile(const std::string& block_map_path);
+
+ explicit operator bool() const {
+ return !path_.empty();
+ }
+
+ std::string path() const {
+ return path_;
+ }
+ uint64_t file_size() const {
+ return file_size_;
+ }
+ uint32_t block_size() const {
+ return block_size_;
+ }
+ RangeSet block_ranges() const {
+ return block_ranges_;
+ }
+
+ private:
+ BlockMapData() = default;
+
+ BlockMapData(const std::string& path, uint64_t file_size, uint32_t block_size,
+ RangeSet block_ranges)
+ : path_(path),
+ file_size_(file_size),
+ block_size_(block_size),
+ block_ranges_(std::move(block_ranges)) {}
+
+ std::string path_;
+ uint64_t file_size_ = 0;
+ uint32_t block_size_ = 0;
+ RangeSet block_ranges_;
+};
+
/*
* Use this to keep track of mapped segments.
*/
diff --git a/logging.cpp b/otautil/logging.cpp
similarity index 65%
rename from logging.cpp
rename to otautil/logging.cpp
index 283d115..484f115 100644
--- a/logging.cpp
+++ b/otautil/logging.cpp
@@ -14,38 +14,51 @@
* limitations under the License.
*/
-#include "logging.h"
+#include "otautil/logging.h"
+#include <dirent.h>
+#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/klog.h>
#include <sys/types.h>
+#include <algorithm>
+#include <memory>
#include <string>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <private/android_filesystem_config.h> /* for AID_SYSTEM */
#include <private/android_logger.h> /* private pmsg functions */
+#include <selinux/label.h>
-#include "common.h"
#include "otautil/dirutil.h"
#include "otautil/paths.h"
-#include "roots.h"
+#include "otautil/roots.h"
-static constexpr const char* LOG_FILE = "/cache/recovery/log";
-static constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install";
-static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
-static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
+constexpr const char* LOG_FILE = "/cache/recovery/log";
+constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install";
+constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
+constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
-static const std::string LAST_KMSG_FILTER = "recovery/last_kmsg";
-static const std::string LAST_LOG_FILTER = "recovery/last_log";
+constexpr const char* LAST_KMSG_FILTER = "recovery/last_kmsg";
+constexpr const char* LAST_LOG_FILTER = "recovery/last_log";
+
+constexpr const char* CACHE_LOG_DIR = "/cache/recovery";
+
+static struct selabel_handle* logging_sehandle;
+
+void SetLoggingSehandle(selabel_handle* handle) {
+ logging_sehandle = handle;
+}
// fopen(3)'s the given file, by mounting volumes and making parent dirs as necessary. Returns the
// file pointer, or nullptr on error.
-static FILE* fopen_path(const std::string& path, const char* mode) {
+static FILE* fopen_path(const std::string& path, const char* mode, const selabel_handle* sehandle) {
if (ensure_path_mounted(path) != 0) {
LOG(ERROR) << "Can't mount " << path;
return nullptr;
@@ -74,8 +87,8 @@
ssize_t logbasename(log_id_t /* id */, char /* prio */, const char* filename, const char* /* buf */,
size_t len, void* arg) {
bool* do_rotate = static_cast<bool*>(arg);
- if (LAST_KMSG_FILTER.find(filename) != std::string::npos ||
- LAST_LOG_FILTER.find(filename) != std::string::npos) {
+ if (std::string(LAST_KMSG_FILTER).find(filename) != std::string::npos ||
+ std::string(LAST_LOG_FILTER).find(filename) != std::string::npos) {
*do_rotate = true;
}
return len;
@@ -92,8 +105,8 @@
size_t dot = name.find_last_of('.');
std::string sub = name.substr(0, dot);
- if (LAST_KMSG_FILTER.find(sub) == std::string::npos &&
- LAST_LOG_FILTER.find(sub) == std::string::npos) {
+ if (std::string(LAST_KMSG_FILTER).find(sub) == std::string::npos &&
+ std::string(LAST_LOG_FILTER).find(sub) == std::string::npos) {
return __android_log_pmsg_file_write(id, prio, filename, buf, len);
}
@@ -165,8 +178,9 @@
tmplog_offset = 0;
}
-void copy_log_file(const std::string& source, const std::string& destination, bool append) {
- FILE* dest_fp = fopen_path(destination, append ? "ae" : "we");
+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);
if (dest_fp == nullptr) {
PLOG(ERROR) << "Can't open " << destination;
} else {
@@ -189,11 +203,11 @@
}
}
-void copy_logs(bool modified_flash, bool has_cache) {
- // We only rotate and record the log of the current session if there are actual attempts to modify
- // the flash, such as wipes, installs from BCB or menu selections. This is to avoid unnecessary
+void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle) {
+ // 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.
- if (!modified_flash) {
+ if (!save_current_log) {
return;
}
@@ -211,9 +225,9 @@
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);
- copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false);
- copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false);
+ 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);
save_kernel_log(LAST_KMSG_FILE);
chmod(LOG_FILE, 0600);
chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM);
@@ -242,3 +256,70 @@
buffer.resize(n);
android::base::WriteStringToFile(buffer, destination);
}
+
+std::vector<saved_log_file> ReadLogFilesToMemory() {
+ ensure_path_mounted("/cache");
+
+ struct dirent* de;
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir);
+ if (!d) {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
+ }
+ return {};
+ }
+
+ std::vector<saved_log_file> log_files;
+ while ((de = readdir(d.get())) != nullptr) {
+ if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
+ std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name);
+
+ struct stat sb;
+ if (stat(path.c_str(), &sb) != 0) {
+ PLOG(ERROR) << "Failed to stat " << path;
+ continue;
+ }
+ // Truncate files to 512kb
+ size_t read_size = std::min<size_t>(sb.st_size, 1 << 19);
+ std::string data(read_size, '\0');
+
+ android::base::unique_fd log_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY)));
+ if (log_fd == -1 || !android::base::ReadFully(log_fd, data.data(), read_size)) {
+ PLOG(ERROR) << "Failed to read log file " << path;
+ continue;
+ }
+
+ log_files.emplace_back(saved_log_file{ path, sb, data });
+ }
+ }
+
+ return log_files;
+}
+
+bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files) {
+ // Re-create the log dir and write back the log entries.
+ if (ensure_path_mounted(CACHE_LOG_DIR) != 0) {
+ PLOG(ERROR) << "Failed to mount " << CACHE_LOG_DIR;
+ return false;
+ }
+
+ if (mkdir_recursively(CACHE_LOG_DIR, 0777, false, logging_sehandle) != 0) {
+ PLOG(ERROR) << "Failed to create " << CACHE_LOG_DIR;
+ return false;
+ }
+
+ for (const auto& log : log_files) {
+ if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
+ log.sb.st_gid)) {
+ PLOG(ERROR) << "Failed to write to " << log.name;
+ }
+ }
+
+ // Any part of the log we'd copied to cache is now gone.
+ // 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);
+
+ return true;
+}
diff --git a/roots.cpp b/otautil/roots.cpp
similarity index 97%
rename from roots.cpp
rename to otautil/roots.cpp
index 7a922b8..815d644 100644
--- a/roots.cpp
+++ b/otautil/roots.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "roots.h"
+#include "otautil/roots.h"
#include <ctype.h>
#include <fcntl.h>
@@ -51,8 +51,6 @@
static Fstab fstab;
-extern struct selabel_handle* sehandle;
-
void load_volume_table() {
if (!ReadDefaultFstab(&fstab)) {
LOG(ERROR) << "Failed to read default fstab";
@@ -176,11 +174,9 @@
PLOG(ERROR) << "format_volume: failed to open " << v->blk_device;
return -1;
}
- length =
- get_file_size(fd.get(), v->length ? -v->length : CRYPT_FOOTER_OFFSET);
+ length = get_file_size(fd.get(), v->length ? -v->length : CRYPT_FOOTER_OFFSET);
if (length <= 0) {
- LOG(ERROR) << "get_file_size: invalid size " << length << " for "
- << v->blk_device;
+ LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device;
return -1;
}
}
diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp
index d8969a0..8366fa0 100644
--- a/otautil/sysutil.cpp
+++ b/otautil/sysutil.cpp
@@ -18,12 +18,13 @@
#include <errno.h> // TEMP_FAILURE_RETRY
#include <fcntl.h>
-#include <stdint.h> // SIZE_MAX
+#include <inttypes.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <algorithm>
+#include <limits>
#include <string>
#include <vector>
@@ -34,6 +35,68 @@
#include <android-base/unique_fd.h>
#include <cutils/android_reboot.h>
+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;
+ return {};
+ }
+
+ std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
+ if (lines.size() < 4) {
+ LOG(ERROR) << "Block map file is too short: " << lines.size();
+ return {};
+ }
+
+ const std::string& block_dev = lines[0];
+
+ uint64_t file_size;
+ uint32_t blksize;
+ if (sscanf(lines[1].c_str(), "%" SCNu64 "%" SCNu32, &file_size, &blksize) != 2) {
+ LOG(ERROR) << "Failed to parse file size and block size: " << lines[1];
+ return {};
+ }
+
+ if (file_size == 0 || blksize == 0) {
+ LOG(ERROR) << "Invalid size in block map file: size " << file_size << ", blksize " << blksize;
+ return {};
+ }
+
+ size_t range_count;
+ if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) {
+ LOG(ERROR) << "Failed to parse block map header: " << lines[2];
+ return {};
+ }
+
+ uint64_t blocks = ((file_size - 1) / blksize) + 1;
+ if (blocks > std::numeric_limits<uint32_t>::max() || range_count == 0 ||
+ lines.size() != 3 + range_count) {
+ LOG(ERROR) << "Invalid data in block map file: size " << file_size << ", blksize " << blksize
+ << ", range_count " << range_count << ", lines " << lines.size();
+ return {};
+ }
+
+ RangeSet ranges;
+ uint64_t remaining_blocks = blocks;
+ for (size_t i = 0; i < range_count; ++i) {
+ const std::string& line = lines[i + 3];
+ uint64_t start, end;
+ if (sscanf(line.c_str(), "%" SCNu64 "%" SCNu64, &start, &end) != 2) {
+ LOG(ERROR) << "failed to parse range " << i << ": " << line;
+ return {};
+ }
+ uint64_t range_blocks = end - start;
+ if (end <= start || range_blocks > remaining_blocks) {
+ LOG(ERROR) << "Invalid range: " << start << " " << end;
+ return {};
+ }
+ ranges.PushBack({ start, end });
+ remaining_blocks -= range_blocks;
+ }
+
+ return BlockMapData(block_dev, file_size, blksize, std::move(ranges));
+}
+
bool MemMapping::MapFD(int fd) {
struct stat sb;
if (fstat(fd, &sb) == -1) {
@@ -55,115 +118,61 @@
return true;
}
-// A "block map" which looks like this (from uncrypt/uncrypt.cpp):
-//
-// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device
-// 49652 4096 # file size in bytes, block size
-// 3 # count of block ranges
-// 1000 1008 # block range 0
-// 2100 2102 # ... block range 1
-// 30 33 # ... block range 2
-//
-// Each block range represents a half-open interval; the line "30 33" reprents the blocks
-// [30, 31, 32].
bool MemMapping::MapBlockFile(const std::string& filename) {
- std::string content;
- if (!android::base::ReadFileToString(filename, &content)) {
- PLOG(ERROR) << "Failed to read " << filename;
+ auto block_map_data = BlockMapData::ParseBlockMapFile(filename);
+ if (!block_map_data) {
return false;
}
- std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
- if (lines.size() < 4) {
- LOG(ERROR) << "Block map file is too short: " << lines.size();
- return false;
- }
-
- size_t size;
- size_t blksize;
- if (sscanf(lines[1].c_str(), "%zu %zu", &size, &blksize) != 2) {
- LOG(ERROR) << "Failed to parse file size and block size: " << lines[1];
- return false;
- }
-
- size_t range_count;
- if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) {
- LOG(ERROR) << "Failed to parse block map header: " << lines[2];
- return false;
- }
-
- size_t blocks;
- if (blksize != 0) {
- blocks = ((size - 1) / blksize) + 1;
- }
- if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0 ||
- lines.size() != 3 + range_count) {
- LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize
- << ", range_count " << range_count << ", lines " << lines.size();
+ if (block_map_data.file_size() > std::numeric_limits<size_t>::max()) {
+ LOG(ERROR) << "File size is too large for mmap " << block_map_data.file_size();
return false;
}
// Reserve enough contiguous address space for the whole file.
+ uint32_t blksize = block_map_data.block_size();
+ uint64_t blocks = ((block_map_data.file_size() - 1) / blksize) + 1;
void* reserve = mmap(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (reserve == MAP_FAILED) {
PLOG(ERROR) << "failed to reserve address space";
return false;
}
- const std::string& block_dev = lines[0];
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_dev.c_str(), O_RDONLY)));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_map_data.path().c_str(), O_RDONLY)));
if (fd == -1) {
- PLOG(ERROR) << "failed to open block device " << block_dev;
+ PLOG(ERROR) << "failed to open block device " << block_map_data.path();
munmap(reserve, blocks * blksize);
return false;
}
ranges_.clear();
- unsigned char* next = static_cast<unsigned char*>(reserve);
+ auto next = static_cast<unsigned char*>(reserve);
size_t remaining_size = blocks * blksize;
- bool success = true;
- for (size_t i = 0; i < range_count; ++i) {
- const std::string& line = lines[i + 3];
-
- size_t start, end;
- if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) {
- LOG(ERROR) << "failed to parse range " << i << ": " << line;
- success = false;
- break;
- }
+ for (const auto& [start, end] : block_map_data.block_ranges()) {
size_t range_size = (end - start) * blksize;
- if (end <= start || (end - start) > SIZE_MAX / blksize || range_size > remaining_size) {
- LOG(ERROR) << "Invalid range: " << start << " " << end;
- success = false;
- break;
- }
-
void* range_start = mmap(next, range_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd,
static_cast<off_t>(start) * blksize);
if (range_start == MAP_FAILED) {
- PLOG(ERROR) << "failed to map range " << i << ": " << line;
- success = false;
- break;
+ PLOG(ERROR) << "failed to map range " << start << ": " << end;
+ munmap(reserve, blocks * blksize);
+ return false;
}
ranges_.emplace_back(MappedRange{ range_start, range_size });
next += range_size;
remaining_size -= range_size;
}
- if (success && remaining_size != 0) {
+ if (remaining_size != 0) {
LOG(ERROR) << "Invalid ranges: remaining_size " << remaining_size;
- success = false;
- }
- if (!success) {
munmap(reserve, blocks * blksize);
return false;
}
addr = static_cast<unsigned char*>(reserve);
- length = size;
+ length = block_map_data.file_size();
- LOG(INFO) << "mmapped " << range_count << " ranges";
+ LOG(INFO) << "mmapped " << block_map_data.block_ranges().size() << " ranges";
return true;
}
diff --git a/recovery-persist.cpp b/recovery-persist.cpp
index e2a6699..294017a 100644
--- a/recovery-persist.cpp
+++ b/recovery-persist.cpp
@@ -43,7 +43,7 @@
#include <metricslogger/metrics_logger.h>
#include <private/android_logger.h> /* private pmsg functions */
-#include "logging.h"
+#include "otautil/logging.h"
#include "otautil/parse_install_logs.h"
constexpr const char* LAST_LOG_FILE = "/data/misc/recovery/last_log";
diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp
index aee1ca5..d41755d 100644
--- a/recovery-refresh.cpp
+++ b/recovery-refresh.cpp
@@ -42,7 +42,7 @@
#include <private/android_logger.h> /* private pmsg functions */
-#include "logging.h"
+#include "otautil/logging.h"
int main(int argc, char **argv) {
static const char filter[] = "recovery/";
diff --git a/recovery.cpp b/recovery.cpp
index 703923e..0e6e497 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -17,7 +17,6 @@
#include "recovery.h"
#include <ctype.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@@ -25,13 +24,10 @@
#include <limits.h>
#include <linux/fs.h>
#include <linux/input.h>
-#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/wait.h>
#include <unistd.h>
#include <algorithm>
@@ -52,38 +48,33 @@
#include <healthhalutils/HealthHalUtils.h>
#include <ziparchive/zip_archive.h>
-#include "adb_install.h"
#include "common.h"
-#include "device.h"
#include "fsck_unshare_blocks.h"
-#include "fuse_sdcard_provider.h"
-#include "fuse_sideload.h"
-#include "install.h"
-#include "logging.h"
-#include "otautil/dirutil.h"
+#include "install/adb_install.h"
+#include "install/fuse_sdcard_install.h"
+#include "install/install.h"
+#include "install/package.h"
+#include "install/wipe_data.h"
#include "otautil/error_code.h"
+#include "otautil/logging.h"
#include "otautil/paths.h"
+#include "otautil/roots.h"
#include "otautil/sysutil.h"
-#include "roots.h"
-#include "screen_ui.h"
-#include "ui.h"
+#include "recovery_ui/screen_ui.h"
+#include "recovery_ui/ui.h"
-static constexpr const char* CACHE_LOG_DIR = "/cache/recovery";
static constexpr const char* COMMAND_FILE = "/cache/recovery/command";
static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale";
static constexpr const char* CACHE_ROOT = "/cache";
-static constexpr const char* DATA_ROOT = "/data";
-static constexpr const char* METADATA_ROOT = "/metadata";
-static constexpr const char* SDCARD_ROOT = "/sdcard";
// 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.");
-bool modified_flash = false;
+static bool save_current_log = false;
std::string stage;
const char* reason = nullptr;
@@ -137,16 +128,6 @@
return android::base::GetBoolProperty("ro.debuggable", false);
}
-// Set the BCB to reboot back into recovery (it won't resume the install from
-// sdcard though).
-static void set_sdcard_update_bootloader_message() {
- std::vector<std::string> options;
- std::string err;
- if (!update_bootloader_message(options, &err)) {
- LOG(ERROR) << "Failed to set BCB message: " << err;
- }
-}
-
// 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.
@@ -163,7 +144,7 @@
}
}
- copy_logs(modified_flash, has_cache);
+ copy_logs(save_current_log, has_cache, sehandle);
// Reset to normal system boot so recovery won't cycle indefinitely.
std::string err;
@@ -182,182 +163,6 @@
sync(); // For good measure.
}
-struct saved_log_file {
- std::string name;
- struct stat sb;
- std::string data;
-};
-
-static bool erase_volume(const char* volume) {
- bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
- bool is_data = (strcmp(volume, DATA_ROOT) == 0);
-
- ui->SetBackground(RecoveryUI::ERASING);
- ui->SetProgressType(RecoveryUI::INDETERMINATE);
-
- std::vector<saved_log_file> log_files;
-
- if (is_cache) {
- // If we're reformatting /cache, we load any past logs
- // (i.e. "/cache/recovery/last_*") and the current log
- // ("/cache/recovery/log") into memory, so we can restore them after
- // the reformat.
-
- ensure_path_mounted(volume);
-
- struct dirent* de;
- std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir);
- if (d) {
- while ((de = readdir(d.get())) != nullptr) {
- if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
- std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name);
-
- struct stat sb;
- if (stat(path.c_str(), &sb) == 0) {
- // truncate files to 512kb
- if (sb.st_size > (1 << 19)) {
- sb.st_size = 1 << 19;
- }
-
- std::string data(sb.st_size, '\0');
- FILE* f = fopen(path.c_str(), "rbe");
- fread(&data[0], 1, data.size(), f);
- fclose(f);
-
- log_files.emplace_back(saved_log_file{ path, sb, data });
- }
- }
- }
- } else {
- if (errno != ENOENT) {
- PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
- }
- }
- }
-
- ui->Print("Formatting %s...\n", volume);
-
- ensure_path_unmounted(volume);
-
- int result;
- if (is_data && reason && strcmp(reason, "convert_fbe") == 0) {
- static constexpr const char* CONVERT_FBE_DIR = "/tmp/convert_fbe";
- static constexpr const char* CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe";
- // Create convert_fbe breadcrumb file to signal init to convert to file based encryption, not
- // full disk encryption.
- if (mkdir(CONVERT_FBE_DIR, 0700) != 0) {
- PLOG(ERROR) << "Failed to mkdir " << CONVERT_FBE_DIR;
- return false;
- }
- FILE* f = fopen(CONVERT_FBE_FILE, "wbe");
- if (!f) {
- PLOG(ERROR) << "Failed to convert to file encryption";
- return false;
- }
- fclose(f);
- result = format_volume(volume, CONVERT_FBE_DIR);
- remove(CONVERT_FBE_FILE);
- rmdir(CONVERT_FBE_DIR);
- } else {
- result = format_volume(volume);
- }
-
- if (is_cache) {
- // Re-create the log dir and write back the log entries.
- if (ensure_path_mounted(CACHE_LOG_DIR) == 0 &&
- mkdir_recursively(CACHE_LOG_DIR, 0777, false, sehandle) == 0) {
- for (const auto& log : log_files) {
- if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
- log.sb.st_gid)) {
- PLOG(ERROR) << "Failed to write to " << log.name;
- }
- }
- } else {
- PLOG(ERROR) << "Failed to mount / create " << CACHE_LOG_DIR;
- }
-
- // Any part of the log we'd copied to cache is now gone.
- // Reset the pointer so we copy from the beginning of the temp
- // log.
- reset_tmplog_offset();
- copy_logs(modified_flash, has_cache);
- }
-
- return (result == 0);
-}
-
-// Sets the usb config to 'state'
-bool SetUsbConfig(const std::string& state) {
- android::base::SetProperty("sys.usb.config", state);
- return android::base::WaitForProperty("sys.usb.state", state);
-}
-
-// Returns the selected filename, or an empty string.
-static std::string browse_directory(const std::string& path, Device* device) {
- ensure_path_mounted(path);
-
- std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
- if (!d) {
- PLOG(ERROR) << "error opening " << path;
- return "";
- }
-
- std::vector<std::string> dirs;
- std::vector<std::string> entries{ "../" }; // "../" is always the first entry.
-
- dirent* de;
- while ((de = readdir(d.get())) != nullptr) {
- std::string name(de->d_name);
-
- if (de->d_type == DT_DIR) {
- // Skip "." and ".." entries.
- if (name == "." || name == "..") continue;
- dirs.push_back(name + "/");
- } else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) {
- entries.push_back(name);
- }
- }
-
- std::sort(dirs.begin(), dirs.end());
- std::sort(entries.begin(), entries.end());
-
- // Append dirs to the entries list.
- entries.insert(entries.end(), dirs.begin(), dirs.end());
-
- std::vector<std::string> headers{ "Choose a package to install:", path };
-
- size_t chosen_item = 0;
- while (true) {
- chosen_item = ui->ShowMenu(
- headers, entries, chosen_item, true,
- std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
-
- // Return if WaitKey() was interrupted.
- if (chosen_item == static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED)) {
- return "";
- }
-
- const std::string& item = entries[chosen_item];
- if (chosen_item == 0) {
- // Go up but continue browsing (if the caller is browse_directory).
- return "";
- }
-
- std::string new_path = path + "/" + item;
- if (new_path.back() == '/') {
- // Recurse down into a subdirectory.
- new_path.pop_back();
- std::string result = browse_directory(new_path, device);
- if (!result.empty()) return result;
- } else {
- // Selected a zip file: return the path to the caller.
- return new_path;
- }
- }
-
- // Unreachable.
-}
-
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" };
@@ -379,28 +184,6 @@
return (chosen_item == 1);
}
-// Return true on success.
-static bool wipe_data(Device* device) {
- modified_flash = true;
-
- ui->Print("\n-- Wiping data...\n");
- bool success = device->PreWipeData();
- if (success) {
- success &= erase_volume(DATA_ROOT);
- if (has_cache) {
- success &= erase_volume(CACHE_ROOT);
- }
- if (volume_for_mount_point(METADATA_ROOT) != nullptr) {
- success &= erase_volume(METADATA_ROOT);
- }
- }
- if (success) {
- success &= device->PostWipeData();
- }
- ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
- return success;
-}
-
static InstallResult prompt_and_wipe_data(Device* device) {
// Use a single string and let ScreenRecoveryUI handles the wrapping.
std::vector<std::string> wipe_data_menu_headers{
@@ -428,7 +211,8 @@
}
if (ask_to_wipe_data(device)) {
- if (wipe_data(device)) {
+ bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
+ if (WipeData(device, convert_fbe)) {
return INSTALL_SUCCESS;
} else {
return INSTALL_ERROR;
@@ -437,25 +221,6 @@
}
}
-// Return true on success.
-static bool wipe_cache(bool should_confirm, Device* device) {
- if (!has_cache) {
- ui->Print("No /cache partition found.\n");
- return false;
- }
-
- if (should_confirm && !yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!")) {
- return false;
- }
-
- modified_flash = true;
-
- ui->Print("\n-- Wiping cache...\n");
- bool success = erase_volume("/cache");
- ui->Print("Cache wipe %s.\n", success ? "complete" : "failed");
- return success;
-}
-
// 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) {
@@ -497,45 +262,93 @@
return true;
}
-// Check if the wipe package matches expectation:
+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 check_wipe_package(size_t wipe_package_size) {
- if (wipe_package_size == 0) {
- LOG(ERROR) << "wipe_package_size is zero";
- return false;
- }
- 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";
- return false;
- }
- if (!verify_package(reinterpret_cast<const unsigned char*>(wipe_package.data()),
- wipe_package.size())) {
- LOG(ERROR) << "Failed to verify package";
- return false;
- }
+static bool CheckWipePackage(Package* wipe_package) {
+ if (!verify_package(wipe_package, ui)) {
+ LOG(ERROR) << "Failed to verify package";
+ return false;
+ }
- // Extract metadata
- ZipArchiveHandle zip;
- int err = OpenArchiveFromMemory(static_cast<void*>(&wipe_package[0]), wipe_package.size(),
- "wipe_package", &zip);
- if (err != 0) {
- LOG(ERROR) << "Can't open wipe package : " << ErrorCodeString(err);
- 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.";
- std::map<std::string, std::string> metadata;
- if (!ReadMetadataFromPackage(zip, &metadata)) {
- LOG(ERROR) << "Failed to parse metadata in the zip file";
- return false;
+ 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 {};
}
+ }
- int result = CheckPackageMetadata(metadata, OtaType::BRICK);
- CloseArchive(zip);
+ 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 == 0;
+ return result;
}
// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE.
@@ -543,25 +356,24 @@
ui->SetBackground(RecoveryUI::ERASING);
ui->SetProgressType(RecoveryUI::INDETERMINATE);
- if (!check_wipe_package(wipe_package_size)) {
+ 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;
}
- static constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe";
- std::string partition_list;
- if (!android::base::ReadFileToString(RECOVERY_WIPE, &partition_list)) {
- LOG(ERROR) << "failed to read \"" << RECOVERY_WIPE << "\"";
+
+ auto partition_list = GetWipePartitionList(wipe_package.get());
+ if (partition_list.empty()) {
+ LOG(ERROR) << "Empty wipe ab partition list";
return false;
}
- std::vector<std::string> lines = android::base::Split(partition_list, "\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;
- }
-
+ for (const auto& partition : partition_list) {
// Proceed anyway even if it fails to wipe some partition.
secure_wipe_partition(partition);
}
@@ -661,84 +473,6 @@
ui->ShowText(true);
}
-// How long (in seconds) we wait for the fuse-provided package file to
-// appear, before timing out.
-#define SDCARD_INSTALL_TIMEOUT 10
-
-static int apply_from_sdcard(Device* device, bool* wipe_cache) {
- modified_flash = true;
-
- if (ensure_path_mounted(SDCARD_ROOT) != 0) {
- ui->Print("\n-- Couldn't mount %s.\n", SDCARD_ROOT);
- return INSTALL_ERROR;
- }
-
- std::string path = browse_directory(SDCARD_ROOT, device);
- if (path.empty()) {
- ui->Print("\n-- No package file selected.\n");
- ensure_path_unmounted(SDCARD_ROOT);
- return INSTALL_ERROR;
- }
-
- ui->Print("\n-- Install %s ...\n", path.c_str());
- set_sdcard_update_bootloader_message();
-
- // 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 = start_sdcard_fuse(path.c_str());
-
- _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;
- int status;
- bool waited = false;
- for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
- if (waitpid(child, &status, WNOHANG) == -1) {
- result = INSTALL_ERROR;
- waited = true;
- break;
- }
-
- struct stat sb;
- if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &sb) == -1) {
- if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) {
- sleep(1);
- continue;
- } else {
- LOG(ERROR) << "Timed out waiting for the fuse-provided package.";
- result = INSTALL_ERROR;
- kill(child, SIGKILL);
- break;
- }
- }
-
- result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0 /*retry_count*/);
- break;
- }
-
- if (!waited) {
- // Calling stat() on this magic filename signals the fuse
- // filesystem to shut down.
- struct stat sb;
- stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &sb);
-
- waitpid(child, &status, 0);
- }
-
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- LOG(ERROR) << "Error exit from the fuse process: " << WEXITSTATUS(status);
- }
-
- ensure_path_unmounted(SDCARD_ROOT);
- return result;
-}
-
// 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) {
@@ -771,7 +505,6 @@
? Device::REBOOT
: device->InvokeMenuItem(chosen_item);
- bool should_wipe_cache = false;
switch (chosen_action) {
case Device::NO_ACTION:
break;
@@ -784,48 +517,47 @@
return chosen_action;
case Device::WIPE_DATA:
+ save_current_log = true;
if (ui->IsTextVisible()) {
if (ask_to_wipe_data(device)) {
- wipe_data(device);
+ WipeData(device, false);
}
} else {
- wipe_data(device);
+ WipeData(device, false);
return Device::NO_ACTION;
}
break;
- case Device::WIPE_CACHE:
- wipe_cache(ui->IsTextVisible(), device);
+ case Device::WIPE_CACHE: {
+ save_current_log = true;
+ std::function<bool()> confirm_func = [&device]() {
+ return yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!");
+ };
+ WipeCache(ui, ui->IsTextVisible() ? confirm_func : nullptr);
if (!ui->IsTextVisible()) return Device::NO_ACTION;
break;
-
+ }
case Device::APPLY_ADB_SIDELOAD:
- case Device::APPLY_SDCARD:
- {
- bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
- if (adb) {
- status = apply_from_adb(&should_wipe_cache);
- } else {
- status = apply_from_sdcard(device, &should_wipe_cache);
- }
+ case Device::APPLY_SDCARD: {
+ save_current_log = true;
+ bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
+ if (adb) {
+ status = apply_from_adb(ui);
+ } else {
+ status = ApplyFromSdcard(device, ui);
+ }
- if (status == INSTALL_SUCCESS && should_wipe_cache) {
- if (!wipe_cache(false, device)) {
- status = INSTALL_ERROR;
- }
- }
-
- if (status != INSTALL_SUCCESS) {
- ui->SetBackground(RecoveryUI::ERROR);
- ui->Print("Installation aborted.\n");
- copy_logs(modified_flash, has_cache);
- } else if (!ui->IsTextVisible()) {
- return Device::NO_ACTION; // reboot if logs aren't visible
- } else {
- ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card");
- }
+ if (status != INSTALL_SUCCESS) {
+ 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
+ } else {
+ ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card");
}
break;
+ }
case Device::VIEW_RECOVERY_LOGS:
choose_recovery_file(device);
@@ -857,20 +589,6 @@
printf("%s=%s\n", key, name);
}
-void ui_print(const char* format, ...) {
- std::string buffer;
- va_list ap;
- va_start(ap, format);
- android::base::StringAppendV(&buffer, format, ap);
- va_end(ap);
-
- if (ui != nullptr) {
- ui->Print("%s", buffer.c_str());
- } else {
- fputs(buffer.c_str(), stdout);
- }
-}
-
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;
@@ -1119,7 +837,7 @@
if (update_package != nullptr) {
// It's not entirely true that we will modify the flash. But we want
// to log the update attempt since update_package is non-NULL.
- modified_flash = true;
+ save_current_log = true;
int required_battery_level;
if (retry_count == 0 && !is_battery_ok(&required_battery_level)) {
@@ -1141,10 +859,7 @@
set_retry_bootloader_message(retry_count + 1, args);
}
- status = install_package(update_package, &should_wipe_cache, true, retry_count);
- if (status == INSTALL_SUCCESS && should_wipe_cache) {
- wipe_cache(false, device);
- }
+ status = install_package(update_package, should_wipe_cache, true, retry_count, ui);
if (status != INSTALL_SUCCESS) {
ui->Print("Installation aborted.\n");
@@ -1152,7 +867,7 @@
// 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(modified_flash, has_cache);
+ copy_logs(save_current_log, has_cache, sehandle);
retry_count += 1;
set_retry_bootloader_message(retry_count, args);
// Print retry count on screen.
@@ -1176,12 +891,14 @@
}
}
} else if (should_wipe_data) {
- if (!wipe_data(device)) {
+ save_current_log = true;
+ bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
+ if (!WipeData(device, convert_fbe)) {
status = INSTALL_ERROR;
}
} else if (should_prompt_and_wipe_data) {
// Trigger the logging to capture the cause, even if user chooses to not wipe data.
- modified_flash = true;
+ save_current_log = true;
ui->ShowText(true);
ui->SetBackground(RecoveryUI::ERROR);
@@ -1190,7 +907,8 @@
ui->ShowText(false);
}
} else if (should_wipe_cache) {
- if (!wipe_cache(false, device)) {
+ save_current_log = true;
+ if (!WipeCache(ui, nullptr)) {
status = INSTALL_ERROR;
}
} else if (should_wipe_ab) {
@@ -1204,15 +922,11 @@
// sideload finishes even if there are errors. Unless one turns on the
// text display during the installation. This is to enable automated
// testing.
+ save_current_log = true;
if (!sideload_auto_reboot) {
ui->ShowText(true);
}
- status = apply_from_adb(&should_wipe_cache);
- if (status == INSTALL_SUCCESS && should_wipe_cache) {
- if (!wipe_cache(false, device)) {
- status = INSTALL_ERROR;
- }
- }
+ status = apply_from_adb(ui);
ui->Print("\nInstall from ADB complete (status: %d).\n", status);
if (sideload_auto_reboot) {
ui->Print("Rebooting automatically.\n");
diff --git a/recovery.h b/recovery.h
index 00e22da..f050549 100644
--- a/recovery.h
+++ b/recovery.h
@@ -19,6 +19,6 @@
#include <string>
#include <vector>
-#include "device.h"
+#include "recovery_ui/device.h"
Device::BuiltinAction start_recovery(Device* device, const std::vector<std::string>& args);
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 6f50802..5f3ab76 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -49,16 +49,16 @@
#include <selinux/selinux.h>
#include "common.h"
-#include "device.h"
#include "fastboot/fastboot.h"
-#include "logging.h"
-#include "minadbd/minadbd.h"
+#include "install/wipe_data.h"
+#include "otautil/logging.h"
#include "otautil/paths.h"
+#include "otautil/roots.h"
#include "otautil/sysutil.h"
#include "recovery.h"
-#include "roots.h"
-#include "stub_ui.h"
-#include "ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/stub_ui.h"
+#include "recovery_ui/ui.h"
static constexpr const char* COMMAND_FILE = "/cache/recovery/command";
static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale";
@@ -178,6 +178,12 @@
return android::base::Trim(content);
}
+// Sets the usb config to 'state'.
+static bool SetUsbConfig(const std::string& state) {
+ android::base::SetProperty("sys.usb.config", state);
+ return android::base::WaitForProperty("sys.usb.state", state);
+}
+
static void ListenRecoverySocket(RecoveryUI* ui, std::atomic<Device::BuiltinAction>& action) {
android::base::unique_fd sock_fd(android_get_control_socket("recovery"));
if (sock_fd < 0) {
@@ -217,9 +223,10 @@
}
static void redirect_stdio(const char* filename) {
- int pipefd[2];
- if (pipe(pipefd) == -1) {
- PLOG(ERROR) << "pipe failed";
+ android::base::unique_fd pipe_read, pipe_write;
+ // Create a pipe that allows parent process sending logs over.
+ if (!android::base::Pipe(&pipe_read, &pipe_write)) {
+ PLOG(ERROR) << "Failed to create pipe for redirecting stdio";
// Fall back to traditional logging mode without timestamps. If these fail, there's not really
// anywhere to complain...
@@ -233,7 +240,7 @@
pid_t pid = fork();
if (pid == -1) {
- PLOG(ERROR) << "fork failed";
+ PLOG(ERROR) << "Failed to fork for redirecting stdio";
// Fall back to traditional logging mode without timestamps. If these fail, there's not really
// anywhere to complain...
@@ -246,8 +253,8 @@
}
if (pid == 0) {
- /// Close the unused write end.
- close(pipefd[1]);
+ // Child process reads the incoming logs and doesn't write to the pipe.
+ pipe_write.reset();
auto start = std::chrono::steady_clock::now();
@@ -255,15 +262,13 @@
FILE* log_fp = fopen(filename, "ae");
if (log_fp == nullptr) {
PLOG(ERROR) << "fopen \"" << filename << "\" failed";
- close(pipefd[0]);
_exit(EXIT_FAILURE);
}
- FILE* pipe_fp = fdopen(pipefd[0], "r");
+ FILE* pipe_fp = android::base::Fdopen(std::move(pipe_read), "r");
if (pipe_fp == nullptr) {
PLOG(ERROR) << "fdopen failed";
check_and_fclose(log_fp, filename);
- close(pipefd[0]);
_exit(EXIT_FAILURE);
}
@@ -283,25 +288,23 @@
PLOG(ERROR) << "getline failed";
+ fclose(pipe_fp);
free(line);
check_and_fclose(log_fp, filename);
- close(pipefd[0]);
_exit(EXIT_FAILURE);
} else {
// Redirect stdout/stderr to the logger process. Close the unused read end.
- close(pipefd[0]);
+ pipe_read.reset();
setbuf(stdout, nullptr);
setbuf(stderr, nullptr);
- if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
+ if (dup2(pipe_write.get(), STDOUT_FILENO) == -1) {
PLOG(ERROR) << "dup2 stdout failed";
}
- if (dup2(pipefd[1], STDERR_FILENO) == -1) {
+ if (dup2(pipe_write.get(), STDERR_FILENO) == -1) {
PLOG(ERROR) << "dup2 stderr failed";
}
-
- close(pipefd[1]);
}
}
@@ -319,16 +322,6 @@
// Take action to refresh pmsg contents
__android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &do_rotate);
- // If this binary is started with the single argument "--adbd", instead of being the normal
- // recovery binary, it turns into kind of a stripped-down version of adbd that only supports the
- // 'sideload' command. Note this must be a real argument, not anything in the command file or
- // bootloader control block; the only way recovery should be run with this argument is when it
- // starts a copy of itself from the apply_from_adb() function.
- if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
- minadbd_main();
- return 0;
- }
-
time_t start = time(nullptr);
// redirect_stdio should be called only in non-sideload mode. Otherwise we may have two logger
@@ -442,6 +435,8 @@
ui->Print("Warning: No file_contexts\n");
}
+ SetLoggingSehandle(sehandle);
+
std::atomic<Device::BuiltinAction> action;
std::thread listener_thread(ListenRecoverySocket, ui, std::ref(action));
listener_thread.detach();
@@ -458,6 +453,8 @@
}
}
+ ui->SetEnableFastbootdLogo(fastboot);
+
auto ret = fastboot ? StartFastboot(device, args) : start_recovery(device, args);
if (ret == Device::KEY_INTERRUPTED) {
diff --git a/recovery_ui/Android.bp b/recovery_ui/Android.bp
new file mode 100644
index 0000000..ee3149d
--- /dev/null
+++ b/recovery_ui/Android.bp
@@ -0,0 +1,91 @@
+// 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_library {
+ name: "librecovery_ui",
+ recovery_available: true,
+
+ defaults: [
+ "recovery_defaults",
+ ],
+
+ srcs: [
+ "device.cpp",
+ "screen_ui.cpp",
+ "ui.cpp",
+ "vr_ui.cpp",
+ "wear_ui.cpp",
+ ],
+
+ export_include_dirs: ["include"],
+
+ static_libs: [
+ "libminui",
+ "libotautil",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libpng",
+ "libz",
+ ],
+}
+
+// Generic device that uses ScreenRecoveryUI.
+cc_library_static {
+ name: "librecovery_ui_default",
+ recovery_available: true,
+
+ defaults: [
+ "recovery_defaults",
+ ],
+
+ srcs: [
+ "default_device.cpp",
+ ],
+
+ export_include_dirs: ["include"],
+}
+
+// The default wear device that uses WearRecoveryUI.
+cc_library_static {
+ name: "librecovery_ui_wear",
+ recovery_available: true,
+
+ defaults: [
+ "recovery_defaults",
+ ],
+
+ srcs: [
+ "wear_device.cpp",
+ ],
+
+ export_include_dirs: ["include"],
+}
+
+// The default VR device that uses VrRecoveryUI.
+cc_library_static {
+ name: "librecovery_ui_vr",
+ recovery_available: true,
+
+ defaults: [
+ "recovery_defaults",
+ ],
+
+ srcs: [
+ "vr_device.cpp",
+ ],
+
+ export_include_dirs: ["include"],
+}
diff --git a/default_device.cpp b/recovery_ui/default_device.cpp
similarity index 90%
rename from default_device.cpp
rename to recovery_ui/default_device.cpp
index a971866..4db461a 100644
--- a/default_device.cpp
+++ b/recovery_ui/default_device.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "device.h"
-#include "screen_ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/screen_ui.h"
Device* make_device() {
return new Device(new ScreenRecoveryUI);
diff --git a/device.cpp b/recovery_ui/device.cpp
similarity index 97%
rename from device.cpp
rename to recovery_ui/device.cpp
index eec1932..ddb0118 100644
--- a/device.cpp
+++ b/recovery_ui/device.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "device.h"
+#include "recovery_ui/device.h"
#include <algorithm>
#include <string>
@@ -23,7 +23,7 @@
#include <android-base/logging.h>
-#include "ui.h"
+#include "recovery_ui/ui.h"
static std::vector<std::pair<std::string, Device::BuiltinAction>> g_menu_actions{
{ "Reboot system now", Device::REBOOT },
diff --git a/device.h b/recovery_ui/include/recovery_ui/device.h
similarity index 95%
rename from device.h
rename to recovery_ui/include/recovery_ui/device.h
index 6a8daf8..3c44510 100644
--- a/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -68,7 +68,7 @@
// Called when recovery starts up (after the UI has been obtained and initialized and after the
// arguments have been parsed, but before anything else).
- virtual void StartRecovery() {};
+ virtual void StartRecovery() {}
// Called from the main thread when recovery is at the main menu and waiting for input, and a key
// is pressed. (Note that "at" the main menu does not necessarily mean the menu is visible;
@@ -93,8 +93,8 @@
// Performs a recovery action selected from the menu. 'menu_position' will be the index of the
// selected menu item, or a non-negative value returned from HandleMenuKey(). The menu will be
- // hidden when this is called; implementations can call ui_print() to print information to the
- // screen. If the menu position is one of the builtin actions, you can just return the
+ // hidden when this is called; implementations can call GetUI()->Print() to print information to
+ // the screen. If the menu position is one of the builtin actions, you can just return the
// corresponding enum value. If it is an action specific to your device, you actually perform it
// here and return NO_ACTION.
virtual BuiltinAction InvokeMenuItem(size_t menu_position);
diff --git a/screen_ui.h b/recovery_ui/include/recovery_ui/screen_ui.h
similarity index 99%
rename from screen_ui.h
rename to recovery_ui/include/recovery_ui/screen_ui.h
index acd44c8..5cda2a2 100644
--- a/screen_ui.h
+++ b/recovery_ui/include/recovery_ui/screen_ui.h
@@ -345,6 +345,8 @@
std::unique_ptr<GRSurface> wipe_data_confirmation_text_;
std::unique_ptr<GRSurface> wipe_data_menu_header_text_;
+ std::unique_ptr<GRSurface> fastbootd_logo_;
+
// current_icon_ points to one of the frames in intro_frames_ or loop_frames_, indexed by
// current_frame_, or error_icon_.
Icon current_icon_;
diff --git a/stub_ui.h b/recovery_ui/include/recovery_ui/stub_ui.h
similarity index 100%
rename from stub_ui.h
rename to recovery_ui/include/recovery_ui/stub_ui.h
diff --git a/ui.h b/recovery_ui/include/recovery_ui/ui.h
similarity index 96%
rename from ui.h
rename to recovery_ui/include/recovery_ui/ui.h
index 4924fec..d55322c 100644
--- a/ui.h
+++ b/recovery_ui/include/recovery_ui/ui.h
@@ -35,20 +35,20 @@
INSTALLING_UPDATE,
ERASING,
NO_COMMAND,
- ERROR
+ ERROR,
};
enum ProgressType {
EMPTY,
INDETERMINATE,
- DETERMINATE
+ DETERMINATE,
};
enum KeyAction {
ENQUEUE,
TOGGLE,
REBOOT,
- IGNORE
+ IGNORE,
};
enum class KeyError : int {
@@ -60,8 +60,8 @@
virtual ~RecoveryUI();
- // Initializes the object; called before anything else. UI texts will be initialized according to
- // the given locale. Returns true on success.
+ // Initializes the object; called before anything else. UI texts will be initialized according
+ // to the given locale. Returns true on success.
virtual bool Init(const std::string& locale);
virtual std::string GetLocale() const = 0;
@@ -168,7 +168,6 @@
virtual size_t ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
const std::vector<std::string>& backup_items,
const std::function<int(int, bool)>& key_handler) = 0;
-
// Displays the localized wipe data confirmation menu with pre-generated images. Falls back to
// the text strings upon failures. The initial selection is the 0th item, which returns to the
// upper level menu.
@@ -176,6 +175,11 @@
const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
const std::function<int(int, bool)>& key_handler) = 0;
+ // Set whether or not the fastbootd logo is displayed.
+ void SetEnableFastbootdLogo(bool enable) {
+ fastbootd_logo_enabled_ = enable;
+ }
+
// Resets the key interrupt status.
void ResetKeyInterruptStatus() {
key_interrupted_ = false;
@@ -200,12 +204,14 @@
// Whether we should listen for touch inputs (default: false).
bool touch_screen_allowed_;
+ bool fastbootd_logo_enabled_;
+
private:
enum class ScreensaverState {
DISABLED,
NORMAL,
DIMMED,
- OFF
+ OFF,
};
// The sensitivity when detecting a swipe.
diff --git a/vr_ui.h b/recovery_ui/include/recovery_ui/vr_ui.h
similarity index 100%
rename from vr_ui.h
rename to recovery_ui/include/recovery_ui/vr_ui.h
diff --git a/wear_ui.h b/recovery_ui/include/recovery_ui/wear_ui.h
similarity index 100%
rename from wear_ui.h
rename to recovery_ui/include/recovery_ui/wear_ui.h
diff --git a/screen_ui.cpp b/recovery_ui/screen_ui.cpp
similarity index 97%
rename from screen_ui.cpp
rename to recovery_ui/screen_ui.cpp
index 6c00a22..870db62 100644
--- a/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "screen_ui.h"
+#include "recovery_ui/screen_ui.h"
#include <dirent.h>
#include <errno.h>
@@ -42,10 +42,10 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include "device.h"
#include "minui/minui.h"
#include "otautil/paths.h"
-#include "ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
// Return the current time as a double (including fractions of a second).
static double now() {
@@ -388,10 +388,10 @@
enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX };
enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX };
static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = {
- { 32, 68, }, // PORTRAIT
- { 32, 68, }, // PORTRAIT_LARGE
- { 26, 56, }, // LANDSCAPE
- { 52, 112, }, // LANDSCAPE_LARGE
+ { 32, 68 }, // PORTRAIT
+ { 32, 68 }, // PORTRAIT_LARGE
+ { 26, 56 }, // LANDSCAPE
+ { 52, 112 }, // LANDSCAPE_LARGE
};
int ScreenRecoveryUI::GetAnimationBaseline() const {
@@ -701,6 +701,16 @@
void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(
const std::vector<std::string>& help_message) {
int y = margin_height_;
+
+ if (fastbootd_logo_ && fastbootd_logo_enabled_) {
+ // Try to get this centered on screen.
+ auto width = gr_get_width(fastbootd_logo_.get());
+ auto height = gr_get_height(fastbootd_logo_.get());
+ auto centered_x = ScreenWidth() / 2 - width / 2;
+ DrawSurface(fastbootd_logo_.get(), 0, 0, width, height, centered_x, y);
+ y += height;
+ }
+
if (menu_) {
int x = margin_width_ + kMenuIndent;
@@ -890,6 +900,10 @@
no_command_text_ = LoadLocalizedBitmap("no_command_text");
error_text_ = LoadLocalizedBitmap("error_text");
+ if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
+ fastbootd_logo_ = LoadBitmap("fastbootd");
+ }
+
// Background text for "installing_update" could be "installing update" or
// "installing security update". It will be set after Init() according to the commands in BCB.
installing_text_.reset();
@@ -1028,7 +1042,7 @@
va_end(ap);
}
-void ScreenRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) {
+void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
PrintV(fmt, false, ap);
diff --git a/ui.cpp b/recovery_ui/ui.cpp
similarity index 98%
rename from ui.cpp
rename to recovery_ui/ui.cpp
index 14b5b09..b7107ff 100644
--- a/ui.cpp
+++ b/recovery_ui/ui.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "ui.h"
+#include "recovery_ui/ui.h"
#include <errno.h>
#include <fcntl.h>
@@ -39,7 +39,6 @@
#include "minui/minui.h"
#include "otautil/sysutil.h"
-#include "roots.h"
using namespace std::chrono_literals;
@@ -59,6 +58,7 @@
brightness_file_(BRIGHTNESS_FILE),
max_brightness_file_(MAX_BRIGHTNESS_FILE),
touch_screen_allowed_(false),
+ fastbootd_logo_enabled_(false),
touch_low_threshold_(android::base::GetIntProperty("ro.recovery.ui.touch_low_threshold",
kDefaultTouchLowThreshold)),
touch_high_threshold_(android::base::GetIntProperty("ro.recovery.ui.touch_high_threshold",
@@ -589,8 +589,7 @@
return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
}
-void RecoveryUI::KeyLongPress(int) {
-}
+void RecoveryUI::KeyLongPress(int) {}
void RecoveryUI::SetEnableReboot(bool enabled) {
std::lock_guard<std::mutex> lg(key_queue_mutex);
diff --git a/vr_device.cpp b/recovery_ui/vr_device.cpp
similarity index 86%
rename from vr_device.cpp
rename to recovery_ui/vr_device.cpp
index 61e15cb..fd76133 100644
--- a/vr_device.cpp
+++ b/recovery_ui/vr_device.cpp
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-#include "device.h"
-#include "vr_ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/vr_ui.h"
Device* make_device() {
- return new Device(new VrRecoveryUI);
+ return new Device(new VrRecoveryUI);
}
-
diff --git a/vr_ui.cpp b/recovery_ui/vr_ui.cpp
similarity index 98%
rename from vr_ui.cpp
rename to recovery_ui/vr_ui.cpp
index 1f0292c..5b9b1b4 100644
--- a/vr_ui.cpp
+++ b/recovery_ui/vr_ui.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "vr_ui.h"
+#include "recovery_ui/vr_ui.h"
#include <android-base/properties.h>
diff --git a/wear_device.cpp b/recovery_ui/wear_device.cpp
similarity index 90%
rename from wear_device.cpp
rename to recovery_ui/wear_device.cpp
index 3268130..bf21bc9 100644
--- a/wear_device.cpp
+++ b/recovery_ui/wear_device.cpp
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-#include "device.h"
-#include "wear_ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/wear_ui.h"
Device* make_device() {
return new Device(new WearRecoveryUI);
}
-
diff --git a/wear_ui.cpp b/recovery_ui/wear_ui.cpp
similarity index 98%
rename from wear_ui.cpp
rename to recovery_ui/wear_ui.cpp
index 6da84c9..8d8108f 100644
--- a/wear_ui.cpp
+++ b/recovery_ui/wear_ui.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "wear_ui.h"
+#include "recovery_ui/wear_ui.h"
#include <string.h>
diff --git a/res-hdpi/images/fastbootd.png b/res-hdpi/images/fastbootd.png
new file mode 100644
index 0000000..5127b3b
--- /dev/null
+++ b/res-hdpi/images/fastbootd.png
Binary files differ
diff --git a/res-mdpi/images/fastbootd.png b/res-mdpi/images/fastbootd.png
new file mode 100644
index 0000000..c1b7bc2
--- /dev/null
+++ b/res-mdpi/images/fastbootd.png
Binary files differ
diff --git a/res-xhdpi/images/fastbootd.png b/res-xhdpi/images/fastbootd.png
new file mode 100644
index 0000000..e3da85a
--- /dev/null
+++ b/res-xhdpi/images/fastbootd.png
Binary files differ
diff --git a/res-xxhdpi/images/fastbootd.png b/res-xxhdpi/images/fastbootd.png
new file mode 100644
index 0000000..482dbb2
--- /dev/null
+++ b/res-xxhdpi/images/fastbootd.png
Binary files differ
diff --git a/res-xxxhdpi/images/fastbootd.png b/res-xxxhdpi/images/fastbootd.png
new file mode 100644
index 0000000..a878b1f
--- /dev/null
+++ b/res-xxxhdpi/images/fastbootd.png
Binary files differ
diff --git a/tests/Android.bp b/tests/Android.bp
index 898ed7d..09ef716 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -76,8 +76,9 @@
librecovery_static_libs = [
"librecovery",
"librecovery_fastboot",
+ "libinstall",
+ "librecovery_ui",
"libminui",
- "libverifier",
"libotautil",
"libhealthhalutils",
@@ -115,9 +116,9 @@
],
static_libs: libapplypatch_static_libs + [
+ "libinstall",
"librecovery_ui",
"libminui",
- "libverifier",
"libotautil",
"libupdater",
"libgtest_prod",
diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp
index 47a5471..3851329 100644
--- a/tests/component/install_test.cpp
+++ b/tests/component/install_test.cpp
@@ -32,9 +32,9 @@
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_writer.h>
-#include "install.h"
+#include "install/install.h"
#include "otautil/paths.h"
-#include "private/install.h"
+#include "private/setup_commands.h"
static void BuildZipArchive(const std::map<std::string, std::string>& file_map, int fd,
int compression_type) {
@@ -107,6 +107,32 @@
CloseArchive(zip);
}
+TEST(InstallTest, read_wipe_ab_partition_list) {
+ std::vector<std::string> partition_list = {
+ "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b",
+ "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b",
+ "/dev/block/bootdevice/by-name/userdata", "# Wipe the boot partitions last",
+ "/dev/block/bootdevice/by-name/boot_a", "/dev/block/bootdevice/by-name/boot_b",
+ };
+ TemporaryFile temp_file;
+ BuildZipArchive({ { "recovery.wipe", android::base::Join(partition_list, '\n') } },
+ temp_file.release(), kCompressDeflated);
+ std::string wipe_package;
+ ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &wipe_package));
+
+ auto package = Package::CreateMemoryPackage(
+ std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr);
+
+ auto read_partition_list = GetWipePartitionList(package.get());
+ std::vector<std::string> expected = {
+ "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b",
+ "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b",
+ "/dev/block/bootdevice/by-name/userdata", "/dev/block/bootdevice/by-name/boot_a",
+ "/dev/block/bootdevice/by-name/boot_b",
+ };
+ ASSERT_EQ(expected, read_partition_list);
+}
+
TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) {
TemporaryFile compatibility_zip_file;
std::string malformed_xml = "malformed";
diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp
index d5e074c..6add99f 100644
--- a/tests/component/sideload_test.cpp
+++ b/tests/component/sideload_test.cpp
@@ -16,6 +16,7 @@
#include <unistd.h>
+#include <memory>
#include <string>
#include <vector>
@@ -23,21 +24,34 @@
#include <android-base/strings.h>
#include <gtest/gtest.h>
+#include "fuse_provider.h"
#include "fuse_sideload.h"
TEST(SideloadTest, fuse_device) {
ASSERT_EQ(0, access("/dev/fuse", R_OK | W_OK));
}
+class FuseTestDataProvider : public FuseDataProvider {
+ public:
+ FuseTestDataProvider(uint64_t file_size, uint32_t block_size)
+ : FuseDataProvider(file_size, block_size) {}
+
+ private:
+ bool ReadBlockAlignedData(uint8_t*, uint32_t, uint32_t) const override {
+ return true;
+ }
+};
+
TEST(SideloadTest, run_fuse_sideload_wrong_parameters) {
- provider_vtab vtab;
- vtab.close = [](void) {};
+ auto provider_small_block = std::make_unique<FuseTestDataProvider>(4096, 4095);
+ ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_small_block)));
- ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, 4095));
- ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, (1 << 22) + 1));
+ auto provider_large_block = std::make_unique<FuseTestDataProvider>(4096, (1 << 22) + 1);
+ ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_large_block)));
- // Too many blocks.
- ASSERT_EQ(-1, run_fuse_sideload(vtab, ((1 << 18) + 1) * 4096, 4096));
+ auto provider_too_many_blocks =
+ std::make_unique<FuseTestDataProvider>(((1 << 18) + 1) * 4096, 4096);
+ ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_too_many_blocks)));
}
TEST(SideloadTest, run_fuse_sideload) {
@@ -50,18 +64,15 @@
const std::string content = android::base::Join(blocks, "");
ASSERT_EQ(16384U, content.size());
- provider_vtab vtab;
- vtab.close = [](void) {};
- vtab.read_block = [&blocks](uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
- if (block >= 4) return -1;
- blocks[block].copy(reinterpret_cast<char*>(buffer), fetch_size);
- return 0;
- };
+ TemporaryFile temp_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
+ auto provider = std::make_unique<FuseFileDataProvider>(temp_file.path, 4096);
+ ASSERT_TRUE(provider->Valid());
TemporaryDir mount_point;
pid_t pid = fork();
if (pid == 0) {
- ASSERT_EQ(0, run_fuse_sideload(vtab, 16384, 4096, mount_point.path));
+ ASSERT_EQ(0, run_fuse_sideload(std::move(provider), mount_point.path));
_exit(EXIT_SUCCESS);
}
diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp
index 0a59403..e27e58c 100644
--- a/tests/component/update_verifier_test.cpp
+++ b/tests/component/update_verifier_test.cpp
@@ -98,7 +98,7 @@
ASSERT_FALSE(verifier_.ParseCareMap());
}
-TEST_F(UpdateVerifierTest, verify_image_smoke) {
+TEST_F(UpdateVerifierTest, verify_image_text_format) {
// This test relies on dm-verity support.
if (!verity_supported) {
GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
@@ -107,52 +107,14 @@
std::string content = "system\n2,0,1";
ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_));
- ASSERT_TRUE(verifier_.ParseCareMap());
- ASSERT_TRUE(verifier_.VerifyPartitions());
-
- // Leading and trailing newlines should be accepted.
- ASSERT_TRUE(android::base::WriteStringToFile("\n" + content + "\n\n", care_map_txt_));
- ASSERT_TRUE(verifier_.ParseCareMap());
- ASSERT_TRUE(verifier_.VerifyPartitions());
+ // CareMap in text format is no longer supported.
+ ASSERT_FALSE(verifier_.ParseCareMap());
}
TEST_F(UpdateVerifierTest, verify_image_empty_care_map) {
ASSERT_FALSE(verifier_.ParseCareMap());
}
-TEST_F(UpdateVerifierTest, verify_image_wrong_lines) {
- // The care map file can have only 2 / 4 / 6 lines.
- ASSERT_TRUE(android::base::WriteStringToFile("line1", care_map_txt_));
- ASSERT_FALSE(verifier_.ParseCareMap());
-
- ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", care_map_txt_));
- ASSERT_FALSE(verifier_.ParseCareMap());
-}
-
-TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) {
- // This test relies on dm-verity support.
- if (!verity_supported) {
- GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
- return;
- }
-
- std::string content = "system\n2,1,0";
- ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_));
- ASSERT_FALSE(verifier_.ParseCareMap());
-}
-
-TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) {
- // This test relies on dm-verity support.
- if (!verity_supported) {
- GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
- return;
- }
-
- std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0";
- ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_));
- ASSERT_FALSE(verifier_.ParseCareMap());
-}
-
TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_smoke) {
// This test relies on dm-verity support.
if (!verity_supported) {
diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp
index 9fcaa0b..ded23c5 100644
--- a/tests/component/verifier_test.cpp
+++ b/tests/component/verifier_test.cpp
@@ -35,8 +35,9 @@
#include <ziparchive/zip_writer.h>
#include "common/test_constants.h"
+#include "install/package.h"
+#include "install/verifier.h"
#include "otautil/sysutil.h"
-#include "verifier.h"
using namespace std::string_literals;
@@ -47,15 +48,22 @@
std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), cert));
}
+static void VerifyFile(const std::string& content, const std::vector<Certificate>& keys,
+ int expected) {
+ auto package =
+ Package::CreateMemoryPackage(std::vector<uint8_t>(content.begin(), content.end()), nullptr);
+ ASSERT_NE(nullptr, package);
+
+ ASSERT_EQ(expected, verify_file(package.get(), keys));
+}
+
static void VerifyPackageWithCertificates(const std::string& name,
const std::vector<Certificate>& certs) {
- std::string package = from_testdata_base(name);
- MemMapping memmap;
- if (!memmap.MapFile(package)) {
- FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n";
- }
+ std::string path = from_testdata_base(name);
+ auto package = Package::CreateMemoryPackage(path, nullptr);
+ ASSERT_NE(nullptr, package);
- ASSERT_EQ(VERIFY_SUCCESS, verify_file(memmap.addr, memmap.length, certs));
+ ASSERT_EQ(VERIFY_SUCCESS, verify_file(package.get(), certs));
}
static void VerifyPackageWithSingleCertificate(const std::string& name, Certificate&& cert) {
@@ -150,6 +158,17 @@
VerifyPackageWithSingleCertificate("otasigned_v5.zip", std::move(cert));
}
+TEST(VerifierTest, LoadCertificateFromBuffer_sha256_rsa4096_bits) {
+ Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
+ LoadKeyFromFile(from_testdata_base("testkey_4096bits.x509.pem"), &cert);
+
+ ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len);
+ ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type);
+ ASSERT_EQ(nullptr, cert.ec);
+
+ VerifyPackageWithSingleCertificate("otasigned_4096bits.zip", std::move(cert));
+}
+
TEST(VerifierTest, LoadCertificateFromBuffer_check_rsa_keys) {
std::unique_ptr<RSA, RSADeleter> rsa(RSA_new());
std::unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free);
@@ -231,20 +250,22 @@
protected:
void SetUp() override {
std::vector<std::string> args = GetParam();
- std::string package = from_testdata_base(args[0]);
- if (!memmap.MapFile(package)) {
- FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n";
- }
+ std::string path = from_testdata_base(args[0]);
+ memory_package_ = Package::CreateMemoryPackage(path, nullptr);
+ ASSERT_NE(nullptr, memory_package_);
+ file_package_ = Package::CreateFilePackage(path, nullptr);
+ ASSERT_NE(nullptr, file_package_);
for (auto it = ++args.cbegin(); it != args.cend(); ++it) {
std::string public_key_file = from_testdata_base("testkey_" + *it + ".x509.pem");
- certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
- LoadKeyFromFile(public_key_file, &certs.back());
+ certs_.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
+ LoadKeyFromFile(public_key_file, &certs_.back());
}
}
- MemMapping memmap;
- std::vector<Certificate> certs;
+ std::unique_ptr<Package> memory_package_;
+ std::unique_ptr<Package> file_package_;
+ std::vector<Certificate> certs_;
};
class VerifierSuccessTest : public VerifierTest {
@@ -264,9 +285,7 @@
// Alter the footer.
package[package.size() - 5] = '\x05';
- ASSERT_EQ(VERIFY_FAILURE,
- verify_file(reinterpret_cast<const unsigned char*>(package.data()), package.size(),
- certs));
+ VerifyFile(package, certs, VERIFY_FAILURE);
}
TEST(VerifierTest, BadPackage_AlteredContent) {
@@ -281,15 +300,11 @@
// Alter the content.
std::string altered1(package);
altered1[50] += 1;
- ASSERT_EQ(VERIFY_FAILURE,
- verify_file(reinterpret_cast<const unsigned char*>(altered1.data()), altered1.size(),
- certs));
+ VerifyFile(altered1, certs, VERIFY_FAILURE);
std::string altered2(package);
altered2[10] += 1;
- ASSERT_EQ(VERIFY_FAILURE,
- verify_file(reinterpret_cast<const unsigned char*>(altered2.data()), altered2.size(),
- certs));
+ VerifyFile(altered2, certs, VERIFY_FAILURE);
}
TEST(VerifierTest, BadPackage_SignatureStartOutOfBounds) {
@@ -299,16 +314,17 @@
// Signature start is 65535 (0xffff) while comment size is 0 (Bug: 31914369).
std::string package = "\x50\x4b\x05\x06"s + std::string(12, '\0') + "\xff\xff\xff\xff\x00\x00"s;
- ASSERT_EQ(VERIFY_FAILURE, verify_file(reinterpret_cast<const unsigned char*>(package.data()),
- package.size(), certs));
+ VerifyFile(package, certs, VERIFY_FAILURE);
}
TEST_P(VerifierSuccessTest, VerifySucceed) {
- ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_SUCCESS);
+ ASSERT_EQ(VERIFY_SUCCESS, verify_file(memory_package_.get(), certs_));
+ ASSERT_EQ(VERIFY_SUCCESS, verify_file(file_package_.get(), certs_));
}
TEST_P(VerifierFailureTest, VerifyFailure) {
- ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_FAILURE);
+ ASSERT_EQ(VERIFY_FAILURE, verify_file(memory_package_.get(), certs_));
+ ASSERT_EQ(VERIFY_FAILURE, verify_file(file_package_.get(), certs_));
}
INSTANTIATE_TEST_CASE_P(SingleKeySuccess, VerifierSuccessTest,
diff --git a/tests/testdata/otasigned_4096bits.zip b/tests/testdata/otasigned_4096bits.zip
new file mode 100644
index 0000000..5016dfc
--- /dev/null
+++ b/tests/testdata/otasigned_4096bits.zip
Binary files differ
diff --git a/tests/testdata/testkey_4096bits.x509.pem b/tests/testdata/testkey_4096bits.x509.pem
new file mode 100644
index 0000000..cba30d6
--- /dev/null
+++ b/tests/testdata/testkey_4096bits.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGADCCA+igAwIBAgIJAJiRMVvanGUaMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD
+VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g
+VmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UE
+AwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+Fw0xODEwMzAxMjEzNTFaFw00NjAzMTcxMjEzNTFaMIGUMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
+A1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHQW5kcm9p
+ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCAiIwDQYJKoZI
+hvcNAQEBBQADggIPADCCAgoCggIBAL3ghKA8Gz9qOORY8gMY4wlB2tCJLDUO2tFG
+LVK1UphtQMp+YEcz/0VQKVV7de7z6V4EMQ5P1HbxHOsjcKn/zXAl4YgFt7b5kZbC
+bpNK4CYHEfho3j6fpYtq5d9q8rIA2kI0uZkkqPy1zXKTl2C2PjOoAnLQRk5xBVQG
+M10/wYsf7yX36mSWoJJwKPp/EzVFpA+hX8HpljeIiZ6CFzKwJdqv9zO/xzfp6NsX
+Tv5EGdkDxmw3qQqKgyl8dLMTZ/2zNfvVOMeZDusEPDF7A/lbU1byLWrKQdCzVb40
+yc7BCSRGYwM29R/byOcgD+lslwKSGzgzNmQXICt1tXz9bSJR8qh4tlAaiRc3ZKBe
+hJWIFGkGtD/cDGtDE5DbNAOz6CdSDdE2XN0Qf0cfN1RHVE6fo2FtFicRRVuFBt8M
+2cbQ7bzmEvtHD6W6dsf120FH7gppXKmnhMx1WazpxR2QltbiYDTy2ZZi4paS/jDB
+fL9gMCWp3Ohg2y74NGfUw5CQWQsDpcki6I7RvwClBCyOV51LHn5LE/nY4DkVrZxk
+Pw0/YrTWz5J5PbdMetTuIunE4ec4lm8nZnh1ET+2MHx2+RoyF5vBs4rp1KHHRaEA
+veD2AfQOWxz7kOG9+akFot7n+QoWEGdwY0mJ9jsO/IITCjv3VbD7o0OoJv1R2AW5
+sK2KQ4PDAgMBAAGjUzBRMB0GA1UdDgQWBBT2EbrayXGhY6VCvSlLtRNyjW9ceDAf
+BgNVHSMEGDAWgBT2EbrayXGhY6VCvSlLtRNyjW9ceDAPBgNVHRMBAf8EBTADAQH/
+MA0GCSqGSIb3DQEBCwUAA4ICAQC7SsWap9zDKmuR0qMUZ6wlualnag0hUG1jZHQP
+t63KO6LmNNMSuXRX60Zcq6WWzgLOyoT4HqHZZ47Jamfb4XQQcnWMMW0tJ3pDtTkz
+dZILBInHJO8QPYI8Du6XWsDLSvMajq6ueBtO3NdcgsNL7eiHf3WoOtajLZxFM94Z
+MESkUQOIsqHolYeTMHLTsuGkX1CK2Zw3Xn18bUSTYwZCHa6mYH00ItUBfetGCnWh
+Y7bth/R15Cc+hocSB7ZsOa/R5kDyDdFDIKrnV5nH5Yd7CryrYC6Ac5UarYrxSJTq
+eKPwqUlJB/tJW/lvdLt8YaURbFGzf/ZqU12zZRafYjmMjcQvfpzMoDSnbvHTA9IR
+ZGO7dwhwykoSaL4/8LWde49xQUq6F2pQBRmEr+7mTzml1MaM5cWEk5emkCMXgLog
+k+c56CAk1EdM1teWik7wR0TIqkkYyYJHTSg61GkXUIXrZJ6iYx2ejDg1+QTPm9rU
+Yr7nP52gVkQuUAX1+xB6wKLSDizQJw8SNiUGXl5+2vwV6+0BI3/CXlQ8I/nRPBC1
+oqOIkRSbE+IF7DP9QvYuNG/3bZZQ8LUVeHxqI5Mq8K2VIJZd95AIwPNMH34SaDGz
+9xjG28Fq4ZkuDP0pCsHM9d2XEwK5PEVS18WW5fJ/QcJKMno4IPTB70ZBBjVzv6Y+
+MYjOrw==
+-----END CERTIFICATE-----
diff --git a/tests/unit/asn1_decoder_test.cpp b/tests/unit/asn1_decoder_test.cpp
index b334a65..d94dd43 100644
--- a/tests/unit/asn1_decoder_test.cpp
+++ b/tests/unit/asn1_decoder_test.cpp
@@ -20,7 +20,7 @@
#include <gtest/gtest.h>
-#include "asn1_decoder.h"
+#include "private/asn1_decoder.h"
TEST(Asn1DecoderTest, Empty_Failure) {
uint8_t empty[] = {};
diff --git a/tests/unit/package_test.cpp b/tests/unit/package_test.cpp
new file mode 100644
index 0000000..a735a69
--- /dev/null
+++ b/tests/unit/package_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 <stdio.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <openssl/sha.h>
+#include <ziparchive/zip_writer.h>
+
+#include "common/test_constants.h"
+#include "install/package.h"
+
+class PackageTest : public ::testing::Test {
+ protected:
+ void SetUp() override;
+
+ // A list of package classes for test, including MemoryPackage and FilePackage.
+ std::vector<std::unique_ptr<Package>> packages_;
+
+ TemporaryFile temp_file_; // test package file.
+ std::string file_content_; // actual bytes of the package file.
+};
+
+void PackageTest::SetUp() {
+ std::vector<std::string> entries = { "file1.txt", "file2.txt", "dir1/file3.txt" };
+ FILE* file_ptr = fdopen(temp_file_.release(), "wb");
+ ZipWriter writer(file_ptr);
+ for (const auto& entry : entries) {
+ ASSERT_EQ(0, writer.StartEntry(entry.c_str(), ZipWriter::kCompress));
+ ASSERT_EQ(0, writer.WriteBytes(entry.c_str(), entry.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+ }
+ writer.Finish();
+ ASSERT_EQ(0, fclose(file_ptr));
+
+ ASSERT_TRUE(android::base::ReadFileToString(temp_file_.path, &file_content_));
+ auto memory_package = Package::CreateMemoryPackage(temp_file_.path, nullptr);
+ ASSERT_TRUE(memory_package);
+ packages_.emplace_back(std::move(memory_package));
+
+ auto file_package = Package::CreateFilePackage(temp_file_.path, nullptr);
+ ASSERT_TRUE(file_package);
+ packages_.emplace_back(std::move(file_package));
+}
+
+TEST_F(PackageTest, ReadFullyAtOffset_success) {
+ for (const auto& package : packages_) {
+ std::vector<uint8_t> buffer(file_content_.size());
+ ASSERT_TRUE(package->ReadFullyAtOffset(buffer.data(), file_content_.size(), 0));
+ ASSERT_EQ(file_content_, std::string(buffer.begin(), buffer.end()));
+
+ ASSERT_TRUE(package->ReadFullyAtOffset(buffer.data(), file_content_.size() - 10, 10));
+ ASSERT_EQ(file_content_.substr(10), std::string(buffer.begin(), buffer.end() - 10));
+ }
+}
+
+TEST_F(PackageTest, ReadFullyAtOffset_failure) {
+ for (const auto& package : packages_) {
+ std::vector<uint8_t> buffer(file_content_.size());
+ // Out of bound read.
+ ASSERT_FALSE(package->ReadFullyAtOffset(buffer.data(), file_content_.size(), 10));
+ }
+}
+
+TEST_F(PackageTest, UpdateHashAtOffset_sha1_hash) {
+ // Check that the hash matches for first half of the file.
+ uint64_t hash_size = file_content_.size() / 2;
+ std::vector<uint8_t> expected_sha(SHA_DIGEST_LENGTH);
+ SHA1(reinterpret_cast<uint8_t*>(file_content_.data()), hash_size, expected_sha.data());
+
+ for (const auto& package : packages_) {
+ SHA_CTX ctx;
+ SHA1_Init(&ctx);
+ std::vector<HasherUpdateCallback> hashers{ std::bind(&SHA1_Update, &ctx, std::placeholders::_1,
+ std::placeholders::_2) };
+ package->UpdateHashAtOffset(hashers, 0, hash_size);
+
+ std::vector<uint8_t> calculated_sha(SHA_DIGEST_LENGTH);
+ SHA1_Final(calculated_sha.data(), &ctx);
+ ASSERT_EQ(expected_sha, calculated_sha);
+ }
+}
+
+TEST_F(PackageTest, GetZipArchiveHandle_extract_entry) {
+ for (const auto& package : packages_) {
+ ZipArchiveHandle zip = package->GetZipArchiveHandle();
+ ASSERT_TRUE(zip);
+
+ // Check that we can extract one zip entry.
+ std::string entry_name = "dir1/file3.txt";
+ ZipString path(entry_name.c_str());
+ ZipEntry entry;
+ ASSERT_EQ(0, FindEntry(zip, path, &entry));
+
+ std::vector<uint8_t> extracted(entry_name.size());
+ ASSERT_EQ(0, ExtractToMemory(zip, &entry, extracted.data(), extracted.size()));
+ ASSERT_EQ(entry_name, std::string(extracted.begin(), extracted.end()));
+ }
+}
diff --git a/tests/unit/screen_ui_test.cpp b/tests/unit/screen_ui_test.cpp
index 647c7b2..883dfbd 100644
--- a/tests/unit/screen_ui_test.cpp
+++ b/tests/unit/screen_ui_test.cpp
@@ -30,11 +30,11 @@
#include <gtest/gtest_prod.h>
#include "common/test_constants.h"
-#include "device.h"
#include "minui/minui.h"
#include "otautil/paths.h"
#include "private/resources.h"
-#include "screen_ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/screen_ui.h"
static const std::vector<std::string> HEADERS{ "header" };
static const std::vector<std::string> ITEMS{ "item1", "item2", "item3", "item4", "1234567890" };
diff --git a/tests/unit/sysutil_test.cpp b/tests/unit/sysutil_test.cpp
index 77625db..3466e8e 100644
--- a/tests/unit/sysutil_test.cpp
+++ b/tests/unit/sysutil_test.cpp
@@ -17,8 +17,10 @@
#include <string>
#include <android-base/file.h>
+#include <android-base/strings.h>
#include <gtest/gtest.h>
+#include "otautil/rangeset.h"
#include "otautil/sysutil.h"
TEST(SysUtilTest, InvalidArgs) {
@@ -28,6 +30,65 @@
ASSERT_FALSE(mapping.MapFile(""));
}
+TEST(SysUtilTest, ParseBlockMapFile_smoke) {
+ std::vector<std::string> content = {
+ "/dev/abc", "49652 4096", "3", "1000 1008", "2100 2102", "30 33",
+ };
+
+ TemporaryFile temp_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(content, '\n'), temp_file.path));
+
+ auto block_map_data = BlockMapData::ParseBlockMapFile(temp_file.path);
+ ASSERT_EQ("/dev/abc", block_map_data.path());
+ ASSERT_EQ(49652, block_map_data.file_size());
+ ASSERT_EQ(4096, block_map_data.block_size());
+ ASSERT_EQ(RangeSet(std::vector<Range>{
+ { 1000, 1008 },
+ { 2100, 2102 },
+ { 30, 33 },
+ }),
+ block_map_data.block_ranges());
+}
+
+TEST(SysUtilTest, ParseBlockMapFile_invalid_line_count) {
+ std::vector<std::string> content = {
+ "/dev/abc", "49652 4096", "2", "1000 1008", "2100 2102", "30 33",
+ };
+
+ TemporaryFile temp_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(content, '\n'), temp_file.path));
+
+ auto block_map_data1 = BlockMapData::ParseBlockMapFile(temp_file.path);
+ ASSERT_FALSE(block_map_data1);
+}
+
+TEST(SysUtilTest, ParseBlockMapFile_invalid_size) {
+ std::vector<std::string> content = {
+ "/dev/abc",
+ "42949672950 4294967295",
+ "1",
+ "0 9",
+ };
+
+ TemporaryFile temp_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(content, '\n'), temp_file.path));
+
+ auto block_map_data = BlockMapData::ParseBlockMapFile(temp_file.path);
+ ASSERT_EQ("/dev/abc", block_map_data.path());
+ ASSERT_EQ(42949672950, block_map_data.file_size());
+ ASSERT_EQ(4294967295, block_map_data.block_size());
+
+ content[1] = "42949672950 4294967296";
+ ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(content, '\n'), temp_file.path));
+ auto large_block_size = BlockMapData::ParseBlockMapFile(temp_file.path);
+ ASSERT_FALSE(large_block_size);
+
+ content[1] = "4294967296 1";
+ ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(content, '\n'), temp_file.path));
+ auto too_many_blocks = BlockMapData::ParseBlockMapFile(temp_file.path);
+ ASSERT_FALSE(too_many_blocks);
+}
+
TEST(SysUtilTest, MapFileRegularFile) {
TemporaryFile temp_file1;
std::string content = "abc";
diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml
index e51b36d..e6f2ffd 100644
--- a/tools/recovery_l10n/res/values-gl/strings.xml
+++ b/tools/recovery_l10n/res/values-gl/strings.xml
@@ -6,9 +6,9 @@
<string name="recovery_no_command" msgid="4465476568623024327">"Non hai ningún comando"</string>
<string name="recovery_error" msgid="5748178989622716736">"Erro"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"Instalando actualización de seguranza"</string>
- <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos de usuario almacenados neste dispositivo."</string>
+ <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos do usuario almacenados neste dispositivo."</string>
<string name="recovery_try_again" msgid="7168248750158873496">"Tentar de novo"</string>
<string name="recovery_factory_data_reset" msgid="7321351565602894783">"Restablecemento dos datos de fábrica"</string>
- <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Queres borrar todos os datos de usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER."</string>
+ <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Queres borrar todos os datos do usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER."</string>
<string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Cancelar"</string>
</resources>
diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml
index 3d66372..2d6c0ab 100644
--- a/tools/recovery_l10n/res/values-ja/strings.xml
+++ b/tools/recovery_l10n/res/values-ja/strings.xml
@@ -6,7 +6,7 @@
<string name="recovery_no_command" msgid="4465476568623024327">"コマンドが指定されていません"</string>
<string name="recovery_error" msgid="5748178989622716736">"エラーが発生しました。"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"セキュリティ アップデートをインストールしています"</string>
- <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、この端末に保存されているすべてのユーザー データを消去することが必要な場合があります。"</string>
+ <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、このデバイスに保存されているすべてのユーザー データを消去することが必要な場合があります。"</string>
<string name="recovery_try_again" msgid="7168248750158873496">"再試行"</string>
<string name="recovery_factory_data_reset" msgid="7321351565602894783">"データの初期化"</string>
<string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"すべてのユーザー データをワイプしますか?\n\nこの操作は元に戻せません。"</string>
diff --git a/update_verifier/include/update_verifier/update_verifier.h b/update_verifier/include/update_verifier/update_verifier.h
index b00890e..4c64b1e 100644
--- a/update_verifier/include/update_verifier/update_verifier.h
+++ b/update_verifier/include/update_verifier/update_verifier.h
@@ -50,9 +50,6 @@
private:
friend class UpdateVerifierTest;
- // Parses the legacy care_map.txt in plain text format.
- bool ParseCareMapPlainText(const std::string& content);
-
// Finds all the dm-enabled partitions, and returns a map of <partition_name, block_device>.
std::map<std::string, std::string> FindDmPartitions();
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index 5e5eac7..d04c455 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -38,17 +38,19 @@
*/
#include "update_verifier/update_verifier.h"
-#include <android/os/IVold.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <algorithm>
#include <future>
+#include <thread>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -57,6 +59,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/hardware/boot/1.0/IBootControl.h>
+#include <android/os/IVold.h>
#include <binder/BinderService.h>
#include <binder/Status.h>
#include <cutils/android_reboot.h>
@@ -68,6 +71,7 @@
using android::hardware::boot::V1_0::BoolResult;
using android::hardware::boot::V1_0::CommandResult;
+// TODO(xunchang) remove the prefix and use a default path instead.
constexpr const char* kDefaultCareMapPrefix = "/data/ota_package/care_map";
// Find directories in format of "/sys/block/dm-X".
@@ -111,6 +115,15 @@
// AVB is using 'vroot' for the root block device but we're expecting 'system'.
if (dm_block_name == "vroot") {
dm_block_name = "system";
+ } else if (android::base::EndsWith(dm_block_name, "-verity")) {
+ auto npos = dm_block_name.rfind("-verity");
+ dm_block_name = dm_block_name.substr(0, npos);
+ } else if (!android::base::GetProperty("ro.boot.avb_version", "").empty()) {
+ // Verified Boot 1.0 doesn't add a -verity suffix. On AVB 2 devices,
+ // if DAP is enabled, then a -verity suffix must be used to
+ // differentiate between dm-linear and dm-verity devices. If we get
+ // here, we're AVB 2 and looking at a non-verity partition.
+ continue;
}
dm_block_devices.emplace(dm_block_name, DEV_PATH + std::string(namelist[n]->d_name));
@@ -196,51 +209,13 @@
return true;
}
-bool UpdateVerifier::ParseCareMapPlainText(const std::string& content) {
- // care_map file has up to six lines, where every two lines make a pair. Within each pair, the
- // first line has the partition name (e.g. "system"), while the second line holds the ranges of
- // all the blocks to verify.
- auto lines = android::base::Split(android::base::Trim(content), "\n");
- if (lines.size() != 2 && lines.size() != 4 && lines.size() != 6) {
- LOG(WARNING) << "Invalid lines in care_map: found " << lines.size()
- << " lines, expecting 2 or 4 or 6 lines.";
- return false;
- }
-
- for (size_t i = 0; i < lines.size(); i += 2) {
- const std::string& partition_name = lines[i];
- const std::string& range_str = lines[i + 1];
- // We're seeing an N care_map.txt. Skip the verification since it's not compatible with O
- // update_verifier (the last few metadata blocks can't be read via device mapper).
- if (android::base::StartsWith(partition_name, "/dev/block/")) {
- LOG(WARNING) << "Found legacy care_map.txt; skipped.";
- return false;
- }
-
- // For block range string, first integer 'count' equals 2 * total number of valid ranges,
- // followed by 'count' number comma separated integers. Every two integers reprensent a
- // block range with the first number included in range but second number not included.
- // For example '4,64536,65343,74149,74150' represents: [64536,65343) and [74149,74150).
- RangeSet ranges = RangeSet::Parse(range_str);
- if (!ranges) {
- LOG(WARNING) << "Error parsing RangeSet string " << range_str;
- return false;
- }
-
- partition_map_.emplace(partition_name, ranges);
- }
-
- return true;
-}
-
bool UpdateVerifier::ParseCareMap() {
partition_map_.clear();
std::string care_map_name = care_map_prefix_ + ".pb";
if (access(care_map_name.c_str(), R_OK) == -1) {
- LOG(WARNING) << care_map_name
- << " doesn't exist, falling back to read the care_map in plain text format.";
- care_map_name = care_map_prefix_ + ".txt";
+ LOG(ERROR) << care_map_name << " doesn't exist";
+ return false;
}
android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY)));
@@ -263,10 +238,6 @@
return false;
}
- if (android::base::EndsWith(care_map_name, ".txt")) {
- return ParseCareMapPlainText(file_content);
- }
-
recovery_update_verifier::CareMap care_map;
if (!care_map.ParseFromString(file_content)) {
LOG(WARNING) << "Failed to parse " << care_map_name << " in protobuf format.";
diff --git a/updater_sample/README.md b/updater_sample/README.md
index bc66a9b..2070ebc 100644
--- a/updater_sample/README.md
+++ b/updater_sample/README.md
@@ -191,7 +191,7 @@
</privapp-permissions>
```
to `frameworks/base/data/etc/privapp-permissions-platform.xml`
-5. Build sample app `mmma -j bootable/recovery/updater_sample`.
+5. Build sample app `make -j SystemUpdaterSample`.
6. Build Android `make -j`
7. [Flash the device](https://source.android.com/setup/build/running)
8. Add update config files; look above at `## Update Config file`;
@@ -225,7 +225,9 @@
## Running tests
-1. Build `mmma bootable/recovery/updater_sample/`
+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`
3. Install tests
diff --git a/verifier.h b/verifier.h
deleted file mode 100644
index df9a4b6..0000000
--- a/verifier.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-#ifndef _RECOVERY_VERIFIER_H
-#define _RECOVERY_VERIFIER_H
-
-#include <stdint.h>
-
-#include <functional>
-#include <memory>
-#include <vector>
-
-#include <openssl/ec_key.h>
-#include <openssl/rsa.h>
-#include <openssl/sha.h>
-
-struct RSADeleter {
- void operator()(RSA* rsa) const {
- RSA_free(rsa);
- }
-};
-
-struct ECKEYDeleter {
- void operator()(EC_KEY* ec_key) const {
- EC_KEY_free(ec_key);
- }
-};
-
-struct Certificate {
- typedef enum {
- KEY_TYPE_RSA,
- KEY_TYPE_EC,
- } KeyType;
-
- Certificate(int hash_len_,
- KeyType key_type_,
- std::unique_ptr<RSA, RSADeleter>&& rsa_,
- std::unique_ptr<EC_KEY, ECKEYDeleter>&& ec_)
- : hash_len(hash_len_),
- key_type(key_type_),
- rsa(std::move(rsa_)),
- ec(std::move(ec_)) {}
-
- // SHA_DIGEST_LENGTH (SHA-1) or SHA256_DIGEST_LENGTH (SHA-256)
- int hash_len;
- KeyType key_type;
- std::unique_ptr<RSA, RSADeleter> rsa;
- std::unique_ptr<EC_KEY, ECKEYDeleter> ec;
-};
-
-/*
- * 'addr' and 'length' define an update package file that has been loaded (or mmap'ed, or
- * whatever) into memory. Verifies that the file is signed and the signature matches one of the
- * given keys. It optionally accepts a callback function for posting the progress to. Returns one
- * of the constants of VERIFY_SUCCESS and VERIFY_FAILURE.
- */
-int verify_file(const unsigned char* addr, size_t length, const std::vector<Certificate>& keys,
- const std::function<void(float)>& set_progress = nullptr);
-
-// Checks that the RSA key has a modulus of 2048 bits long, and public exponent is 3 or 65537.
-bool CheckRSAKey(const std::unique_ptr<RSA, RSADeleter>& rsa);
-
-// Checks that the field size of the curve for the EC key is 256 bits.
-bool CheckECKey(const std::unique_ptr<EC_KEY, ECKEYDeleter>& ec_key);
-
-// Parses a PEM-encoded x509 certificate from the given buffer and saves it into |cert|. Returns
-// false if there is a parsing failure or the signature's encryption algorithm is not supported.
-bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert);
-
-// Iterates over the zip entries with the suffix "x509.pem" and returns a list of recognized
-// certificates. Returns an empty list if we fail to parse any of the entries.
-std::vector<Certificate> LoadKeysFromZipfile(const std::string& zip_name);
-
-#define VERIFY_SUCCESS 0
-#define VERIFY_FAILURE 1
-
-#endif /* _RECOVERY_VERIFIER_H */