From cecad743c1225bc203085e04cc70b54bcd51c374 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 3 Apr 2019 11:35:51 -0700 Subject: [PATCH 001/171] libotautil exports libfstab header. otautil/roots.h includes , but users of otautil/roots.h don't need to explicitly depend on libfstab unless they have a real need. Also remove the unneeded include of from fsck_unshare_blocks.cpp. Test: mmma -j bootable/recovery Change-Id: Id3dc995a4769e631ab242843ee439bd94b2bf0bc --- Android.bp | 3 --- fsck_unshare_blocks.cpp | 1 - install/Android.bp | 1 - otautil/Android.bp | 4 ++++ 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Android.bp b/Android.bp index a44a2c62..fc8b139a 100644 --- a/Android.bp +++ b/Android.bp @@ -75,7 +75,6 @@ cc_defaults { // external dependencies "libhealthhalutils", - "libfstab", ], } @@ -153,7 +152,6 @@ cc_binary { static_libs: [ "libotautil", - "libfstab", ], init_rc: [ @@ -181,7 +179,6 @@ cc_binary { static_libs: [ "libotautil", - "libfstab", ], init_rc: [ diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp index e74f8ba6..0f8fffac 100644 --- a/fsck_unshare_blocks.cpp +++ b/fsck_unshare_blocks.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include "otautil/roots.h" diff --git a/install/Android.bp b/install/Android.bp index aa47990a..095a6d02 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -43,7 +43,6 @@ cc_defaults { // external dependencies "libvintf_recovery", "libvintf", - "libfstab", ], } diff --git a/otautil/Android.bp b/otautil/Android.bp index b4936c08..c9ecba78 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -61,6 +61,10 @@ cc_library_static { "libfs_mgr", "libselinux", ], + + export_static_lib_headers: [ + "libfstab", + ], }, }, } From 5954a237cb085f710c02c8ba938d1eb6d562aed1 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 8 Apr 2019 09:53:18 -0700 Subject: [PATCH 002/171] Import translations. DO NOT MERGE Change-Id: I6aff7f01df0c28775a2dbef26ee3476cb5d83060 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-ja/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml index 3d663727..2d6c0abc 100644 --- a/tools/recovery_l10n/res/values-ja/strings.xml +++ b/tools/recovery_l10n/res/values-ja/strings.xml @@ -6,7 +6,7 @@ "コマンドが指定されていません" "エラーが発生しました。" "セキュリティ アップデートをインストールしています" - "Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、この端末に保存されているすべてのユーザー データを消去することが必要な場合があります。" + "Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、このデバイスに保存されているすべてのユーザー データを消去することが必要な場合があります。" "再試行" "データの初期化" "すべてのユーザー データをワイプしますか?\n\nこの操作は元に戻せません。" From 0deed3389bac84e074afa7f748a7bf77917f104e Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 8 Apr 2019 11:26:11 -0700 Subject: [PATCH 003/171] Build libinstall as a static library. It was once considered to be shared between recovery and minadbd, so that the latter can start an install on its own. The plan has been changed, since package install -- including device wipe operations -- could be device-specific, which should be done by recovery only. This CL moves libinstall back to a static library, which also saves the overall size (reducing from 140256 + 660576 to 555880 bytes on aosp_taimen-userdebug). Bug: 130166585 Test: Run recovery_component_test. Test: `adb sideload` on taimen. Change-Id: Ib1f5f79f235df4682c0bd104425c9c122f6091ba --- Android.bp | 3 +-- CleanSpec.mk | 10 +++++++--- install/Android.bp | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Android.bp b/Android.bp index fc8b139a..137004c0 100644 --- a/Android.bp +++ b/Android.bp @@ -69,6 +69,7 @@ cc_defaults { ], static_libs: [ + "libinstall", "librecovery_fastboot", "libminui", "libotautil", @@ -92,7 +93,6 @@ cc_library_static { ], shared_libs: [ - "libinstall", "librecovery_ui", ], } @@ -112,7 +112,6 @@ cc_binary { ], shared_libs: [ - "libinstall", "libminadbd_services", "librecovery_ui", ], diff --git a/CleanSpec.mk b/CleanSpec.mk index fec823e7..a7ab0d9b 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/install/Android.bp b/install/Android.bp index 095a6d02..85cf9ac4 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -46,7 +46,7 @@ cc_defaults { ], } -cc_library { +cc_library_static { name: "libinstall", recovery_available: true, From 34690ced91e22f5d9b5dd19c33b11c8e0b4bafa0 Mon Sep 17 00:00:00 2001 From: xunchang Date: Fri, 5 Apr 2019 16:16:07 -0700 Subject: [PATCH 004/171] Add socket communication between recovery and minadbd This cl adds a socket pair to support the communication between recovery and minadbd. Therefore, minadbd will be able to issue multiple commands to recovery and get back the status of each command. This cl also switches the adb sideload from the recovery menu to use this protocol; and moves minadbd to a separate binary. Bug: 130166585 Test: sideload a package Change-Id: I80d36d5c4e6fe1ae3ea23640907bc50c0dc0d482 --- Android.bp | 2 +- install/Android.bp | 4 + install/adb_install.cpp | 309 +++++++++++++++++----- minadbd/Android.bp | 31 ++- minadbd/minadbd.cpp | 48 +++- minadbd/minadbd_services.cpp | 73 ++++- minadbd/{minadbd.h => minadbd_services.h} | 7 +- minadbd/minadbd_types.h | 53 ++++ recovery_main.cpp | 11 - 9 files changed, 441 insertions(+), 97 deletions(-) rename minadbd/{minadbd.h => minadbd_services.h} (90%) create mode 100644 minadbd/minadbd_types.h diff --git a/Android.bp b/Android.bp index 137004c0..582f3b62 100644 --- a/Android.bp +++ b/Android.bp @@ -112,7 +112,6 @@ cc_binary { ], shared_libs: [ - "libminadbd_services", "librecovery_ui", ], @@ -124,6 +123,7 @@ cc_binary { required: [ "e2fsdroid.recovery", "librecovery_ui_ext", + "minadbd", "mke2fs.conf.recovery", "mke2fs.recovery", "recovery_deps", diff --git a/install/Android.bp b/install/Android.bp index 85cf9ac4..2ffc4421 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -19,6 +19,10 @@ cc_defaults { "recovery_defaults", ], + header_libs: [ + "libminadbd_headers", + ], + shared_libs: [ "libbase", "libbootloader_message", diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 5296ff60..dc7ee0b3 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -21,23 +21,265 @@ #include #include #include +#include +#include #include #include #include #include +#include +#include +#include + +#include #include +#include #include +#include +#include #include "fuse_sideload.h" #include "install/install.h" +#include "minadbd_types.h" #include "recovery_ui/ui.h" +using CommandFunction = std::function; + 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(cmd_code_string.c_str()); + if (cmd_code >= static_cast(MinadbdCommands::kError)) { + LOG(ERROR) << "Unsupported command code: " << cmd_code; + return MinadbdCommands::kError; + } + + return static_cast(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(bool* wipe_cache, 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, wipe_cache, 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& 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( + 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(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& 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& 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(bool* wipe_cache, RecoveryUI* ui) { // Save the usb state to restore after the sideload operation. std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); @@ -51,66 +293,13 @@ int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { "\n\nNow send the package you want to apply\n" "to the device with \"adb sideload \"...\n"); - pid_t child; - if ((child = fork()) == 0) { - execl("/system/bin/recovery", "recovery", "--adbd", nullptr); - _exit(EXIT_FAILURE); - } + int install_result = INSTALL_ERROR; + std::map command_map{ + { MinadbdCommands::kInstall, + std::bind(&AdbInstallPackageHandler, wipe_cache, ui, &install_result) }, + }; - 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, ui); - 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)); - } - } + 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. @@ -124,5 +313,5 @@ int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { } } - return result; + return install_result; } diff --git a/minadbd/Android.bp b/minadbd/Android.bp index 9b889f4a..e4f7712e 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -40,7 +40,6 @@ cc_library { srcs: [ "fuse_adb_provider.cpp", - "minadbd.cpp", "minadbd_services.cpp", ], @@ -52,6 +51,36 @@ cc_library { ], } +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, diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp index 349189cc..57158ad5 100644 --- a/minadbd/minadbd.cpp +++ b/minadbd/minadbd.cpp @@ -14,30 +14,54 @@ * limitations under the License. */ -#include "minadbd.h" - #include +#include #include #include #include +#include + +#include +#include #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 6fe5c79b..79e6fc4e 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "minadbd_services.h" + #include #include #include @@ -27,38 +29,95 @@ #include #include +#include +#include +#include +#include +#include + #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( + 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; + + if (!WriteCommandToFd(MinadbdCommands::kInstall, minadbd_socket)) { + exit(kMinadbdSocketIOError); + } auto adb_data_reader = std::make_unique(std::move(sfd), file_size, block_size); - int result = run_fuse_sideload(std::move(adb_data_reader)); + if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) { + LOG(ERROR) << "Failed to start fuse"; + exit(kMinadbdFuseStartError); + } - printf("sideload_host finished\n"); - exit(result == 0 ? 0 : 1); + MinadbdCommandStatus status; + if (!WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } + LOG(INFO) << "Got command status: " << static_cast(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 90% rename from minadbd/minadbd.h rename to minadbd/minadbd_services.h index 3570a5da..6835bd77 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 00000000..7bd69096 --- /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 + +// 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/recovery_main.cpp b/recovery_main.cpp index b41368d7..8b4ad5a3 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -51,7 +51,6 @@ #include "common.h" #include "fastboot/fastboot.h" #include "logging.h" -#include "minadbd/minadbd.h" #include "otautil/paths.h" #include "otautil/roots.h" #include "otautil/sysutil.h" @@ -322,16 +321,6 @@ int main(int argc, char** argv) { // 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 From 79c5fb32e8b6eb74dfa64a4f9650ce4fb4034a9c Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 11 Apr 2019 16:52:41 -0700 Subject: [PATCH 005/171] Import translations. DO NOT MERGE Change-Id: I87585a82fccf625bc05c5805fc90459d572cee10 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-gl/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml index e51b36df..e6f2ffd8 100644 --- a/tools/recovery_l10n/res/values-gl/strings.xml +++ b/tools/recovery_l10n/res/values-gl/strings.xml @@ -6,9 +6,9 @@ "Non hai ningún comando" "Erro" "Instalando actualización de seguranza" - "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." + "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." "Tentar de novo" "Restablecemento dos datos de fábrica" - "Queres borrar todos os datos de usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER." + "Queres borrar todos os datos do usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER." "Cancelar" From 29077dc25a33a89a9adc462922cfac97e58edb38 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 13 Apr 2019 01:52:59 -0700 Subject: [PATCH 006/171] Import translations. DO NOT MERGE Change-Id: Ic746bf49180622c3de6029067a811b91007ed507 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-gl/strings.xml | 4 ++-- tools/recovery_l10n/res/values-ja/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml index e51b36df..e6f2ffd8 100644 --- a/tools/recovery_l10n/res/values-gl/strings.xml +++ b/tools/recovery_l10n/res/values-gl/strings.xml @@ -6,9 +6,9 @@ "Non hai ningún comando" "Erro" "Instalando actualización de seguranza" - "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." + "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." "Tentar de novo" "Restablecemento dos datos de fábrica" - "Queres borrar todos os datos de usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER." + "Queres borrar todos os datos do usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER." "Cancelar" diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml index 3d663727..2d6c0abc 100644 --- a/tools/recovery_l10n/res/values-ja/strings.xml +++ b/tools/recovery_l10n/res/values-ja/strings.xml @@ -6,7 +6,7 @@ "コマンドが指定されていません" "エラーが発生しました。" "セキュリティ アップデートをインストールしています" - "Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、この端末に保存されているすべてのユーザー データを消去することが必要な場合があります。" + "Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、このデバイスに保存されているすべてのユーザー データを消去することが必要な場合があります。" "再試行" "データの初期化" "すべてのユーザー データをワイプしますか?\n\nこの操作は元に戻せません。" From 316e9717461890dd319dc370970069fe4532a561 Mon Sep 17 00:00:00 2001 From: xunchang Date: Fri, 12 Apr 2019 16:22:15 -0700 Subject: [PATCH 007/171] Move wipe cache|data to libinstall Therefore, libinstall becomes the sole owner to handle the request from minadbd service. The change also includes 1. move logging.cpp out of librecovery 2. drop the dependency on common.h 3. now it's more sensible to move the wipe_cache as part of install_package. move the wipe_cache to the end of the function. Bug: 130166585 Test: wipe data and cache from menu Change-Id: I6f356dccdb38015c50acf756bac246f87c30fc1f --- Android.bp | 3 - install/Android.bp | 1 + install/adb_install.cpp | 9 +- install/fuse_sdcard_install.cpp | 4 +- install/include/install/adb_install.h | 2 +- install/include/install/fuse_sdcard_install.h | 2 +- install/include/install/install.h | 9 +- install/include/install/wipe_data.h | 32 +++ install/install.cpp | 18 +- install/wipe_data.cpp | 190 +++++++++++++++ otautil/Android.bp | 1 + .../include/otautil/logging.h | 5 +- logging.cpp => otautil/logging.cpp | 25 +- recovery-persist.cpp | 2 +- recovery-refresh.cpp | 2 +- recovery.cpp | 217 +++--------------- recovery_main.cpp | 5 +- 17 files changed, 303 insertions(+), 224 deletions(-) create mode 100644 install/include/install/wipe_data.h create mode 100644 install/wipe_data.cpp rename logging.h => otautil/include/otautil/logging.h (91%) rename logging.cpp => otautil/logging.cpp (92%) diff --git a/Android.bp b/Android.bp index 582f3b62..0eb5fd9e 100644 --- a/Android.bp +++ b/Android.bp @@ -107,7 +107,6 @@ cc_binary { ], srcs: [ - "logging.cpp", "recovery_main.cpp", ], @@ -139,7 +138,6 @@ cc_binary { ], srcs: [ - "logging.cpp", "recovery-persist.cpp", ], @@ -167,7 +165,6 @@ cc_binary { ], srcs: [ - "logging.cpp", "recovery-refresh.cpp", ], diff --git a/install/Android.bp b/install/Android.bp index 2ffc4421..b18e2902 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -65,6 +65,7 @@ cc_library_static { "install.cpp", "package.cpp", "verifier.cpp", + "wipe_data.cpp", ], shared_libs: [ diff --git a/install/adb_install.cpp b/install/adb_install.cpp index dc7ee0b3..548b6e5b 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -81,7 +81,7 @@ static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { } // Installs the package from FUSE. Returns true if the installation succeeds, and false otherwise. -static bool AdbInstallPackageHandler(bool* wipe_cache, RecoveryUI* ui, int* result) { +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 @@ -99,7 +99,7 @@ static bool AdbInstallPackageHandler(bool* wipe_cache, RecoveryUI* ui, int* resu break; } } - *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0, ui); + *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui); break; } @@ -280,7 +280,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { +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. @@ -295,8 +295,7 @@ int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { int install_result = INSTALL_ERROR; std::map command_map{ - { MinadbdCommands::kInstall, - std::bind(&AdbInstallPackageHandler, wipe_cache, ui, &install_result) }, + { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, }; CreateMinadbdServiceAndExecuteCommands(command_map); diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_sdcard_install.cpp index dde289f8..1aa8768e 100644 --- a/install/fuse_sdcard_install.cpp +++ b/install/fuse_sdcard_install.cpp @@ -133,7 +133,7 @@ static bool StartSdcardFuse(const std::string& path) { return run_fuse_sideload(std::move(file_data_reader)) == 0; } -int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui) { +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; @@ -184,7 +184,7 @@ int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui) { } } - result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0 /*retry_count*/, ui); + result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui); break; } diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index dbc82450..f7b065b6 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -18,4 +18,4 @@ #include -int apply_from_adb(bool* wipe_cache, RecoveryUI* ui); +int apply_from_adb(RecoveryUI* ui); diff --git a/install/include/install/fuse_sdcard_install.h b/install/include/install/fuse_sdcard_install.h index 345aea45..d9214ca3 100644 --- a/install/include/install/fuse_sdcard_install.h +++ b/install/include/install/fuse_sdcard_install.h @@ -19,4 +19,4 @@ #include "recovery_ui/device.h" #include "recovery_ui/ui.h" -int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui); +int ApplyFromSdcard(Device* device, RecoveryUI* ui); diff --git a/install/include/install/install.h b/install/include/install/install.h index 74fb3d17..1e41b484 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -43,10 +43,11 @@ enum class OtaType { 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, - RecoveryUI* ui); +// 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); // Verifies the package by ota keys. Returns true if the package is verified successfully, // otherwise returns false. diff --git a/install/include/install/wipe_data.h b/install/include/install/wipe_data.h new file mode 100644 index 00000000..06b1b95d --- /dev/null +++ b/install/include/install/wipe_data.h @@ -0,0 +1,32 @@ +/* + * 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 + +#include "recovery_ui/device.h" +#include "recovery_ui/ui.h" + +struct selabel_handle; + +void SetWipeDataSehandle(selabel_handle* handle); + +// Returns true on success. +bool WipeCache(RecoveryUI* ui, const std::function& confirm); + +// Returns true on success. +bool WipeData(Device* device, bool convert_fbe); diff --git a/install/install.cpp b/install/install.cpp index a1124c36..e2d47009 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -48,6 +48,7 @@ #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" @@ -631,10 +632,9 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo return result; } -int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, int retry_count, - RecoveryUI* ui) { +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); auto start = std::chrono::system_clock::now(); @@ -647,8 +647,10 @@ int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, 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, ui); + 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. @@ -703,6 +705,12 @@ int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, // 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; } diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp new file mode 100644 index 00000000..7db79048 --- /dev/null +++ b/install/wipe_data.cpp @@ -0,0 +1,190 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#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"; + +constexpr const char* CACHE_LOG_DIR = "/cache/recovery"; + +static struct selabel_handle* sehandle; + +void SetWipeDataSehandle(selabel_handle* handle) { + sehandle = handle; +} + +struct saved_log_file { + std::string name; + struct stat sb; + std::string data; +}; + +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 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 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 && 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) { + // 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(true /* save_current_log */, true /* has_cache */, sehandle); + } + + return (result == 0); +} + +bool WipeCache(RecoveryUI* ui, const std::function& 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/otautil/Android.bp b/otautil/Android.bp index c9ecba78..73398c3a 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -40,6 +40,7 @@ cc_library_static { android: { srcs: [ "dirutil.cpp", + "logging.cpp", "mounts.cpp", "parse_install_logs.cpp", "roots.cpp", diff --git a/logging.h b/otautil/include/otautil/logging.h similarity index 91% rename from logging.h rename to otautil/include/otautil/logging.h index 3cfbc7af..c4f13292 100644 --- a/logging.h +++ b/otautil/include/otautil/logging.h @@ -26,6 +26,8 @@ static constexpr int KEEP_LOG_COUNT = 10; +struct selabel_handle; + ssize_t logbasename(log_id_t id, char prio, const char* filename, const char* buf, size_t len, void* arg); @@ -41,8 +43,7 @@ void rotate_logs(const char* last_log_file, const char* last_kmsg_file); void check_and_fclose(FILE* fp, const std::string& name); void copy_log_file_to_pmsg(const std::string& source, const std::string& destination); -void copy_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); diff --git a/logging.cpp b/otautil/logging.cpp similarity index 92% rename from logging.cpp rename to otautil/logging.cpp index 48f9ec31..7c330ac6 100644 --- a/logging.cpp +++ b/otautil/logging.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "logging.h" +#include "otautil/logging.h" #include #include @@ -29,8 +29,8 @@ #include #include /* for AID_SYSTEM */ #include /* private pmsg functions */ +#include -#include "common.h" #include "otautil/dirutil.h" #include "otautil/paths.h" #include "otautil/roots.h" @@ -45,7 +45,7 @@ static const std::string LAST_LOG_FILTER = "recovery/last_log"; // 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; @@ -165,8 +165,9 @@ void reset_tmplog_offset() { 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 +190,11 @@ void copy_log_file(const std::string& source, const std::string& destination, bo } } -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 +212,9 @@ void copy_logs(bool modified_flash, bool has_cache) { 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); diff --git a/recovery-persist.cpp b/recovery-persist.cpp index e2a6699f..294017a1 100644 --- a/recovery-persist.cpp +++ b/recovery-persist.cpp @@ -43,7 +43,7 @@ #include #include /* 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 aee1ca59..d41755d0 100644 --- a/recovery-refresh.cpp +++ b/recovery-refresh.cpp @@ -42,7 +42,7 @@ #include /* 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 03491849..0e6e4976 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -55,30 +54,27 @@ #include "install/fuse_sdcard_install.h" #include "install/install.h" #include "install/package.h" -#include "logging.h" -#include "otautil/dirutil.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 "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"; // We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed // into target_files.zip. Assert the version defined in code and in Android.mk are consistent. static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions."); -static bool modified_flash = false; +static bool save_current_log = false; std::string stage; const char* reason = nullptr; @@ -148,7 +144,7 @@ static void finish_recovery() { } } - 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; @@ -167,110 +163,6 @@ static void finish_recovery() { 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 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 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); -} - static bool yes_no(Device* device, const char* question1, const char* question2) { std::vector headers{ question1, question2 }; std::vector items{ " No", " Yes" }; @@ -292,28 +184,6 @@ static bool ask_to_wipe_data(Device* device) { 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 wipe_data_menu_headers{ @@ -341,7 +211,8 @@ static InstallResult prompt_and_wipe_data(Device* device) { } 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; @@ -350,25 +221,6 @@ static InstallResult prompt_and_wipe_data(Device* device) { } } -// 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) { @@ -653,7 +505,6 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { ? Device::REBOOT : device->InvokeMenuItem(chosen_item); - bool should_wipe_cache = false; switch (chosen_action) { case Device::NO_ACTION: break; @@ -666,41 +517,40 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { 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 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: { - modified_flash = true; + save_current_log = true; bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD); if (adb) { - status = apply_from_adb(&should_wipe_cache, ui); + status = apply_from_adb(ui); } else { - status = ApplyFromSdcard(device, &should_wipe_cache, ui); - } - - if (status == INSTALL_SUCCESS && should_wipe_cache) { - if (!wipe_cache(false, device)) { - status = INSTALL_ERROR; - } + status = ApplyFromSdcard(device, ui); } if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); - copy_logs(modified_flash, has_cache); + copy_logs(save_current_log, has_cache, sehandle); } else if (!ui->IsTextVisible()) { return Device::NO_ACTION; // reboot if logs aren't visible } else { @@ -987,7 +837,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Installation aborted.\n"); @@ -1021,7 +867,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); ui->SetBackground(RecoveryUI::ERROR); @@ -1059,7 +907,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(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) { @@ -1073,15 +922,11 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); } - status = apply_from_adb(&should_wipe_cache, ui); - 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_main.cpp b/recovery_main.cpp index 8b4ad5a3..8f3f2a78 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -50,7 +50,8 @@ #include "common.h" #include "fastboot/fastboot.h" -#include "logging.h" +#include "install/wipe_data.h" +#include "otautil/logging.h" #include "otautil/paths.h" #include "otautil/roots.h" #include "otautil/sysutil.h" @@ -434,6 +435,8 @@ int main(int argc, char** argv) { ui->Print("Warning: No file_contexts\n"); } + SetWipeDataSehandle(sehandle); + std::atomic action; std::thread listener_thread(ListenRecoverySocket, ui, std::ref(action)); listener_thread.detach(); From 2be9737cf449dd0650c85ee5168d09b12d386077 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 15 Apr 2019 12:45:50 -0700 Subject: [PATCH 008/171] Remove the FD parameter from FuseDataProvider ctor. This leaves the FD implementation details to subclasses. In particular, it allows minadbd to do additional works with the FD after sideloading. Bug: 128415917 Test: atest recovery_component_test Test: atest minadbd_test Test: Sideload package on taimen. Change-Id: I106bbaad05201227bbc5fe28890bbbb06fdcb67e --- fuse_sideload/include/fuse_provider.h | 23 +++++++++++------------ minadbd/fuse_adb_provider.h | 15 +++++++-------- minadbd/minadbd_services.cpp | 3 +-- tests/component/sideload_test.cpp | 22 +++++++++++++++------- 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/fuse_sideload/include/fuse_provider.h b/fuse_sideload/include/fuse_provider.h index 499d57aa..59059cf9 100644 --- a/fuse_sideload/include/fuse_provider.h +++ b/fuse_sideload/include/fuse_provider.h @@ -25,8 +25,8 @@ // This is the base class to read data from source and provide the data to FUSE. class FuseDataProvider { public: - FuseDataProvider(android::base::unique_fd&& fd, uint64_t file_size, uint32_t block_size) - : fd_(std::move(fd)), file_size_(file_size), fuse_block_size_(block_size) {} + FuseDataProvider(uint64_t file_size, uint32_t block_size) + : file_size_(file_size), fuse_block_size_(block_size) {} virtual ~FuseDataProvider() = default; @@ -37,21 +37,15 @@ class FuseDataProvider { return fuse_block_size_; } - bool Valid() const { - return fd_ != -1; - } - // 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() = 0; + virtual void Close() {} protected: FuseDataProvider() = default; - // The underlying source to read data from. - android::base::unique_fd fd_; // 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. @@ -61,13 +55,18 @@ class FuseDataProvider { // This class reads data from a file. class FuseFileDataProvider : public FuseDataProvider { public: - FuseFileDataProvider(android::base::unique_fd&& fd, uint64_t file_size, uint32_t block_size) - : FuseDataProvider(std::move(fd), file_size, block_size) {} - 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/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h index 3fb689bd..24a463d9 100644 --- a/minadbd/fuse_adb_provider.h +++ b/minadbd/fuse_adb_provider.h @@ -14,25 +14,24 @@ * limitations under the License. */ -#ifndef __FUSE_ADB_PROVIDER_H -#define __FUSE_ADB_PROVIDER_H +#pragma once #include -#include "android-base/unique_fd.h" - #include "fuse_provider.h" // This class reads data from adb server. class FuseAdbDataProvider : public FuseDataProvider { public: - FuseAdbDataProvider(android::base::unique_fd&& fd, uint64_t file_size, uint32_t block_size) - : FuseDataProvider(std::move(fd), file_size, block_size) {} + 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; -}; -#endif + private: + // The underlying source to read data from (i.e. the one that talks to the host). + int fd_; +}; diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 79e6fc4e..f2b65c09 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -96,8 +96,7 @@ static void sideload_host_service(unique_fd sfd, const std::string& args) { exit(kMinadbdSocketIOError); } - auto adb_data_reader = - std::make_unique(std::move(sfd), file_size, block_size); + auto adb_data_reader = std::make_unique(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); diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp index f5981acb..6add99f4 100644 --- a/tests/component/sideload_test.cpp +++ b/tests/component/sideload_test.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include "fuse_provider.h" @@ -32,17 +31,26 @@ 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) { - auto provider_small_block = - std::make_unique(android::base::unique_fd(), 4096, 4095); + auto provider_small_block = std::make_unique(4096, 4095); ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_small_block))); - auto provider_large_block = - std::make_unique(android::base::unique_fd(), 4096, (1 << 22) + 1); + auto provider_large_block = std::make_unique(4096, (1 << 22) + 1); ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_large_block))); - auto provider_too_many_blocks = std::make_unique( - android::base::unique_fd(), ((1 << 18) + 1) * 4096, 4096); + auto provider_too_many_blocks = + std::make_unique(((1 << 18) + 1) * 4096, 4096); ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_too_many_blocks))); } From 2239b9e4dd08e307ad74dc44b597fd53d2d17de8 Mon Sep 17 00:00:00 2001 From: xunchang Date: Mon, 15 Apr 2019 15:24:24 -0700 Subject: [PATCH 009/171] Move load & restore logs to logging.cpp We perform these steps to perserve the recovery logs when wiping /cache partition. Move them to logging.cpp to keep the actually EraseVolume function concise. Bug: 130166585 Test: unit tests pass, mount cache and check last log after cache Change-Id: Idc52833817a446f3a0148a3dd2112f911c9ef48d --- install/include/install/wipe_data.h | 2 - install/wipe_data.cpp | 75 ++------------------- otautil/include/otautil/logging.h | 14 ++++ otautil/logging.cpp | 100 +++++++++++++++++++++++++--- recovery_main.cpp | 2 +- 5 files changed, 109 insertions(+), 84 deletions(-) diff --git a/install/include/install/wipe_data.h b/install/include/install/wipe_data.h index 06b1b95d..b34891f3 100644 --- a/install/include/install/wipe_data.h +++ b/install/include/install/wipe_data.h @@ -23,8 +23,6 @@ struct selabel_handle; -void SetWipeDataSehandle(selabel_handle* handle); - // Returns true on success. bool WipeCache(RecoveryUI* ui, const std::function& confirm); diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp index 7db79048..765a8152 100644 --- a/install/wipe_data.cpp +++ b/install/wipe_data.cpp @@ -16,14 +16,11 @@ #include "install/wipe_data.h" -#include -#include #include #include #include #include -#include #include #include @@ -39,20 +36,6 @@ constexpr const char* CACHE_ROOT = "/cache"; constexpr const char* DATA_ROOT = "/data"; constexpr const char* METADATA_ROOT = "/metadata"; -constexpr const char* CACHE_LOG_DIR = "/cache/recovery"; - -static struct selabel_handle* sehandle; - -void SetWipeDataSehandle(selabel_handle* handle) { - sehandle = handle; -} - -struct saved_log_file { - std::string name; - struct stat sb; - std::string data; -}; - 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); @@ -61,43 +44,10 @@ static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) { ui->SetProgressType(RecoveryUI::INDETERMINATE); std::vector 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 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; - } - } + // 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); @@ -128,24 +78,7 @@ static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) { } 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(true /* save_current_log */, true /* has_cache */, sehandle); + RestoreLogFilesAfterFormat(log_files); } return (result == 0); diff --git a/otautil/include/otautil/logging.h b/otautil/include/otautil/logging.h index c4f13292..60834978 100644 --- a/otautil/include/otautil/logging.h +++ b/otautil/include/otautil/logging.h @@ -18,9 +18,11 @@ #define _LOGGING_H #include +#include #include #include +#include #include @@ -28,6 +30,14 @@ 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); @@ -48,4 +58,8 @@ void reset_tmplog_offset(); void save_kernel_log(const char* destination); +std::vector ReadLogFilesToMemory(); + +bool RestoreLogFilesAfterFormat(const std::vector& log_files); + #endif //_LOGGING_H diff --git a/otautil/logging.cpp b/otautil/logging.cpp index 7c330ac6..484f1150 100644 --- a/otautil/logging.cpp +++ b/otautil/logging.cpp @@ -16,17 +16,22 @@ #include "otautil/logging.h" +#include +#include #include #include #include #include +#include +#include #include #include #include #include #include +#include #include /* for AID_SYSTEM */ #include /* private pmsg functions */ #include @@ -35,13 +40,21 @@ #include "otautil/paths.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. @@ -74,8 +87,8 @@ void check_and_fclose(FILE* fp, const std::string& name) { 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(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 @@ ssize_t logrotate(log_id_t id, char prio, const char* filename, const char* buf, 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); } @@ -243,3 +256,70 @@ void save_kernel_log(const char* destination) { buffer.resize(n); android::base::WriteStringToFile(buffer, destination); } + +std::vector ReadLogFilesToMemory() { + ensure_path_mounted("/cache"); + + struct dirent* de; + std::unique_ptr d(opendir(CACHE_LOG_DIR), closedir); + if (!d) { + if (errno != ENOENT) { + PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR; + } + return {}; + } + + std::vector 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(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& 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/recovery_main.cpp b/recovery_main.cpp index 8f3f2a78..5f3ab76d 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -435,7 +435,7 @@ int main(int argc, char** argv) { ui->Print("Warning: No file_contexts\n"); } - SetWipeDataSehandle(sehandle); + SetLoggingSehandle(sehandle); std::atomic action; std::thread listener_thread(ListenRecoverySocket, ui, std::ref(action)); From c6dc325e88a25201aa3856e6532c3ed14203a376 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 16 Apr 2019 14:22:25 -0700 Subject: [PATCH 010/171] Allow entering rescue mode via recovery UI. Only enabled on debuggable builds. Bug: 128415917 Test: Sideload package on taimen. Test: Choose "Enter rescue" from recovery UI. Change-Id: I913dbdbcffd3179e6fa72ca862f74ca8f1364b02 --- install/adb_install.cpp | 31 +++++++++++++++++------- install/include/install/adb_install.h | 2 +- minadbd/minadbd.cpp | 12 +++++++-- minadbd/minadbd_services.cpp | 6 +++++ minadbd/minadbd_services.h | 2 ++ recovery.cpp | 18 ++++++++++---- recovery_main.cpp | 4 +++ recovery_ui/device.cpp | 1 + recovery_ui/include/recovery_ui/device.h | 1 + 9 files changed, 60 insertions(+), 17 deletions(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 548b6e5b..f430920a 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,7 @@ #include "fuse_sideload.h" #include "install/install.h" #include "minadbd_types.h" +#include "otautil/sysutil.h" #include "recovery_ui/ui.h" using CommandFunction = std::function; @@ -228,7 +230,7 @@ static void ListenAndExecuteMinadbdCommands( // b11. exit the listening loop // static void CreateMinadbdServiceAndExecuteCommands( - const std::map& command_map) { + const std::map& command_map, bool rescue_mode) { signal(SIGPIPE, SIG_IGN); android::base::unique_fd recovery_socket; @@ -245,9 +247,16 @@ static void CreateMinadbdServiceAndExecuteCommands( } if (child == 0) { recovery_socket.reset(); - execl("/system/bin/minadbd", "minadbd", "--socket_fd", - std::to_string(minadbd_socket.release()).c_str(), nullptr); - + std::vector minadbd_commands = { + "/system/bin/minadbd", + "--socket_fd", + std::to_string(minadbd_socket.release()), + }; + if (rescue_mode) { + minadbd_commands.push_back("--rescue"); + } + auto exec_args = StringVectorToNullTerminatedArray(minadbd_commands); + execv(exec_args[0], exec_args.data()); _exit(EXIT_FAILURE); } @@ -280,7 +289,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int apply_from_adb(RecoveryUI* ui) { +int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode) { // 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. @@ -289,16 +298,20 @@ int apply_from_adb(RecoveryUI* ui) { return INSTALL_ERROR; } - ui->Print( - "\n\nNow send the package you want to apply\n" - "to the device with \"adb sideload \"...\n"); + if (!rescue_mode) { + ui->Print( + "\n\nNow send the package you want to apply\n" + "to the device with \"adb sideload \"...\n"); + } else { + ui->Print("\n\nWaiting for rescue commands...\n"); + } int install_result = INSTALL_ERROR; std::map command_map{ { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, }; - CreateMinadbdServiceAndExecuteCommands(command_map); + CreateMinadbdServiceAndExecuteCommands(command_map, rescue_mode); // Clean up before switching to the older state, for example setting the state // to none sets sys/class/android_usb/android0/enable to 0. diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index f7b065b6..208d0c78 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -18,4 +18,4 @@ #include -int apply_from_adb(RecoveryUI* ui); +int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode); diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp index 57158ad5..c80d5490 100644 --- a/minadbd/minadbd.cpp +++ b/minadbd/minadbd.cpp @@ -31,10 +31,13 @@ #include "minadbd_services.h" #include "minadbd_types.h" +using namespace std::string_literals; + 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) { + if ((argc != 3 && argc != 4) || argv[1] != "--socket_fd"s || + (argc == 4 && argv[3] != "--rescue"s)) { LOG(ERROR) << "minadbd has invalid arguments, argc: " << argc; exit(kMinadbdArgumentsParsingError); } @@ -50,7 +53,12 @@ int main(int argc, char** argv) { } SetMinadbdSocketFd(socket_fd); - adb_device_banner = "sideload"; + if (argc == 4) { + SetMinadbdRescueMode(true); + adb_device_banner = "rescue"; + } else { + adb_device_banner = "sideload"; + } signal(SIGPIPE, SIG_IGN); diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index f2b65c09..eaf88ecc 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -45,10 +45,16 @@ #include "sysdeps.h" static int minadbd_socket = -1; +static bool rescue_mode = false; + void SetMinadbdSocketFd(int socket_fd) { minadbd_socket = socket_fd; } +void SetMinadbdRescueMode(bool rescue) { + rescue_mode = rescue; +} + static bool WriteCommandToFd(MinadbdCommands cmd, int fd) { char message[kMinadbdMessageSize]; memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix)); diff --git a/minadbd/minadbd_services.h b/minadbd/minadbd_services.h index 6835bd77..20e3410c 100644 --- a/minadbd/minadbd_services.h +++ b/minadbd/minadbd_services.h @@ -17,3 +17,5 @@ #pragma once void SetMinadbdSocketFd(int socket_fd); + +void SetMinadbdRescueMode(bool); diff --git a/recovery.cpp b/recovery.cpp index 0e6e4976..ce29cb27 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -538,12 +538,20 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { break; } case Device::APPLY_ADB_SIDELOAD: - case Device::APPLY_SDCARD: { + case Device::APPLY_SDCARD: + case Device::ENTER_RESCUE: { save_current_log = true; - bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD); - if (adb) { - status = apply_from_adb(ui); + + bool adb = true; + if (chosen_action == Device::ENTER_RESCUE) { + // Switch to graphics screen. + ui->ShowText(false); + status = ApplyFromAdb(ui, true /* rescue_mode */); + ui->ShowText(true); + } else if (chosen_action == Device::APPLY_ADB_SIDELOAD) { + status = ApplyFromAdb(ui, false /* rescue_mode */); } else { + adb = false; status = ApplyFromSdcard(device, ui); } @@ -926,7 +934,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); } - status = apply_from_adb(ui); + status = ApplyFromAdb(ui, false /* rescue_mode */); ui->Print("\nInstall from ADB complete (status: %d).\n", status); if (sideload_auto_reboot) { ui->Print("Rebooting automatically.\n"); diff --git a/recovery_main.cpp b/recovery_main.cpp index 5f3ab76d..38e1db73 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -423,6 +423,10 @@ int main(int argc, char** argv) { device->RemoveMenuItemForAction(Device::ENTER_FASTBOOT); } + if (!is_ro_debuggable()) { + device->RemoveMenuItemForAction(Device::ENTER_RESCUE); + } + ui->SetBackground(RecoveryUI::NONE); if (show_text) ui->ShowText(true); diff --git a/recovery_ui/device.cpp b/recovery_ui/device.cpp index ddb0118d..e7ae1a3e 100644 --- a/recovery_ui/device.cpp +++ b/recovery_ui/device.cpp @@ -37,6 +37,7 @@ static std::vector> g_menu_actions { "View recovery logs", Device::VIEW_RECOVERY_LOGS }, { "Run graphics test", Device::RUN_GRAPHICS_TEST }, { "Run locale test", Device::RUN_LOCALE_TEST }, + { "Enter rescue", Device::ENTER_RESCUE }, { "Power off", Device::SHUTDOWN }, }; diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 3c44510c..8f17639d 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -50,6 +50,7 @@ class Device { KEY_INTERRUPTED = 13, ENTER_FASTBOOT = 14, ENTER_RECOVERY = 15, + ENTER_RESCUE = 16, }; explicit Device(RecoveryUI* ui); From ed717ca17d0b1a35f2d2e57802e2381a6004fdd1 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 4 Apr 2019 18:37:58 -0700 Subject: [PATCH 011/171] minadbd: Support rescue install and getprop commands. Bug: 128415917 Test: Enter rescue mode on taimen. Send the following commands: `adb rescue getprop ro.build.fingerprint` `adb rescue getprop ro.build.date.utc` `adb rescue install /path/to/package.zip` Test: Sideload on taimen w/ `adb sideload /path/to/package.zip`. Change-Id: Ibc25daf9fd13f7002e54789f67aaf85d06976bb8 --- minadbd/Android.bp | 1 - minadbd/fuse_adb_provider.cpp | 4 -- minadbd/fuse_adb_provider.h | 2 - minadbd/minadbd_services.cpp | 97 +++++++++++++++++++++++++++++------ minadbd/minadbd_types.h | 1 + 5 files changed, 83 insertions(+), 22 deletions(-) diff --git a/minadbd/Android.bp b/minadbd/Android.bp index e4f7712e..b1c68ca9 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -76,7 +76,6 @@ cc_binary { "libadbd", "libbase", "libcrypto", - "libfusesideload", "libminadbd_services", ], } diff --git a/minadbd/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp index 9d19a1ec..47719b07 100644 --- a/minadbd/fuse_adb_provider.cpp +++ b/minadbd/fuse_adb_provider.cpp @@ -37,7 +37,3 @@ bool FuseAdbDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_s return true; } - -void FuseAdbDataProvider::Close() { - WriteFdExactly(fd_, "DONEDONE"); -} diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h index 24a463d9..c5561e57 100644 --- a/minadbd/fuse_adb_provider.h +++ b/minadbd/fuse_adb_provider.h @@ -29,8 +29,6 @@ class FuseAdbDataProvider : public FuseDataProvider { 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_; diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index eaf88ecc..136392a6 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -28,15 +28,19 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include "adb.h" #include "adb_unique_fd.h" +#include "adb_utils.h" #include "fdevent.h" #include "fuse_adb_provider.h" #include "fuse_sideload.h" @@ -87,46 +91,109 @@ static bool WaitForCommandStatus(int fd, MinadbdCommandStatus* status) { return true; } -static void sideload_host_service(unique_fd sfd, const std::string& args) { +static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, + MinadbdCommandStatus* status) { + auto pieces = android::base::Split(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) { + if (pieces.size() != 2 || !android::base::ParseInt(pieces[0], &file_size) || file_size <= 0 || + !android::base::ParseInt(pieces[1], &block_size) || block_size <= 0) { LOG(ERROR) << "bad sideload-host arguments: " << args; - exit(kMinadbdPackageSizeError); + return kMinadbdPackageSizeError; } LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size; if (!WriteCommandToFd(MinadbdCommands::kInstall, minadbd_socket)) { - exit(kMinadbdSocketIOError); + return kMinadbdSocketIOError; } auto adb_data_reader = std::make_unique(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); + return kMinadbdFuseStartError; } + if (!WaitForCommandStatus(minadbd_socket, status)) { + return kMinadbdMessageFormatError; + } + + // Signal host-side adb to stop. For sideload mode, we always send kSideloadServiceExitSuccess + // (i.e. "DONEDONE") regardless of the install result. For rescue mode, we send failure message on + // install error. + if (!rescue_mode || *status == MinadbdCommandStatus::kSuccess) { + if (!android::base::WriteFully(sfd, kSideloadServiceExitSuccess, + strlen(kSideloadServiceExitSuccess))) { + return kMinadbdHostSocketIOError; + } + } else { + if (!android::base::WriteFully(sfd, kSideloadServiceExitFailure, + strlen(kSideloadServiceExitFailure))) { + return kMinadbdHostSocketIOError; + } + } + + return kMinadbdSuccess; +} + +// Sideload service always exits after serving an install command. +static void SideloadHostService(unique_fd sfd, const std::string& args) { MinadbdCommandStatus status; - if (!WaitForCommandStatus(minadbd_socket, &status)) { - exit(kMinadbdMessageFormatError); - } - LOG(INFO) << "Got command status: " << static_cast(status); + exit(RunAdbFuseSideload(sfd.get(), args, &status)); +} - LOG(INFO) << "sideload_host finished"; - exit(kMinadbdSuccess); +// Rescue service waits for the next command after an install command. +static void RescueInstallHostService(unique_fd sfd, const std::string& args) { + MinadbdCommandStatus status; + if (auto result = RunAdbFuseSideload(sfd.get(), args, &status); result != kMinadbdSuccess) { + exit(result); + } +} + +static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { + static const std::unordered_set kGetpropAllowedProps = { + "ro.build.fingerprint", + "ro.build.date.utc", + }; + auto allowed = kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end(); + if (!allowed) { + return; + } + + auto result = android::base::GetProperty(prop, ""); + if (result.empty()) { + return; + } + if (!android::base::WriteFully(sfd, result.data(), result.size())) { + exit(kMinadbdHostSocketIOError); + } } unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { + if (rescue_mode) { + if (ConsumePrefix(&name, "rescue-install:")) { + // rescue-install:: + std::string args(name); + return create_service_thread( + "rescue-install", std::bind(RescueInstallHostService, std::placeholders::_1, args)); + } else if (ConsumePrefix(&name, "rescue-getprop:")) { + // rescue-getprop: + std::string args(name); + return create_service_thread( + "rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args)); + } + return unique_fd{}; + } + 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(kMinadbdAdbVersionError); - } else if (name.starts_with("sideload-host:")) { - std::string arg(name.substr(strlen("sideload-host:"))); + } else if (ConsumePrefix(&name, "sideload-host:")) { + // sideload-host:: + std::string args(name); return create_service_thread("sideload-host", - std::bind(sideload_host_service, std::placeholders::_1, arg)); + std::bind(SideloadHostService, std::placeholders::_1, args)); } return unique_fd{}; } diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h index 7bd69096..5fb7803e 100644 --- a/minadbd/minadbd_types.h +++ b/minadbd/minadbd_types.h @@ -35,6 +35,7 @@ enum MinadbdErrorCode : int { kMinadbdUnsupportedCommandError = 7, kMinadbdCommandExecutionError = 8, kMinadbdErrorUnknown = 9, + kMinadbdHostSocketIOError = 10, }; enum class MinadbdCommandStatus : uint32_t { From 9d05c8a35776444f6e967f8a4ac5863a31e54cf6 Mon Sep 17 00:00:00 2001 From: xunchang Date: Tue, 16 Apr 2019 12:07:42 -0700 Subject: [PATCH 012/171] Fall back to en-US if localized bitmap is missing for a locale We used to show the image for the last locale or an empty image, if the localized image is missing for a specific locale. As the default english one is more meaningful to users, we should just error out and fall back to use the default locale when the image loading fails. Bug: 128934634 Test: run graphic test, locale test Change-Id: Iafd3e8466aec63b4952d1959b2a3d37e358677d4 --- minui/resources.cpp | 14 ++++++++++---- recovery_main.cpp | 1 - recovery_ui/include/recovery_ui/ui.h | 2 ++ recovery_ui/screen_ui.cpp | 20 +++++++++++++++----- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/minui/resources.cpp b/minui/resources.cpp index 069a4952..53c932bf 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -414,12 +414,18 @@ int res_create_localized_alpha_surface(const char* name, __unused int len = row[4]; char* loc = reinterpret_cast(&row[5]); - if (y + 1 + h >= height || matches_locale(loc, locale)) { + // We need to include one additional line for the metadata of the localized image. + if (y + 1 + h > height) { + printf("Read exceeds the image boundary, y %u, h %d, height %u\n", y, h, height); + return -8; + } + + if (matches_locale(loc, locale)) { printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); auto surface = GRSurface::Create(w, h, w, 1); if (!surface) { - return -8; + return -9; } for (int i = 0; i < h; ++i, ++y) { @@ -428,7 +434,7 @@ int res_create_localized_alpha_surface(const char* name, } *pSurface = surface.release(); - break; + return 0; } for (int i = 0; i < h; ++i, ++y) { @@ -436,7 +442,7 @@ int res_create_localized_alpha_surface(const char* name, } } - return 0; + return -10; } void res_free_surface(GRSurface* surface) { diff --git a/recovery_main.cpp b/recovery_main.cpp index 38e1db73..37d9da0d 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -373,7 +373,6 @@ int main(int argc, char** argv) { } if (locale.empty()) { - static constexpr const char* DEFAULT_LOCALE = "en-US"; locale = DEFAULT_LOCALE; } } diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h index d55322cf..797e2f0d 100644 --- a/recovery_ui/include/recovery_ui/ui.h +++ b/recovery_ui/include/recovery_ui/ui.h @@ -27,6 +27,8 @@ #include #include +static constexpr const char* DEFAULT_LOCALE = "en-US"; + // Abstract class for controlling the user interface during recovery. class RecoveryUI { public: diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp index 870db621..82300452 100644 --- a/recovery_ui/screen_ui.cpp +++ b/recovery_ui/screen_ui.cpp @@ -817,12 +817,22 @@ std::unique_ptr ScreenRecoveryUI::LoadBitmap(const std::string& filen std::unique_ptr ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) { GRSurface* surface; - if (auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface); - result < 0) { - LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")"; - return nullptr; + auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface); + if (result == 0) { + return std::unique_ptr(surface); } - return std::unique_ptr(surface); + // TODO(xunchang) create a error code enum to refine the retry condition. + LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error " + << result << "). Falling back to use default locale."; + + result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface); + if (result == 0) { + return std::unique_ptr(surface); + } + + LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE + << " (error " << result << ")"; + return nullptr; } static char** Alloc2d(size_t rows, size_t cols) { From 34723087fe79349607e3128707f0739e5e230a77 Mon Sep 17 00:00:00 2001 From: xunchang Date: Mon, 22 Apr 2019 15:32:17 -0700 Subject: [PATCH 013/171] matches_locale no longer accept empty locales in the png file The legacy png files have an empty line in the end. And the recovery used to match any missing locale, e.g. "he" with that line and gets an empty image. Since the empty image is barely useful, we should just error out and fall back to the default locale. This reversed the unit test check added in d17a6885253da909e376ba5ca5084f5281f3557c Bug: 128934634 Test: run locale test with "he" and legacy images, recovery reports error and doesn't crash even without default locale fall back Change-Id: Ibdb7dd0b42348de5e392c834cce67ff02be85c24 --- minui/resources.cpp | 4 ++++ tests/unit/locale_test.cpp | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/minui/resources.cpp b/minui/resources.cpp index 53c932bf..00d36d5f 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -347,6 +347,10 @@ bool matches_locale(const std::string& prefix, const std::string& locale) { // match the locale string without the {script} section. // For instance, prefix == "en" matches locale == "en-US", prefix == "sr-Latn" matches locale // == "sr-Latn-BA", and prefix == "zh-CN" matches locale == "zh-Hans-CN". + if (prefix.empty()) { + return false; + } + if (android::base::StartsWith(locale, prefix)) { return true; } diff --git a/tests/unit/locale_test.cpp b/tests/unit/locale_test.cpp index cdaba0e8..c69434c1 100644 --- a/tests/unit/locale_test.cpp +++ b/tests/unit/locale_test.cpp @@ -27,7 +27,7 @@ TEST(LocaleTest, Misc) { EXPECT_FALSE(matches_locale("en-GB", "en")); EXPECT_FALSE(matches_locale("en-GB", "en-US")); EXPECT_FALSE(matches_locale("en-US", "")); - // Empty locale prefix in the PNG file will match the input locale. - EXPECT_TRUE(matches_locale("", "en-US")); + // Empty locale prefix in the PNG file should not match the input locale. + EXPECT_FALSE(matches_locale("", "en-US")); EXPECT_TRUE(matches_locale("sr-Latn", "sr-Latn-BA")); } From 9c04eb46b7492033e4675bd303a8bb20548081b5 Mon Sep 17 00:00:00 2001 From: xunchang Date: Wed, 17 Apr 2019 14:43:58 -0700 Subject: [PATCH 014/171] Add test for minadbd Ass some unit tests to check if the minadbd service exit correctly in the failure case. Also start the fuse and verify the socket communication between minadbd with adb host, and minadbd with recovery. Bug: 131037235 Test: run unit tests repeatedly, injects some errors and test fails without dangling process. Change-Id: I2f073b701b25d7f1aafc59868a7a91a8cbefaf49 --- minadbd/Android.bp | 2 + minadbd/minadbd_services.cpp | 8 +- minadbd/minadbd_services.h | 4 + minadbd/minadbd_services_test.cpp | 213 ++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 minadbd/minadbd_services_test.cpp diff --git a/minadbd/Android.bp b/minadbd/Android.bp index b1c68ca9..007e5057 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -90,12 +90,14 @@ cc_test { srcs: [ "fuse_adb_provider_test.cpp", + "minadbd_services_test.cpp", ], static_libs: [ "libminadbd_services", "libfusesideload", "libadbd", + "libcrypto", ], shared_libs: [ diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 136392a6..f6aff71f 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -50,6 +50,7 @@ static int minadbd_socket = -1; static bool rescue_mode = false; +static std::string sideload_mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT; void SetMinadbdSocketFd(int socket_fd) { minadbd_socket = socket_fd; @@ -59,6 +60,10 @@ void SetMinadbdRescueMode(bool rescue) { rescue_mode = rescue; } +void SetSideloadMountPoint(const std::string& path) { + sideload_mount_point = path; +} + static bool WriteCommandToFd(MinadbdCommands cmd, int fd) { char message[kMinadbdMessageSize]; memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix)); @@ -109,7 +114,8 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, } auto adb_data_reader = std::make_unique(sfd, file_size, block_size); - if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) { + if (int result = run_fuse_sideload(std::move(adb_data_reader), sideload_mount_point.c_str()); + result != 0) { LOG(ERROR) << "Failed to start fuse"; return kMinadbdFuseStartError; } diff --git a/minadbd/minadbd_services.h b/minadbd/minadbd_services.h index 20e3410c..5575c6b8 100644 --- a/minadbd/minadbd_services.h +++ b/minadbd/minadbd_services.h @@ -16,6 +16,10 @@ #pragma once +#include + void SetMinadbdSocketFd(int socket_fd); void SetMinadbdRescueMode(bool); + +void SetSideloadMountPoint(const std::string& path); diff --git a/minadbd/minadbd_services_test.cpp b/minadbd/minadbd_services_test.cpp new file mode 100644 index 00000000..413ba0df --- /dev/null +++ b/minadbd/minadbd_services_test.cpp @@ -0,0 +1,213 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "adb.h" +#include "adb_io.h" +#include "fuse_adb_provider.h" +#include "fuse_sideload.h" +#include "minadbd_services.h" +#include "minadbd_types.h" +#include "socket.h" + +class MinadbdServicesTest : public ::testing::Test { + protected: + static constexpr int EXIT_TIME_OUT = 10; + + void SetUp() override { + ASSERT_TRUE( + android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &minadbd_socket_, &recovery_socket_)); + SetMinadbdSocketFd(minadbd_socket_); + SetSideloadMountPoint(mount_point_.path); + + package_path_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_FILENAME; + exit_flag_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_EXIT_FLAG; + + signal(SIGPIPE, SIG_IGN); + } + + void TearDown() override { + // Umount in case the test fails. Ignore the result. + umount(mount_point_.path); + + signal(SIGPIPE, SIG_DFL); + } + + void ReadAndCheckCommandMessage(int fd, MinadbdCommands expected_command) { + std::vector received(kMinadbdMessageSize, '\0'); + ASSERT_TRUE(android::base::ReadFully(fd, received.data(), kMinadbdMessageSize)); + + std::vector expected(kMinadbdMessageSize, '\0'); + memcpy(expected.data(), kMinadbdCommandPrefix, strlen(kMinadbdCommandPrefix)); + memcpy(expected.data() + strlen(kMinadbdCommandPrefix), &expected_command, + sizeof(expected_command)); + ASSERT_EQ(expected, received); + } + + void WaitForFusePath() { + constexpr int TIME_OUT = 10; + for (int i = 0; i < TIME_OUT; ++i) { + struct stat sb; + if (stat(package_path_.c_str(), &sb) == 0) { + return; + } + + if (errno == ENOENT) { + sleep(1); + continue; + } + FAIL() << "Timed out waiting for the fuse-provided package " << strerror(errno); + } + } + + void StatExitFlagAndExitProcess(int exit_code) { + struct stat sb; + if (stat(exit_flag_.c_str(), &sb) != 0) { + PLOG(ERROR) << "Failed to stat " << exit_flag_; + } + + exit(exit_code); + } + + void WriteMinadbdCommandStatus(MinadbdCommandStatus status) { + std::string status_message(kMinadbdMessageSize, '\0'); + memcpy(status_message.data(), kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix)); + memcpy(status_message.data() + strlen(kMinadbdStatusPrefix), &status, sizeof(status)); + ASSERT_TRUE( + android::base::WriteFully(recovery_socket_, status_message.data(), kMinadbdMessageSize)); + } + + void ExecuteCommandAndWaitForExit(const std::string& command) { + unique_fd fd = daemon_service_to_fd(command, nullptr); + ASSERT_NE(-1, fd); + sleep(EXIT_TIME_OUT); + } + + android::base::unique_fd minadbd_socket_; + android::base::unique_fd recovery_socket_; + + TemporaryDir mount_point_; + std::string package_path_; + std::string exit_flag_; +}; + +TEST_F(MinadbdServicesTest, SideloadHostService_wrong_size_argument) { + ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:abc:4096"), + ::testing::ExitedWithCode(kMinadbdPackageSizeError), ""); +} + +TEST_F(MinadbdServicesTest, SideloadHostService_wrong_block_size) { + ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:10:20"), + ::testing::ExitedWithCode(kMinadbdFuseStartError), ""); +} + +TEST_F(MinadbdServicesTest, SideloadHostService_broken_minadbd_socket) { + SetMinadbdSocketFd(-1); + ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"), + ::testing::ExitedWithCode(kMinadbdSocketIOError), ""); +} + +TEST_F(MinadbdServicesTest, SideloadHostService_broken_recovery_socket) { + recovery_socket_.reset(); + ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"), + ::testing::ExitedWithCode(kMinadbdSocketIOError), ""); +} + +TEST_F(MinadbdServicesTest, SideloadHostService_wrong_command_format) { + auto test_body = [&](const std::string& command) { + unique_fd fd = daemon_service_to_fd(command, nullptr); + ASSERT_NE(-1, fd); + WaitForFusePath(); + ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall); + + struct stat sb; + ASSERT_EQ(0, stat(exit_flag_.c_str(), &sb)); + ASSERT_TRUE(android::base::WriteStringToFd("12345678", recovery_socket_)); + sleep(EXIT_TIME_OUT); + }; + + ASSERT_EXIT(test_body("sideload-host:4096:4096"), + ::testing::ExitedWithCode(kMinadbdMessageFormatError), ""); +} + +TEST_F(MinadbdServicesTest, SideloadHostService_read_data_from_fuse) { + auto test_body = [&]() { + std::vector content(4096, 'a'); + // Start a new process instead of a thread to read from the package mounted by FUSE. Because + // the test may not exit and report failures correctly when the thread blocks by a syscall. + pid_t pid = fork(); + if (pid == 0) { + WaitForFusePath(); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(package_path_.c_str(), O_RDONLY))); + // Do not use assertion here because we want to stat the exit flag and exit the process. + // Otherwise the test will wait for the time out instead of failing immediately. + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << package_path_; + StatExitFlagAndExitProcess(1); + } + std::vector content_from_fuse(4096); + if (!android::base::ReadFully(fd, content_from_fuse.data(), 4096)) { + PLOG(ERROR) << "Failed to read from " << package_path_; + StatExitFlagAndExitProcess(1); + } + if (content_from_fuse != content) { + LOG(ERROR) << "Content read from fuse doesn't match with the expected value"; + StatExitFlagAndExitProcess(1); + } + StatExitFlagAndExitProcess(0); + } + + unique_fd fd = daemon_service_to_fd("sideload-host:4096:4096", nullptr); + ASSERT_NE(-1, fd); + ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall); + + // Mimic the response from adb host. + std::string adb_message(8, '\0'); + ASSERT_TRUE(android::base::ReadFully(fd, adb_message.data(), 8)); + ASSERT_EQ(android::base::StringPrintf("%08u", 0), adb_message); + ASSERT_TRUE(android::base::WriteFully(fd, content.data(), 4096)); + + // Check that we read the correct data from fuse. + int child_status; + waitpid(pid, &child_status, 0); + ASSERT_TRUE(WIFEXITED(child_status)); + ASSERT_EQ(0, WEXITSTATUS(child_status)); + + WriteMinadbdCommandStatus(MinadbdCommandStatus::kSuccess); + + // TODO(xunchang) check if adb host-side receives "DONEDONE", there's a race condition between + // receiving the message and exit of test body (by detached thread in minadbd service). + exit(kMinadbdSuccess); + }; + + ASSERT_EXIT(test_body(), ::testing::ExitedWithCode(kMinadbdSuccess), ""); +} From 10f441a9dbb91be3124f455439631abcf8e96cde Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 19 Apr 2019 15:22:15 -0700 Subject: [PATCH 015/171] minadbd: Support `adb reboot` under sideload/rescue modes. Bug: 128415917 Test: Run the following commands under sideload and rescue modes respectively. $ adb reboot $ adb reboot bootloader $ adb reboot recovery $ adb reboot rescue $ adb reboot invalid Change-Id: I84daf63e3360b7b4a0af5e055149a4f54e10ba90 --- install/adb_install.cpp | 98 +++++++++++++++++------- install/include/install/adb_install.h | 6 +- install/include/install/install.h | 3 +- minadbd/minadbd_services.cpp | 43 ++++++++++- minadbd/minadbd_services_test.cpp | 6 +- minadbd/minadbd_types.h | 13 +++- recovery.cpp | 58 ++++++++------ recovery_main.cpp | 5 ++ recovery_ui/include/recovery_ui/device.h | 4 + 9 files changed, 176 insertions(+), 60 deletions(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index f430920a..d79f6f4b 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -44,30 +45,34 @@ #include "install/install.h" #include "minadbd_types.h" #include "otautil/sysutil.h" +#include "recovery_ui/device.h" #include "recovery_ui/ui.h" -using CommandFunction = std::function; +// A CommandFunction returns a pair of (result, should_continue), which indicates the command +// execution result and whether it should proceed to the next iteration. The execution result will +// always be sent to the minadbd side. +using CommandFunction = std::function()>; 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) { +// Parses the minadbd command in |message|; returns MinadbdCommand::kError upon errors. +static MinadbdCommand ParseMinadbdCommand(const std::string& message) { if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) { LOG(ERROR) << "Failed to parse command in message " << message; - return MinadbdCommands::kError; + return MinadbdCommand::kError; } auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix)); auto cmd_code = android::base::get_unaligned(cmd_code_string.c_str()); - if (cmd_code >= static_cast(MinadbdCommands::kError)) { + if (cmd_code >= static_cast(MinadbdCommand::kError)) { LOG(ERROR) << "Unsupported command code: " << cmd_code; - return MinadbdCommands::kError; + return MinadbdCommand::kError; } - return static_cast(cmd_code); + return static_cast(cmd_code); } static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { @@ -82,13 +87,15 @@ static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { return true; } -// Installs the package from FUSE. Returns true if the installation succeeds, and false otherwise. -static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) { +// Installs the package from FUSE. Returns the installation result and whether it should continue +// waiting for new commands. +static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) { // 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; + bool should_continue = true; *result = INSTALL_ERROR; for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { struct stat st; @@ -97,6 +104,7 @@ static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) { sleep(1); continue; } else { + should_continue = false; ui->Print("\nTimed out waiting for fuse to be ready.\n\n"); break; } @@ -108,13 +116,39 @@ static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) { // 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; + return std::make_pair(*result == INSTALL_SUCCESS, should_continue); } -// 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& command_map) { +static auto AdbRebootHandler(MinadbdCommand command, int* result, + Device::BuiltinAction* reboot_action) { + switch (command) { + case MinadbdCommand::kRebootBootloader: + *reboot_action = Device::REBOOT_BOOTLOADER; + break; + case MinadbdCommand::kRebootFastboot: + *reboot_action = Device::ENTER_FASTBOOT; + break; + case MinadbdCommand::kRebootRecovery: + *reboot_action = Device::ENTER_RECOVERY; + break; + case MinadbdCommand::kRebootRescue: + // Use Device::REBOOT_RESCUE instead of Device::ENTER_RESCUE. This allows rebooting back into + // rescue mode (potentially using a newly installed recovery image). + *reboot_action = Device::REBOOT_RESCUE; + break; + case MinadbdCommand::kRebootAndroid: + default: + *reboot_action = Device::REBOOT; + break; + } + *result = INSTALL_REBOOT; + return std::make_pair(true, false); +} + +// Parses and executes the command from minadbd. Returns whether the caller should keep waiting for +// next command. +static bool HandleMessageFromMinadbd(int socket_fd, + const std::map& command_map) { char buffer[kMinadbdMessageSize]; if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) { PLOG(ERROR) << "Failed to read message from minadbd"; @@ -122,8 +156,8 @@ static bool HandleMessageFromMinadbd( } std::string message(buffer, buffer + kMinadbdMessageSize); - auto command_type = ParseMinadbdCommands(message); - if (command_type == MinadbdCommands::kError) { + auto command_type = ParseMinadbdCommand(message); + if (command_type == MinadbdCommand::kError) { return false; } if (command_map.find(command_type) == command_map.end()) { @@ -135,17 +169,19 @@ static bool HandleMessageFromMinadbd( // 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(command_type); - return WriteStatusToFd(MinadbdCommandStatus::kFailure, socket_fd); + const auto [result, should_continue] = command_func(); + LOG(INFO) << "Command " << static_cast(command_type) << " finished with " << result; + if (!WriteStatusToFd(result ? MinadbdCommandStatus::kSuccess : MinadbdCommandStatus::kFailure, + socket_fd)) { + return false; } - return WriteStatusToFd(MinadbdCommandStatus::kSuccess, socket_fd); + return should_continue; } // 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& command_map) { + const std::map& command_map) { android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC)); if (epoll_fd == -1) { PLOG(ERROR) << "Failed to create epoll"; @@ -230,7 +266,7 @@ static void ListenAndExecuteMinadbdCommands( // b11. exit the listening loop // static void CreateMinadbdServiceAndExecuteCommands( - const std::map& command_map, bool rescue_mode) { + const std::map& command_map, bool rescue_mode) { signal(SIGPIPE, SIG_IGN); android::base::unique_fd recovery_socket; @@ -271,7 +307,6 @@ static void CreateMinadbdServiceAndExecuteCommands( std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket), std::ref(command_map)); - if (listener_thread.joinable()) { listener_thread.join(); } @@ -289,7 +324,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode) { +int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action) { // Save the usb state to restore after the sideload operation. std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); // Clean up state and stop adbd. @@ -307,8 +342,19 @@ int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode) { } int install_result = INSTALL_ERROR; - std::map command_map{ - { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, + std::map command_map{ + { MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, + { MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid, + &install_result, reboot_action) }, + { MinadbdCommand::kRebootBootloader, + std::bind(&AdbRebootHandler, MinadbdCommand::kRebootBootloader, &install_result, + reboot_action) }, + { MinadbdCommand::kRebootFastboot, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootFastboot, + &install_result, reboot_action) }, + { MinadbdCommand::kRebootRecovery, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRecovery, + &install_result, reboot_action) }, + { MinadbdCommand::kRebootRescue, + std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) }, }; CreateMinadbdServiceAndExecuteCommands(command_map, rescue_mode); diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index 208d0c78..49b32b54 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -16,6 +16,10 @@ #pragma once +#include #include -int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode); +// Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum +// InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with +// the reboot target set in reboot_action. +int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action); diff --git a/install/include/install/install.h b/install/include/install/install.h index 1e41b484..c0a8f1f4 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -34,7 +34,8 @@ enum InstallResult { INSTALL_NONE, INSTALL_SKIPPED, INSTALL_RETRY, - INSTALL_KEY_INTERRUPTED + INSTALL_KEY_INTERRUPTED, + INSTALL_REBOOT, }; enum class OtaType { diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index f6aff71f..9b1999d9 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -64,7 +64,7 @@ void SetSideloadMountPoint(const std::string& path) { sideload_mount_point = path; } -static bool WriteCommandToFd(MinadbdCommands cmd, int fd) { +static bool WriteCommandToFd(MinadbdCommand cmd, int fd) { char message[kMinadbdMessageSize]; memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix)); android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), cmd); @@ -109,7 +109,7 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size; - if (!WriteCommandToFd(MinadbdCommands::kInstall, minadbd_socket)) { + if (!WriteCommandToFd(MinadbdCommand::kInstall, minadbd_socket)) { return kMinadbdSocketIOError; } @@ -175,7 +175,45 @@ static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { } } +// Reboots into the given target. We don't reboot directly from minadbd, but going through recovery +// instead. This allows recovery to finish all the pending works (clear BCB, save logs etc) before +// the reboot. +static void RebootHostService(unique_fd /* sfd */, const std::string& target) { + MinadbdCommand command; + if (target == "bootloader") { + command = MinadbdCommand::kRebootBootloader; + } else if (target == "rescue") { + command = MinadbdCommand::kRebootRescue; + } else if (target == "recovery") { + command = MinadbdCommand::kRebootRecovery; + } else if (target == "fastboot") { + command = MinadbdCommand::kRebootFastboot; + } else { + command = MinadbdCommand::kRebootAndroid; + } + if (!WriteCommandToFd(command, minadbd_socket)) { + exit(kMinadbdSocketIOError); + } + MinadbdCommandStatus status; + if (!WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } +} + unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { + // Common services that are supported both in sideload and rescue modes. + if (ConsumePrefix(&name, "reboot:")) { + // "reboot:", where target must be one of the following. + std::string args(name); + if (args.empty() || args == "bootloader" || args == "rescue" || args == "recovery" || + args == "fastboot") { + return create_service_thread("reboot", + std::bind(RebootHostService, std::placeholders::_1, args)); + } + return unique_fd{}; + } + + // Rescue-specific services. if (rescue_mode) { if (ConsumePrefix(&name, "rescue-install:")) { // rescue-install:: @@ -191,6 +229,7 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport * return unique_fd{}; } + // Sideload-specific services. 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). diff --git a/minadbd/minadbd_services_test.cpp b/minadbd/minadbd_services_test.cpp index 413ba0df..593180bb 100644 --- a/minadbd/minadbd_services_test.cpp +++ b/minadbd/minadbd_services_test.cpp @@ -62,7 +62,7 @@ class MinadbdServicesTest : public ::testing::Test { signal(SIGPIPE, SIG_DFL); } - void ReadAndCheckCommandMessage(int fd, MinadbdCommands expected_command) { + void ReadAndCheckCommandMessage(int fd, MinadbdCommand expected_command) { std::vector received(kMinadbdMessageSize, '\0'); ASSERT_TRUE(android::base::ReadFully(fd, received.data(), kMinadbdMessageSize)); @@ -147,7 +147,7 @@ TEST_F(MinadbdServicesTest, SideloadHostService_wrong_command_format) { unique_fd fd = daemon_service_to_fd(command, nullptr); ASSERT_NE(-1, fd); WaitForFusePath(); - ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall); + ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall); struct stat sb; ASSERT_EQ(0, stat(exit_flag_.c_str(), &sb)); @@ -188,7 +188,7 @@ TEST_F(MinadbdServicesTest, SideloadHostService_read_data_from_fuse) { unique_fd fd = daemon_service_to_fd("sideload-host:4096:4096", nullptr); ASSERT_NE(-1, fd); - ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall); + ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall); // Mimic the response from adb host. std::string adb_message(8, '\0'); diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h index 5fb7803e..b370b795 100644 --- a/minadbd/minadbd_types.h +++ b/minadbd/minadbd_types.h @@ -43,12 +43,19 @@ enum class MinadbdCommandStatus : uint32_t { kFailure = 1, }; -enum class MinadbdCommands : uint32_t { +enum class MinadbdCommand : uint32_t { kInstall = 0, kUiPrint = 1, - kError = 2, + kRebootAndroid = 2, + kRebootBootloader = 3, + kRebootFastboot = 4, + kRebootRecovery = 5, + kRebootRescue = 6, + + // Last but invalid command. + kError, }; -static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommands)); +static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommand)); static_assert(kMinadbdMessageSize == sizeof(kMinadbdStatusPrefix) - 1 + sizeof(MinadbdCommandStatus)); diff --git a/recovery.cpp b/recovery.cpp index ce29cb27..5bd9b172 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -512,6 +512,7 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { case Device::REBOOT: case Device::SHUTDOWN: case Device::REBOOT_BOOTLOADER: + case Device::REBOOT_RESCUE: case Device::ENTER_FASTBOOT: case Device::ENTER_RECOVERY: return chosen_action; @@ -537,32 +538,36 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { if (!ui->IsTextVisible()) return Device::NO_ACTION; break; } + case Device::APPLY_ADB_SIDELOAD: case Device::APPLY_SDCARD: case Device::ENTER_RESCUE: { save_current_log = true; bool adb = true; + Device::BuiltinAction reboot_action; if (chosen_action == Device::ENTER_RESCUE) { // Switch to graphics screen. ui->ShowText(false); - status = ApplyFromAdb(ui, true /* rescue_mode */); - ui->ShowText(true); + status = ApplyFromAdb(ui, true /* rescue_mode */, &reboot_action); } else if (chosen_action == Device::APPLY_ADB_SIDELOAD) { - status = ApplyFromAdb(ui, false /* rescue_mode */); + status = ApplyFromAdb(ui, false /* rescue_mode */, &reboot_action); } else { adb = false; status = ApplyFromSdcard(device, ui); } + ui->Print("\nInstall from %s completed with status %d.\n", adb ? "ADB" : "SD card", status); + if (status == INSTALL_REBOOT) { + return reboot_action; + } + 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; } @@ -841,6 +846,9 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Supported API: %d\n", kRecoveryApiVersion); int status = INSTALL_SUCCESS; + // next_action indicates the next target to reboot into upon finishing the install. It could be + // overridden to a different reboot target per user request. + Device::BuiltinAction next_action = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; if (update_package != nullptr) { // It's not entirely true that we will modify the flash. But we want @@ -924,19 +932,18 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); } - status = ApplyFromAdb(ui, false /* rescue_mode */); + status = ApplyFromAdb(ui, false /* rescue_mode */, &next_action); ui->Print("\nInstall from ADB complete (status: %d).\n", status); if (sideload_auto_reboot) { + status = INSTALL_REBOOT; ui->Print("Rebooting automatically.\n"); } } else if (fsck_unshare_blocks) { @@ -961,23 +968,26 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorIsTextVisible()) { - Device::BuiltinAction temp = prompt_and_wait(device, status); - if (temp != Device::NO_ACTION) { - after = temp; + // Determine the next action. + // - If the state is INSTALL_REBOOT, device will reboot into the target as specified in + // `next_action`. + // - If the recovery menu is visible, prompt and wait for commands. + // - If the state is INSTALL_NONE, wait for commands (e.g. in user build, one manually boots + // into recovery to sideload a package or to wipe the device). + // - In all other cases, reboot the device. Therefore, normal users will observe the device + // rebooting a) immediately upon successful finish (INSTALL_SUCCESS); or b) an "error" screen + // for 5s followed by an automatic reboot. + if (status != INSTALL_REBOOT) { + if (status == INSTALL_NONE || ui->IsTextVisible()) { + Device::BuiltinAction temp = prompt_and_wait(device, status); + if (temp != Device::NO_ACTION) { + next_action = temp; + } } } // Save logs and clean up before rebooting or shutting down. finish_recovery(); - return after; + return next_action; } diff --git a/recovery_main.cpp b/recovery_main.cpp index 37d9da0d..0eb2962e 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -477,6 +477,11 @@ int main(int argc, char** argv) { android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); break; + case Device::REBOOT_RESCUE: + ui->Print("Rebooting to rescue...\n"); + android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,rescue"); + break; + case Device::ENTER_FASTBOOT: if (logical_partitions_mapped()) { ui->Print("Partitions may be mounted - rebooting to enter fastboot."); diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 8f17639d..09b5d1f4 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -50,7 +50,11 @@ class Device { KEY_INTERRUPTED = 13, ENTER_FASTBOOT = 14, ENTER_RECOVERY = 15, + // ENTER vs REBOOT: The latter will trigger a reboot that uses `rescue` as the reboot target. + // So it goes from rescue -> bootloader -> rescue, whereas ENTER_RESCUE switches from recovery + // -> rescue directly. ENTER_RESCUE = 16, + REBOOT_RESCUE = 17, }; explicit Device(RecoveryUI* ui); From a14e860190ce7012e0ba75348d5a629bf2d6fe90 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 24 Apr 2019 14:03:52 -0700 Subject: [PATCH 016/171] Add TEST_MAPPING. Currently it triggers minadbd_test, recovery_unit_test and recovery_component_test during presubmit. Test: TreeHugger Change-Id: Ie0e2ddc9519a7e9b66d059cdf0b81dcbba631c25 --- TEST_MAPPING | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 TEST_MAPPING diff --git a/TEST_MAPPING b/TEST_MAPPING new file mode 100644 index 00000000..a7c34dda --- /dev/null +++ b/TEST_MAPPING @@ -0,0 +1,13 @@ +{ + "presubmit": [ + { + "name": "minadbd_test" + }, + { + "name": "recovery_unit_test" + }, + { + "name": "recovery_component_test" + } + ] +} From d9cb014d431fee946308bdb8979c8e8fa74b582f Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 23 Apr 2019 11:46:25 -0700 Subject: [PATCH 017/171] Parse BCB command to enter rescue mode. bootloader will set `boot-rescue` in BCB command field to indicate booting into rescue mode. This CL adds the matching parsing code. This CL changes the on-screen UI to display the default image while waiting for each sideload / rescue command. It also changes the minadbd reboot handlers to use REBOOT_ instead of the previous ENTER_ actions. This ensures a reboot going through bootloader, which may load a newly installed bootloader/recovery. Bug: 128505466 Test: Boot into rescue mode. Run `adb rescue getprop` and `adb rescue install`. Check the UI. Then run `adb reboot rescue`. Change-Id: I5b7de9dfd898ed8e14bea0d4ad7385a9bae26e94 --- install/adb_install.cpp | 24 +++++++++++------- recovery.cpp | 18 ++++++++++--- recovery_main.cpp | 32 +++++++++++++++++++++--- recovery_ui/include/recovery_ui/device.h | 11 +++++--- 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index d79f6f4b..9dfe0407 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -121,19 +121,20 @@ static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) { static auto AdbRebootHandler(MinadbdCommand command, int* result, Device::BuiltinAction* reboot_action) { + // Use Device::REBOOT_{FASTBOOT,RECOVERY,RESCUE}, instead of the ones with ENTER_. This allows + // rebooting back into fastboot/recovery/rescue mode through bootloader, which may use a newly + // installed bootloader/recovery image. switch (command) { case MinadbdCommand::kRebootBootloader: *reboot_action = Device::REBOOT_BOOTLOADER; break; case MinadbdCommand::kRebootFastboot: - *reboot_action = Device::ENTER_FASTBOOT; + *reboot_action = Device::REBOOT_FASTBOOT; break; case MinadbdCommand::kRebootRecovery: - *reboot_action = Device::ENTER_RECOVERY; + *reboot_action = Device::REBOOT_RECOVERY; break; case MinadbdCommand::kRebootRescue: - // Use Device::REBOOT_RESCUE instead of Device::ENTER_RESCUE. This allows rebooting back into - // rescue mode (potentially using a newly installed recovery image). *reboot_action = Device::REBOOT_RESCUE; break; case MinadbdCommand::kRebootAndroid: @@ -180,7 +181,7 @@ static bool HandleMessageFromMinadbd(int 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, + RecoveryUI* ui, pid_t minadbd_pid, android::base::unique_fd&& socket_fd, const std::map& command_map) { android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC)); if (epoll_fd == -1) { @@ -203,6 +204,10 @@ static void ListenAndExecuteMinadbdCommands( // Set the timeout to be 300s when waiting for minadbd commands. constexpr int TIMEOUT_MILLIS = 300 * 1000; while (true) { + // Reset the progress bar and the background image before each command. + ui->SetProgressType(RecoveryUI::EMPTY); + ui->SetBackground(RecoveryUI::NO_COMMAND); + // Poll for the status change of the socket_fd, and handle the message if the fd is ready to // read. int event_count = @@ -266,7 +271,8 @@ static void ListenAndExecuteMinadbdCommands( // b11. exit the listening loop // static void CreateMinadbdServiceAndExecuteCommands( - const std::map& command_map, bool rescue_mode) { + RecoveryUI* ui, const std::map& command_map, + bool rescue_mode) { signal(SIGPIPE, SIG_IGN); android::base::unique_fd recovery_socket; @@ -305,8 +311,8 @@ static void CreateMinadbdServiceAndExecuteCommands( return; } - std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket), - std::ref(command_map)); + std::thread listener_thread(ListenAndExecuteMinadbdCommands, ui, child, + std::move(recovery_socket), std::ref(command_map)); if (listener_thread.joinable()) { listener_thread.join(); } @@ -357,7 +363,7 @@ int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) }, }; - CreateMinadbdServiceAndExecuteCommands(command_map, rescue_mode); + CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode); // Clean up before switching to the older state, for example setting the state // to none sets sys/class/android_usb/android0/enable to 0. diff --git a/recovery.cpp b/recovery.cpp index 5bd9b172..f9b3bfc0 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -509,12 +509,14 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { case Device::NO_ACTION: break; - case Device::REBOOT: - case Device::SHUTDOWN: - case Device::REBOOT_BOOTLOADER: - case Device::REBOOT_RESCUE: case Device::ENTER_FASTBOOT: case Device::ENTER_RECOVERY: + case Device::REBOOT: + case Device::REBOOT_BOOTLOADER: + case Device::REBOOT_FASTBOOT: + case Device::REBOOT_RECOVERY: + case Device::REBOOT_RESCUE: + case Device::SHUTDOWN: return chosen_action; case Device::WIPE_DATA: @@ -728,6 +730,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Rebooting automatically.\n"); } + } else if (rescue) { + save_current_log = true; + status = ApplyFromAdb(ui, true /* rescue_mode */, &next_action); + ui->Print("\nInstall from ADB complete (status: %d).\n", status); } else if (fsck_unshare_blocks) { if (!do_fsck_unshare_blocks()) { status = INSTALL_ERROR; diff --git a/recovery_main.cpp b/recovery_main.cpp index 0eb2962e..6e69b700 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -155,9 +155,11 @@ static std::vector get_args(const int argc, char** const argv) { } // Finally, if no arguments were specified, check whether we should boot - // into fastboot. + // into fastboot or rescue mode. if (args.size() == 1 && boot_command == "boot-fastboot") { args.emplace_back("--fastboot"); + } else if (args.size() == 1 && boot_command == "boot-rescue") { + args.emplace_back("--rescue"); } return args; @@ -469,6 +471,7 @@ int main(int argc, char** argv) { switch (ret) { case Device::SHUTDOWN: ui->Print("Shutting down...\n"); + // TODO: Move all the reboots to reboot(), which should conditionally set quiescent flag. android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,"); break; @@ -477,11 +480,32 @@ int main(int argc, char** argv) { android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); break; - case Device::REBOOT_RESCUE: - ui->Print("Rebooting to rescue...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,rescue"); + case Device::REBOOT_FASTBOOT: + ui->Print("Rebooting to recovery/fastboot...\n"); + android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot"); break; + case Device::REBOOT_RECOVERY: + ui->Print("Rebooting to recovery...\n"); + reboot("reboot,recovery"); + break; + + case Device::REBOOT_RESCUE: { + // Not using `reboot("reboot,rescue")`, as it requires matching support in kernel and/or + // bootloader. + bootloader_message boot = {}; + strlcpy(boot.command, "boot-rescue", sizeof(boot.command)); + std::string err; + if (!write_bootloader_message(boot, &err)) { + LOG(ERROR) << "Failed to write bootloader message: " << err; + // Stay under recovery on failure. + continue; + } + ui->Print("Rebooting to recovery/rescue...\n"); + reboot("reboot,recovery"); + break; + } + case Device::ENTER_FASTBOOT: if (logical_partitions_mapped()) { ui->Print("Partitions may be mounted - rebooting to enter fastboot."); diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 09b5d1f4..7c76cdb0 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -33,6 +33,10 @@ class Device { static constexpr const int kHighlightDown = -3; static constexpr const int kInvokeItem = -4; + // ENTER vs REBOOT: The latter will trigger a reboot that goes through bootloader, which allows + // using a new bootloader / recovery image if applicable. For example, REBOOT_RESCUE goes from + // rescue -> bootloader -> rescue, whereas ENTER_RESCUE switches from recovery -> rescue + // directly. enum BuiltinAction { NO_ACTION = 0, REBOOT = 1, @@ -50,11 +54,10 @@ class Device { KEY_INTERRUPTED = 13, ENTER_FASTBOOT = 14, ENTER_RECOVERY = 15, - // ENTER vs REBOOT: The latter will trigger a reboot that uses `rescue` as the reboot target. - // So it goes from rescue -> bootloader -> rescue, whereas ENTER_RESCUE switches from recovery - // -> rescue directly. ENTER_RESCUE = 16, - REBOOT_RESCUE = 17, + REBOOT_FASTBOOT = 17, + REBOOT_RECOVERY = 18, + REBOOT_RESCUE = 19, }; explicit Device(RecoveryUI* ui); From 83186dddb20f3203ee4838ee99afc02dd5e6a357 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 24 Apr 2019 21:34:17 -0700 Subject: [PATCH 018/171] Add recovery_host_test to TEST_MAPPING. Also remove the AndroidTest.xml file, which is no longer needed (the `data` property in Android.bp takes care of that). The AndroidTest.xml file would otherwise block `atest recovery_host_test` from running. Test: TreeHugger; check the test result. Change-Id: If545878a1f3ae627986e19a94b42162f133b9098 --- TEST_MAPPING | 4 ++++ tests/Android.bp | 2 ++ tests/AndroidTest.xml | 33 --------------------------------- 3 files changed, 6 insertions(+), 33 deletions(-) delete mode 100644 tests/AndroidTest.xml diff --git a/TEST_MAPPING b/TEST_MAPPING index a7c34dda..c87ece24 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -8,6 +8,10 @@ }, { "name": "recovery_component_test" + }, + { + "name": "recovery_host_test", + "host": true } ] } diff --git a/tests/Android.bp b/tests/Android.bp index 09ef716d..2e5334d9 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -197,6 +197,8 @@ cc_test_host { "libz", ], + test_suites: ["general-tests"], + data: ["testdata/*"], target: { diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml deleted file mode 100644 index 6b86085a..00000000 --- a/tests/AndroidTest.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - From fedeef6f6d1f7b8f1e5a8b9e77f8dc21ef6b3c95 Mon Sep 17 00:00:00 2001 From: xunchang Date: Mon, 22 Apr 2019 12:18:14 -0700 Subject: [PATCH 019/171] Support wipe command in rescue mode Bug: 131037235 Test: unit tests pass, run `adb rescue wipe` Change-Id: I22668f2c98fe2d9195d2561f961c28a7c08e712c --- install/adb_install.cpp | 23 +++++++++----- install/include/install/adb_install.h | 3 +- minadbd/minadbd_services.cpp | 46 +++++++++++++++++++++++---- minadbd/minadbd_services_test.cpp | 2 +- minadbd/minadbd_types.h | 4 ++- recovery.cpp | 8 ++--- 6 files changed, 64 insertions(+), 22 deletions(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 9dfe0407..4dd1f1b0 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -43,6 +43,7 @@ #include "fuse_sideload.h" #include "install/install.h" +#include "install/wipe_data.h" #include "minadbd_types.h" #include "otautil/sysutil.h" #include "recovery_ui/device.h" @@ -330,7 +331,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action) { +int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) { // Save the usb state to restore after the sideload operation. std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); // Clean up state and stop adbd. @@ -339,13 +340,7 @@ int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot return INSTALL_ERROR; } - if (!rescue_mode) { - ui->Print( - "\n\nNow send the package you want to apply\n" - "to the device with \"adb sideload \"...\n"); - } else { - ui->Print("\n\nWaiting for rescue commands...\n"); - } + RecoveryUI* ui = device->GetUI(); int install_result = INSTALL_ERROR; std::map command_map{ @@ -363,6 +358,18 @@ int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) }, }; + if (!rescue_mode) { + ui->Print( + "\n\nNow send the package you want to apply\n" + "to the device with \"adb sideload \"...\n"); + } else { + ui->Print("\n\nWaiting for rescue commands...\n"); + command_map.emplace(MinadbdCommand::kWipeData, [&device]() { + bool result = WipeData(device, false); + return std::make_pair(result, true); + }); + } + CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode); // Clean up before switching to the older state, for example setting the state diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index 49b32b54..3a0a8174 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -17,9 +17,8 @@ #pragma once #include -#include // Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum // InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with // the reboot target set in reboot_action. -int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action); +int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action); diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 9b1999d9..1c4c0f49 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -104,7 +104,7 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, if (pieces.size() != 2 || !android::base::ParseInt(pieces[0], &file_size) || file_size <= 0 || !android::base::ParseInt(pieces[1], &block_size) || block_size <= 0) { LOG(ERROR) << "bad sideload-host arguments: " << args; - return kMinadbdPackageSizeError; + return kMinadbdHostCommandArgumentError; } LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size; @@ -124,17 +124,17 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, return kMinadbdMessageFormatError; } - // Signal host-side adb to stop. For sideload mode, we always send kSideloadServiceExitSuccess + // Signal host-side adb to stop. For sideload mode, we always send kMinadbdServicesExitSuccess // (i.e. "DONEDONE") regardless of the install result. For rescue mode, we send failure message on // install error. if (!rescue_mode || *status == MinadbdCommandStatus::kSuccess) { - if (!android::base::WriteFully(sfd, kSideloadServiceExitSuccess, - strlen(kSideloadServiceExitSuccess))) { + if (!android::base::WriteFully(sfd, kMinadbdServicesExitSuccess, + strlen(kMinadbdServicesExitSuccess))) { return kMinadbdHostSocketIOError; } } else { - if (!android::base::WriteFully(sfd, kSideloadServiceExitFailure, - strlen(kSideloadServiceExitFailure))) { + if (!android::base::WriteFully(sfd, kMinadbdServicesExitFailure, + strlen(kMinadbdServicesExitFailure))) { return kMinadbdHostSocketIOError; } } @@ -200,6 +200,34 @@ static void RebootHostService(unique_fd /* sfd */, const std::string& target) { } } +static void WipeDeviceService(unique_fd fd, const std::string& args) { + auto pieces = android::base::Split(args, ":"); + if (pieces.size() != 2 || pieces[0] != "userdata") { + LOG(ERROR) << "Failed to parse wipe device command arguments " << args; + exit(kMinadbdHostCommandArgumentError); + } + + size_t message_size; + if (!android::base::ParseUint(pieces[1], &message_size) || + message_size < strlen(kMinadbdServicesExitSuccess)) { + LOG(ERROR) << "Failed to parse wipe device message size in " << args; + exit(kMinadbdHostCommandArgumentError); + } + + WriteCommandToFd(MinadbdCommand::kWipeData, minadbd_socket); + MinadbdCommandStatus status; + if (!WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } + + std::string response = (status == MinadbdCommandStatus::kSuccess) ? kMinadbdServicesExitSuccess + : kMinadbdServicesExitFailure; + response += std::string(message_size - response.size(), '\0'); + if (!android::base::WriteFully(fd, response.c_str(), response.size())) { + exit(kMinadbdHostSocketIOError); + } +} + unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { // Common services that are supported both in sideload and rescue modes. if (ConsumePrefix(&name, "reboot:")) { @@ -225,7 +253,13 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport * std::string args(name); return create_service_thread( "rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args)); + } else if (ConsumePrefix(&name, "rescue-wipe:")) { + // rescue-wipe:target: + std::string args(name); + return create_service_thread("rescue-wipe", + std::bind(WipeDeviceService, std::placeholders::_1, args)); } + return unique_fd{}; } diff --git a/minadbd/minadbd_services_test.cpp b/minadbd/minadbd_services_test.cpp index 593180bb..f8787379 100644 --- a/minadbd/minadbd_services_test.cpp +++ b/minadbd/minadbd_services_test.cpp @@ -122,7 +122,7 @@ class MinadbdServicesTest : public ::testing::Test { TEST_F(MinadbdServicesTest, SideloadHostService_wrong_size_argument) { ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:abc:4096"), - ::testing::ExitedWithCode(kMinadbdPackageSizeError), ""); + ::testing::ExitedWithCode(kMinadbdHostCommandArgumentError), ""); } TEST_F(MinadbdServicesTest, SideloadHostService_wrong_block_size) { diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h index b370b795..99fd45e8 100644 --- a/minadbd/minadbd_types.h +++ b/minadbd/minadbd_types.h @@ -30,7 +30,7 @@ enum MinadbdErrorCode : int { kMinadbdSocketIOError = 2, kMinadbdMessageFormatError = 3, kMinadbdAdbVersionError = 4, - kMinadbdPackageSizeError = 5, + kMinadbdHostCommandArgumentError = 5, kMinadbdFuseStartError = 6, kMinadbdUnsupportedCommandError = 7, kMinadbdCommandExecutionError = 8, @@ -51,6 +51,8 @@ enum class MinadbdCommand : uint32_t { kRebootFastboot = 4, kRebootRecovery = 5, kRebootRescue = 6, + kWipeCache = 7, + kWipeData = 8, // Last but invalid command. kError, diff --git a/recovery.cpp b/recovery.cpp index f9b3bfc0..5fc673ec 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -551,9 +551,9 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { if (chosen_action == Device::ENTER_RESCUE) { // Switch to graphics screen. ui->ShowText(false); - status = ApplyFromAdb(ui, true /* rescue_mode */, &reboot_action); + status = ApplyFromAdb(device, true /* rescue_mode */, &reboot_action); } else if (chosen_action == Device::APPLY_ADB_SIDELOAD) { - status = ApplyFromAdb(ui, false /* rescue_mode */, &reboot_action); + status = ApplyFromAdb(device, false /* rescue_mode */, &reboot_action); } else { adb = false; status = ApplyFromSdcard(device, ui); @@ -946,7 +946,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); } - status = ApplyFromAdb(ui, false /* rescue_mode */, &next_action); + status = ApplyFromAdb(device, false /* rescue_mode */, &next_action); ui->Print("\nInstall from ADB complete (status: %d).\n", status); if (sideload_auto_reboot) { status = INSTALL_REBOOT; @@ -954,7 +954,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("\nInstall from ADB complete (status: %d).\n", status); } else if (fsck_unshare_blocks) { if (!do_fsck_unshare_blocks()) { From 7f19d100b5ed76e8c51155b5e2698cf5802e1da6 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 26 Apr 2019 22:56:56 -0700 Subject: [PATCH 020/171] Add install/wipe_device.cpp. Prior to this CL, GetWipePartitionList was declared in install.h (libinstall) but defined in recovery.cpp (librecovery). This CL addresses the issue by refactoring wipe-device related functions into install/wipe_device.cpp. Test: atest recovery_component_test Change-Id: I7ebe04ccfda3d793e085403560a0a202752d9ee3 --- install/Android.bp | 1 + install/include/install/install.h | 3 - install/include/install/wipe_device.h | 29 ++++ install/wipe_device.cpp | 198 ++++++++++++++++++++++++++ recovery.cpp | 169 +--------------------- tests/component/install_test.cpp | 1 + 6 files changed, 233 insertions(+), 168 deletions(-) create mode 100644 install/include/install/wipe_device.h create mode 100644 install/wipe_device.cpp diff --git a/install/Android.bp b/install/Android.bp index b18e2902..4696e501 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -66,6 +66,7 @@ cc_library_static { "package.cpp", "verifier.cpp", "wipe_data.cpp", + "wipe_device.cpp", ], shared_libs: [ diff --git a/install/include/install/install.h b/install/include/install/install.h index c0a8f1f4..d90c20f3 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -58,9 +58,6 @@ bool verify_package(Package* package, RecoveryUI* ui); // result to |metadata|. Return true if succeed, otherwise return false. bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map* metadata); -// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe. -std::vector 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); diff --git a/install/include/install/wipe_device.h b/install/include/install/wipe_device.h new file mode 100644 index 00000000..c60b9999 --- /dev/null +++ b/install/include/install/wipe_device.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "install/package.h" +#include "recovery_ui/device.h" + +// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE. +bool WipeAbDevice(Device* device, size_t wipe_package_size); + +// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe. +std::vector GetWipePartitionList(Package* wipe_package); diff --git a/install/wipe_device.cpp b/install/wipe_device.cpp new file mode 100644 index 00000000..72b96f7b --- /dev/null +++ b/install/wipe_device.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "install/wipe_device.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "bootloader_message/bootloader_message.h" +#include "install/install.h" +#include "install/package.h" +#include "recovery_ui/device.h" +#include "recovery_ui/ui.h" + +std::vector GetWipePartitionList(Package* wipe_package) { + ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle(); + if (!zip) { + LOG(ERROR) << "Failed to get ZipArchiveHandle"; + return {}; + } + + constexpr char RECOVERY_WIPE_ENTRY_NAME[] = "recovery.wipe"; + + std::string partition_list_content; + 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(partition_list_content.data()), length); + err != 0) { + LOG(ERROR) << "Failed to extract " << RECOVERY_WIPE_ENTRY_NAME << ": " + << ErrorCodeString(err); + return {}; + } + } else { + LOG(INFO) << "Failed to find " << RECOVERY_WIPE_ENTRY_NAME + << ", falling back to use the partition list on device."; + + constexpr char RECOVERY_WIPE_ON_DEVICE[] = "/etc/recovery.wipe"; + if (!android::base::ReadFileToString(RECOVERY_WIPE_ON_DEVICE, &partition_list_content)) { + PLOG(ERROR) << "failed to read \"" << RECOVERY_WIPE_ON_DEVICE << "\""; + return {}; + } + } + + std::vector result; + auto lines = android::base::Split(partition_list_content, "\n"); + for (const auto& line : lines) { + auto partition = android::base::Trim(line); + // Ignore '#' comment or empty lines. + if (android::base::StartsWith(partition, "#") || partition.empty()) { + continue; + } + result.push_back(line); + } + + return result; +} + +// Secure-wipes a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with +// BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT. +static bool SecureWipePartition(const std::string& partition) { + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY))); + if (fd == -1) { + PLOG(ERROR) << "Failed to open \"" << partition << "\""; + return false; + } + + uint64_t range[2] = { 0, 0 }; + if (ioctl(fd, BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) { + PLOG(ERROR) << "Failed to get partition size"; + return false; + } + LOG(INFO) << "Secure-wiping \"" << partition << "\" from " << range[0] << " to " << range[1]; + + LOG(INFO) << " Trying BLKSECDISCARD..."; + if (ioctl(fd, BLKSECDISCARD, &range) == -1) { + PLOG(WARNING) << " Failed"; + + // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT. + unsigned int zeroes; + if (ioctl(fd, BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) { + LOG(INFO) << " Trying BLKDISCARD..."; + if (ioctl(fd, BLKDISCARD, &range) == -1) { + PLOG(ERROR) << " Failed"; + return false; + } + } else { + LOG(INFO) << " Trying BLKZEROOUT..."; + if (ioctl(fd, BLKZEROOUT, &range) == -1) { + PLOG(ERROR) << " Failed"; + return false; + } + } + } + + LOG(INFO) << " Done"; + return true; +} + +static std::unique_ptr ReadWipePackage(size_t wipe_package_size) { + if (wipe_package_size == 0) { + LOG(ERROR) << "wipe_package_size is zero"; + return nullptr; + } + + std::string wipe_package; + if (std::string err_str; !read_wipe_package(&wipe_package, wipe_package_size, &err_str)) { + PLOG(ERROR) << "Failed to read wipe package" << err_str; + return nullptr; + } + + return Package::CreateMemoryPackage( + std::vector(wipe_package.begin(), wipe_package.end()), nullptr); +} + +// Checks if the wipe package matches expectation. If the check passes, reads the list of +// partitions to wipe from the package. Checks include +// 1. verify the package. +// 2. check metadata (ota-type, pre-device and serial number if having one). +static bool CheckWipePackage(Package* wipe_package, RecoveryUI* ui) { + if (!verify_package(wipe_package, ui)) { + LOG(ERROR) << "Failed to verify package"; + return false; + } + + ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle(); + if (!zip) { + LOG(ERROR) << "Failed to get ZipArchiveHandle"; + return false; + } + + std::map metadata; + if (!ReadMetadataFromPackage(zip, &metadata)) { + LOG(ERROR) << "Failed to parse metadata in the zip file"; + return false; + } + + return CheckPackageMetadata(metadata, OtaType::BRICK) == 0; +} + +bool WipeAbDevice(Device* device, size_t wipe_package_size) { + auto ui = device->GetUI(); + ui->SetBackground(RecoveryUI::ERASING); + ui->SetProgressType(RecoveryUI::INDETERMINATE); + + auto wipe_package = ReadWipePackage(wipe_package_size); + if (!wipe_package) { + LOG(ERROR) << "Failed to open wipe package"; + return false; + } + + if (!CheckWipePackage(wipe_package.get(), ui)) { + LOG(ERROR) << "Failed to verify wipe package"; + return false; + } + + auto partition_list = GetWipePartitionList(wipe_package.get()); + if (partition_list.empty()) { + LOG(ERROR) << "Empty wipe ab partition list"; + return false; + } + + for (const auto& partition : partition_list) { + // Proceed anyway even if it fails to wipe some partition. + SecureWipePartition(partition); + } + return true; +} diff --git a/recovery.cpp b/recovery.cpp index f9b3bfc0..30d8fbfa 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -18,11 +18,9 @@ #include #include -#include #include #include #include -#include #include #include #include @@ -30,8 +28,8 @@ #include #include -#include #include +#include #include #include #include @@ -42,12 +40,11 @@ #include #include #include -#include -#include #include /* for property_list */ #include #include +#include "bootloader_message/bootloader_message.h" #include "common.h" #include "fsck_unshare_blocks.h" #include "install/adb_install.h" @@ -55,6 +52,7 @@ #include "install/install.h" #include "install/package.h" #include "install/wipe_data.h" +#include "install/wipe_device.h" #include "otautil/error_code.h" #include "otautil/logging.h" #include "otautil/paths.h" @@ -221,165 +219,6 @@ static InstallResult prompt_and_wipe_data(Device* device) { } } -// Secure-wipe a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with -// BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT. -static bool secure_wipe_partition(const std::string& partition) { - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY))); - if (fd == -1) { - PLOG(ERROR) << "Failed to open \"" << partition << "\""; - return false; - } - - uint64_t range[2] = { 0, 0 }; - if (ioctl(fd, BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) { - PLOG(ERROR) << "Failed to get partition size"; - return false; - } - LOG(INFO) << "Secure-wiping \"" << partition << "\" from " << range[0] << " to " << range[1]; - - LOG(INFO) << " Trying BLKSECDISCARD..."; - if (ioctl(fd, BLKSECDISCARD, &range) == -1) { - PLOG(WARNING) << " Failed"; - - // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT. - unsigned int zeroes; - if (ioctl(fd, BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) { - LOG(INFO) << " Trying BLKDISCARD..."; - if (ioctl(fd, BLKDISCARD, &range) == -1) { - PLOG(ERROR) << " Failed"; - return false; - } - } else { - LOG(INFO) << " Trying BLKZEROOUT..."; - if (ioctl(fd, BLKZEROOUT, &range) == -1) { - PLOG(ERROR) << " Failed"; - return false; - } - } - } - - LOG(INFO) << " Done"; - return true; -} - -static std::unique_ptr 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(wipe_package.begin(), wipe_package.end()), nullptr); -} - -// Checks if the wipe package matches expectation. If the check passes, reads the list of -// partitions to wipe from the package. Checks include -// 1. verify the package. -// 2. check metadata (ota-type, pre-device and serial number if having one). -static bool CheckWipePackage(Package* wipe_package) { - if (!verify_package(wipe_package, ui)) { - LOG(ERROR) << "Failed to verify package"; - return false; - } - - ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle(); - if (!zip) { - LOG(ERROR) << "Failed to get ZipArchiveHandle"; - return false; - } - - std::map 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 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(partition_list_content.data()), length); - err != 0) { - LOG(ERROR) << "Failed to extract " << RECOVERY_WIPE_ENTRY_NAME << ": " - << ErrorCodeString(err); - return {}; - } - } else { - LOG(INFO) << "Failed to find " << RECOVERY_WIPE_ENTRY_NAME - << ", falling back to use the partition list on device."; - - static constexpr const char* RECOVERY_WIPE_ON_DEVICE = "/etc/recovery.wipe"; - if (!android::base::ReadFileToString(RECOVERY_WIPE_ON_DEVICE, &partition_list_content)) { - PLOG(ERROR) << "failed to read \"" << RECOVERY_WIPE_ON_DEVICE << "\""; - return {}; - } - } - - std::vector result; - std::vector lines = android::base::Split(partition_list_content, "\n"); - for (const std::string& line : lines) { - std::string partition = android::base::Trim(line); - // Ignore '#' comment or empty lines. - if (android::base::StartsWith(partition, "#") || partition.empty()) { - continue; - } - result.push_back(line); - } - - return result; -} - -// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE. -static bool wipe_ab_device(size_t wipe_package_size) { - ui->SetBackground(RecoveryUI::ERASING); - ui->SetProgressType(RecoveryUI::INDETERMINATE); - - auto wipe_package = ReadWipePackage(wipe_package_size); - if (!wipe_package) { - LOG(ERROR) << "Failed to open wipe package"; - return false; - } - - if (!CheckWipePackage(wipe_package.get())) { - LOG(ERROR) << "Failed to verify wipe package"; - return false; - } - - auto partition_list = GetWipePartitionList(wipe_package.get()); - if (partition_list.empty()) { - LOG(ERROR) << "Empty wipe ab partition list"; - return false; - } - - for (const auto& partition : partition_list) { - // Proceed anyway even if it fails to wipe some partition. - secure_wipe_partition(partition); - } - return true; -} - static void choose_recovery_file(Device* device) { std::vector entries; if (has_cache) { @@ -934,7 +773,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector #include "install/install.h" +#include "install/wipe_device.h" #include "otautil/paths.h" #include "private/setup_commands.h" From 782dcc1996f6b39ef9e085d7153c2f109ff2609a Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 29 Apr 2019 11:23:16 -0700 Subject: [PATCH 021/171] Consolidate the codes that handle reboot/shutdown. Test: Choose `Reboot system now`, `Power off`, `Reboot to bootloader` from recovery UI respectively. Test: `adb reboot recovery` while under sideload mode. Change-Id: I0f3d55b80b472178ea4f6970b29cd9df0778b639 --- otautil/include/otautil/sysutil.h | 15 ++++++++------- otautil/sysutil.cpp | 13 ++++++++++--- recovery.cpp | 4 ++-- recovery_main.cpp | 18 ++++++++---------- recovery_ui/ui.cpp | 2 +- updater/install.cpp | 2 +- 6 files changed, 30 insertions(+), 24 deletions(-) diff --git a/otautil/include/otautil/sysutil.h b/otautil/include/otautil/sysutil.h index 692a99e9..48e9011e 100644 --- a/otautil/include/otautil/sysutil.h +++ b/otautil/include/otautil/sysutil.h @@ -14,12 +14,12 @@ * limitations under the License. */ -#ifndef _OTAUTIL_SYSUTIL -#define _OTAUTIL_SYSUTIL +#pragma once #include #include +#include #include #include "rangeset.h" @@ -101,13 +101,14 @@ class MemMapping { std::vector ranges_; }; -// Wrapper function to trigger a reboot, by additionally handling quiescent reboot mode. The -// command should start with "reboot," (e.g. "reboot,bootloader" or "reboot,"). -bool reboot(const std::string& command); +// Reboots the device into the specified target, by additionally handling quiescent reboot mode. +// 'target' can be an empty string, which indicates booting into Android. +bool Reboot(std::string_view target); + +// Triggers a shutdown. +bool Shutdown(); // Returns a null-terminated char* array, where the elements point to the C-strings in the given // vector, plus an additional nullptr at the end. This is a helper function that facilitates // calling C functions (such as getopt(3)) that expect an array of C-strings. std::vector StringVectorToNullTerminatedArray(const std::vector& args); - -#endif // _OTAUTIL_SYSUTIL diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp index 8366fa0a..2b486180 100644 --- a/otautil/sysutil.cpp +++ b/otautil/sysutil.cpp @@ -214,14 +214,21 @@ MemMapping::~MemMapping() { ranges_.clear(); } -bool reboot(const std::string& command) { - std::string cmd = command; - if (android::base::GetBoolProperty("ro.boot.quiescent", false)) { +bool Reboot(std::string_view target) { + std::string cmd = "reboot," + std::string(target); + // Honor the quiescent mode if applicable. + if (target != "bootloader" && target != "fastboot" && + android::base::GetBoolProperty("ro.boot.quiescent", false)) { cmd += ",quiescent"; } return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd); } +bool Shutdown() { + // "shutdown" doesn't need a "reason" arg nor a comma. + return android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown"); +} + std::vector StringVectorToNullTerminatedArray(const std::vector& args) { std::vector result(args.size()); std::transform(args.cbegin(), args.cend(), result.begin(), diff --git a/recovery.cpp b/recovery.cpp index 5fc673ec..42dad412 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -895,8 +895,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Retry attempt %d\n", retry_count); - // Reboot and retry the update - if (!reboot("reboot,recovery")) { + // Reboot back into recovery to retry the update. + if (!Reboot("recovery")) { ui->Print("Reboot failed\n"); } else { while (true) { diff --git a/recovery_main.cpp b/recovery_main.cpp index 6e69b700..b999505f 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -41,7 +41,6 @@ #include #include #include -#include #include #include /* private pmsg functions */ #include @@ -471,27 +470,26 @@ int main(int argc, char** argv) { switch (ret) { case Device::SHUTDOWN: ui->Print("Shutting down...\n"); - // TODO: Move all the reboots to reboot(), which should conditionally set quiescent flag. - android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,"); + Shutdown(); break; case Device::REBOOT_BOOTLOADER: ui->Print("Rebooting to bootloader...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); + Reboot("bootloader"); break; case Device::REBOOT_FASTBOOT: ui->Print("Rebooting to recovery/fastboot...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot"); + Reboot("fastboot"); break; case Device::REBOOT_RECOVERY: ui->Print("Rebooting to recovery...\n"); - reboot("reboot,recovery"); + Reboot("recovery"); break; case Device::REBOOT_RESCUE: { - // Not using `reboot("reboot,rescue")`, as it requires matching support in kernel and/or + // Not using `Reboot("rescue")`, as it requires matching support in kernel and/or // bootloader. bootloader_message boot = {}; strlcpy(boot.command, "boot-rescue", sizeof(boot.command)); @@ -502,14 +500,14 @@ int main(int argc, char** argv) { continue; } ui->Print("Rebooting to recovery/rescue...\n"); - reboot("reboot,recovery"); + Reboot("recovery"); break; } case Device::ENTER_FASTBOOT: if (logical_partitions_mapped()) { ui->Print("Partitions may be mounted - rebooting to enter fastboot."); - android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot"); + Reboot("fastboot"); } else { LOG(INFO) << "Entering fastboot"; fastboot = true; @@ -523,7 +521,7 @@ int main(int argc, char** argv) { default: ui->Print("Rebooting...\n"); - reboot("reboot,"); + Reboot(""); break; } } diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index b7107ff2..7ea9307c 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -375,7 +375,7 @@ void RecoveryUI::ProcessKey(int key_code, int updown) { case RecoveryUI::REBOOT: if (reboot_enabled) { - reboot("reboot,"); + Reboot(""); while (true) { pause(); } diff --git a/updater/install.cpp b/updater/install.cpp index 20a204a8..8eba64f5 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -778,7 +778,7 @@ Value* RebootNowFn(const char* name, State* state, const std::vector Date: Fri, 26 Apr 2019 23:41:49 -0700 Subject: [PATCH 022/171] tests: Merge recovery_component_test into recovery_unit_test. Most of the tests in component/ are in fact unit tests. And it doesn't look practically beneficial to distinguish between the two: - They have the same test setup; - We always run both (recovery_unit_test and recovery_component_test) at the same time; - Breaking any of them would be equally bad. This CL merges the tests in recovery_component_test into recovery_unit_test to save the effort to maintain both. Test: Run recovery_unit_test on marlin (via `adb sync data`). Test: `atest recovery_unit_test` Change-Id: I93ff32e7219cd83425a4bcfe5613978a8dd48d75 --- CleanSpec.mk | 4 + README.md | 2 - TEST_MAPPING | 3 - tests/Android.bp | 45 ++----- tests/component/resources_test.cpp | 120 ------------------ .../applypatch_modes_test.cpp | 0 .../bootloader_message_test.cpp | 0 tests/{component => unit}/edify_test.cpp | 0 .../fuse_sideload_test.cpp} | 0 tests/{component => unit}/imgdiff_test.cpp | 0 tests/{component => unit}/install_test.cpp | 0 tests/unit/resources_test.cpp | 101 ++++++++++++++- tests/{component => unit}/uncrypt_test.cpp | 0 .../update_verifier_test.cpp | 0 tests/{component => unit}/updater_test.cpp | 0 tests/{component => unit}/verifier_test.cpp | 0 16 files changed, 116 insertions(+), 159 deletions(-) delete mode 100644 tests/component/resources_test.cpp rename tests/{component => unit}/applypatch_modes_test.cpp (100%) rename tests/{component => unit}/bootloader_message_test.cpp (100%) rename tests/{component => unit}/edify_test.cpp (100%) rename tests/{component/sideload_test.cpp => unit/fuse_sideload_test.cpp} (100%) rename tests/{component => unit}/imgdiff_test.cpp (100%) rename tests/{component => unit}/install_test.cpp (100%) rename tests/{component => unit}/uncrypt_test.cpp (100%) rename tests/{component => unit}/update_verifier_test.cpp (100%) rename tests/{component => unit}/updater_test.cpp (100%) rename tests/{component => unit}/verifier_test.cpp (100%) diff --git a/CleanSpec.mk b/CleanSpec.mk index a7ab0d9b..6bd1eb17 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -51,6 +51,10 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libinstall.recovery_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/system/lib64/libinstall.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest/recovery_component_test) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest64/recovery_component_test) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/testcases/recovery_component_test) + # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/README.md b/README.md index efcd318b..0ccc10b5 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,9 @@ Running the tests # 32-bit device adb shell /data/nativetest/recovery_unit_test/recovery_unit_test - adb shell /data/nativetest/recovery_component_test/recovery_component_test # Or 64-bit device adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test - adb shell /data/nativetest64/recovery_component_test/recovery_component_test Running the manual tests ------------------------ diff --git a/TEST_MAPPING b/TEST_MAPPING index c87ece24..a3045828 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -6,9 +6,6 @@ { "name": "recovery_unit_test" }, - { - "name": "recovery_component_test" - }, { "name": "recovery_host_test", "host": true diff --git a/tests/Android.bp b/tests/Android.bp index 2e5334d9..ec2124a5 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -79,6 +79,8 @@ librecovery_static_libs = [ "libinstall", "librecovery_ui", "libminui", + "libfusesideload", + "libbootloader_message", "libotautil", "libhealthhalutils", @@ -87,10 +89,8 @@ librecovery_static_libs = [ "android.hardware.health@2.0", "android.hardware.health@1.0", - "libbootloader_message", "libext4_utils", "libfs_mgr", - "libfusesideload", "libhidl-gen-utils", "libhidlbase", "libhidltransport", @@ -107,6 +107,7 @@ cc_test { defaults: [ "recovery_test_defaults", + "libupdater_defaults", ], test_suites: ["device-tests"], @@ -115,16 +116,21 @@ cc_test { "unit/*.cpp", ], - static_libs: libapplypatch_static_libs + [ - "libinstall", + static_libs: libapplypatch_static_libs + librecovery_static_libs + [ "librecovery_ui", "libminui", "libotautil", "libupdater", + "libupdate_verifier", + "libgtest_prod", + "libprotobuf-cpp-lite", ], - data: ["testdata/*"], + data: [ + "testdata/*", + ":res-testdata", + ], } cc_test { @@ -142,33 +148,6 @@ cc_test { ], } -cc_test { - name: "recovery_component_test", - isolated: true, - - defaults: [ - "recovery_test_defaults", - "libupdater_defaults", - ], - - test_suites: ["device-tests"], - - srcs: [ - "component/*.cpp", - ], - - static_libs: libapplypatch_static_libs + librecovery_static_libs + [ - "libupdater", - "libupdate_verifier", - "libprotobuf-cpp-lite", - ], - - data: [ - "testdata/*", - ":res-testdata", - ], -} - cc_test_host { name: "recovery_host_test", isolated: true, @@ -178,7 +157,7 @@ cc_test_host { ], srcs: [ - "component/imgdiff_test.cpp", + "unit/imgdiff_test.cpp", ], static_libs: [ diff --git a/tests/component/resources_test.cpp b/tests/component/resources_test.cpp deleted file mode 100644 index d7fdb8fa..00000000 --- a/tests/component/resources_test.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "minui/minui.h" -#include "private/resources.h" - -static const std::string kLocale = "zu"; - -static const std::vector kResourceImagesDirs{ - "res-mdpi/images/", "res-hdpi/images/", "res-xhdpi/images/", - "res-xxhdpi/images/", "res-xxxhdpi/images/", -}; - -static int png_filter(const dirent* de) { - if (de->d_type != DT_REG || !android::base::EndsWith(de->d_name, "_text.png")) { - return 0; - } - return 1; -} - -// Finds out all the PNG files to test, which stay under the same dir with the executabl.. -static std::vector add_files() { - std::vector files; - for (const std::string& images_dir : kResourceImagesDirs) { - static std::string exec_dir = android::base::GetExecutableDirectory(); - std::string dir_path = exec_dir + "/" + images_dir; - dirent** namelist; - int n = scandir(dir_path.c_str(), &namelist, png_filter, alphasort); - if (n == -1) { - printf("Failed to scandir %s: %s\n", dir_path.c_str(), strerror(errno)); - continue; - } - if (n == 0) { - printf("No file is added for test in %s\n", dir_path.c_str()); - } - - while (n--) { - std::string file_path = dir_path + namelist[n]->d_name; - files.push_back(file_path); - free(namelist[n]); - } - free(namelist); - } - return files; -} - -class ResourcesTest : public testing::TestWithParam { - public: - static std::vector png_list; - - protected: - void SetUp() override { - png_ = std::make_unique(GetParam()); - ASSERT_TRUE(png_); - - ASSERT_EQ(PNG_COLOR_TYPE_GRAY, png_->color_type()) << "Recovery expects grayscale PNG file."; - ASSERT_LT(static_cast(5), png_->width()); - ASSERT_LT(static_cast(0), png_->height()); - ASSERT_EQ(1, png_->channels()) << "Recovery background text images expects 1-channel PNG file."; - } - - std::unique_ptr png_{ nullptr }; -}; - -// Parses a png file and tests if it's qualified for the background text image under recovery. -TEST_P(ResourcesTest, ValidateLocale) { - std::vector row(png_->width()); - for (png_uint_32 y = 0; y < png_->height(); ++y) { - png_read_row(png_->png_ptr(), row.data(), nullptr); - int w = (row[1] << 8) | row[0]; - int h = (row[3] << 8) | row[2]; - int len = row[4]; - EXPECT_LT(0, w); - EXPECT_LT(0, h); - EXPECT_LT(0, len) << "Locale string should be non-empty."; - EXPECT_NE(0, row[5]) << "Locale string is missing."; - - ASSERT_GE(png_->height(), y + 1 + h) << "Locale: " << kLocale << " is not found in the file."; - char* loc = reinterpret_cast(&row[5]); - if (matches_locale(loc, kLocale.c_str())) { - EXPECT_TRUE(android::base::StartsWith(loc, kLocale)); - break; - } - for (int i = 0; i < h; ++i, ++y) { - png_read_row(png_->png_ptr(), row.data(), nullptr); - } - } -} - -std::vector ResourcesTest::png_list = add_files(); - -INSTANTIATE_TEST_CASE_P(BackgroundTextValidation, ResourcesTest, - ::testing::ValuesIn(ResourcesTest::png_list.cbegin(), - ResourcesTest::png_list.cend())); diff --git a/tests/component/applypatch_modes_test.cpp b/tests/unit/applypatch_modes_test.cpp similarity index 100% rename from tests/component/applypatch_modes_test.cpp rename to tests/unit/applypatch_modes_test.cpp diff --git a/tests/component/bootloader_message_test.cpp b/tests/unit/bootloader_message_test.cpp similarity index 100% rename from tests/component/bootloader_message_test.cpp rename to tests/unit/bootloader_message_test.cpp diff --git a/tests/component/edify_test.cpp b/tests/unit/edify_test.cpp similarity index 100% rename from tests/component/edify_test.cpp rename to tests/unit/edify_test.cpp diff --git a/tests/component/sideload_test.cpp b/tests/unit/fuse_sideload_test.cpp similarity index 100% rename from tests/component/sideload_test.cpp rename to tests/unit/fuse_sideload_test.cpp diff --git a/tests/component/imgdiff_test.cpp b/tests/unit/imgdiff_test.cpp similarity index 100% rename from tests/component/imgdiff_test.cpp rename to tests/unit/imgdiff_test.cpp diff --git a/tests/component/install_test.cpp b/tests/unit/install_test.cpp similarity index 100% rename from tests/component/install_test.cpp rename to tests/unit/install_test.cpp diff --git a/tests/unit/resources_test.cpp b/tests/unit/resources_test.cpp index c3f72718..30274430 100644 --- a/tests/unit/resources_test.cpp +++ b/tests/unit/resources_test.cpp @@ -14,12 +14,62 @@ * limitations under the License. */ -#include +#include +#include +#include +#include +#include +#include + +#include +#include #include +#include #include "common/test_constants.h" #include "minui/minui.h" +#include "private/resources.h" + +static const std::string kLocale = "zu"; + +static const std::vector kResourceImagesDirs{ + "res-mdpi/images/", "res-hdpi/images/", "res-xhdpi/images/", + "res-xxhdpi/images/", "res-xxxhdpi/images/", +}; + +static int png_filter(const dirent* de) { + if (de->d_type != DT_REG || !android::base::EndsWith(de->d_name, "_text.png")) { + return 0; + } + return 1; +} + +// Finds out all the PNG files to test, which stay under the same dir with the executabl.. +static std::vector add_files() { + std::vector files; + for (const std::string& images_dir : kResourceImagesDirs) { + static std::string exec_dir = android::base::GetExecutableDirectory(); + std::string dir_path = exec_dir + "/" + images_dir; + dirent** namelist; + int n = scandir(dir_path.c_str(), &namelist, png_filter, alphasort); + if (n == -1) { + printf("Failed to scandir %s: %s\n", dir_path.c_str(), strerror(errno)); + continue; + } + if (n == 0) { + printf("No file is added for test in %s\n", dir_path.c_str()); + } + + while (n--) { + std::string file_path = dir_path + namelist[n]->d_name; + files.push_back(file_path); + free(namelist[n]); + } + free(namelist); + } + return files; +} TEST(ResourcesTest, res_create_multi_display_surface) { GRSurface** frames; @@ -35,3 +85,52 @@ TEST(ResourcesTest, res_create_multi_display_surface) { } free(frames); } + +class ResourcesTest : public testing::TestWithParam { + public: + static std::vector png_list; + + protected: + void SetUp() override { + png_ = std::make_unique(GetParam()); + ASSERT_TRUE(png_); + + ASSERT_EQ(PNG_COLOR_TYPE_GRAY, png_->color_type()) << "Recovery expects grayscale PNG file."; + ASSERT_LT(static_cast(5), png_->width()); + ASSERT_LT(static_cast(0), png_->height()); + ASSERT_EQ(1, png_->channels()) << "Recovery background text images expects 1-channel PNG file."; + } + + std::unique_ptr png_{ nullptr }; +}; + +// Parses a png file and tests if it's qualified for the background text image under recovery. +TEST_P(ResourcesTest, ValidateLocale) { + std::vector row(png_->width()); + for (png_uint_32 y = 0; y < png_->height(); ++y) { + png_read_row(png_->png_ptr(), row.data(), nullptr); + int w = (row[1] << 8) | row[0]; + int h = (row[3] << 8) | row[2]; + int len = row[4]; + EXPECT_LT(0, w); + EXPECT_LT(0, h); + EXPECT_LT(0, len) << "Locale string should be non-empty."; + EXPECT_NE(0, row[5]) << "Locale string is missing."; + + ASSERT_GE(png_->height(), y + 1 + h) << "Locale: " << kLocale << " is not found in the file."; + char* loc = reinterpret_cast(&row[5]); + if (matches_locale(loc, kLocale.c_str())) { + EXPECT_TRUE(android::base::StartsWith(loc, kLocale)); + break; + } + for (int i = 0; i < h; ++i, ++y) { + png_read_row(png_->png_ptr(), row.data(), nullptr); + } + } +} + +std::vector ResourcesTest::png_list = add_files(); + +INSTANTIATE_TEST_CASE_P(BackgroundTextValidation, ResourcesTest, + ::testing::ValuesIn(ResourcesTest::png_list.cbegin(), + ResourcesTest::png_list.cend())); diff --git a/tests/component/uncrypt_test.cpp b/tests/unit/uncrypt_test.cpp similarity index 100% rename from tests/component/uncrypt_test.cpp rename to tests/unit/uncrypt_test.cpp diff --git a/tests/component/update_verifier_test.cpp b/tests/unit/update_verifier_test.cpp similarity index 100% rename from tests/component/update_verifier_test.cpp rename to tests/unit/update_verifier_test.cpp diff --git a/tests/component/updater_test.cpp b/tests/unit/updater_test.cpp similarity index 100% rename from tests/component/updater_test.cpp rename to tests/unit/updater_test.cpp diff --git a/tests/component/verifier_test.cpp b/tests/unit/verifier_test.cpp similarity index 100% rename from tests/component/verifier_test.cpp rename to tests/unit/verifier_test.cpp From 36c7276cb29c0990933de6da8dbcf7ea2dc2741d Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 30 Apr 2019 00:25:41 -0700 Subject: [PATCH 023/171] install: Return bool for a few check functions. The results from these functions have boolean semantics. They're returning `int` prior to this CL, with some of them mixing 0 and InstallResult. Note that SetUpNonAbUpdateCommands() was returning INSTALL_CORRUPT / INSTALL_ERROR / 0 prior to this change, but all the callers handle INSTALL_CORRUPT and INSTALL_ERROR the same way. This CL changes them to return bool instead. Test: `mmma -j bootable/recovery` Test: TreeHugger Test: Sideload on taimen. Change-Id: Ic1b5dbf79aaca68b53ab8ea2c8ba3d19f988c571 --- install/include/install/install.h | 8 +-- install/include/private/setup_commands.h | 8 +-- install/install.cpp | 64 ++++++++++++------------ install/wipe_device.cpp | 2 +- tests/unit/install_test.cpp | 54 ++++++++++---------- 5 files changed, 67 insertions(+), 69 deletions(-) diff --git a/install/include/install/install.h b/install/include/install/install.h index d90c20f3..cc595f6b 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -62,7 +62,7 @@ bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map& metadata, OtaType ota_type); +// Checks if the metadata in the OTA package has expected values. Mandatory checks: ota-type, +// pre-device and serial number (if presents). A/B OTA specific checks: pre-build version, +// fingerprint, timestamp. +bool CheckPackageMetadata(const std::map& metadata, OtaType ota_type); diff --git a/install/include/private/setup_commands.h b/install/include/private/setup_commands.h index 7fdc741d..dcff7611 100644 --- a/install/include/private/setup_commands.h +++ b/install/include/private/setup_commands.h @@ -27,13 +27,13 @@ // |zip| located at |package|. Stores the command line that should be called into |cmd|. The // |status_fd| is the file descriptor the child process should use to report back the progress of // the update. -int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count, - int status_fd, std::vector* cmd); +bool SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count, + int status_fd, std::vector* cmd); // Sets up the commands for an A/B update. Extracts the needed entries from the open zip archive // |zip| located at |package|. Stores the command line that should be called into |cmd|. The // |status_fd| is the file descriptor the child process should use to report back the progress of // the update. Note that since this applies to the sideloading flow only, it takes one less // parameter |retry_count| than the non-A/B version. -int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd, - std::vector* cmd); +bool SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd, + std::vector* cmd); diff --git a/install/install.cpp b/install/install.cpp index e2d47009..3f4df13e 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -139,14 +139,14 @@ static void ReadSourceTargetBuild(const std::map& meta // Checks the build version, fingerprint and timestamp in the metadata of the A/B package. // Downgrading is not allowed unless explicitly enabled in the package and only for // incremental packages. -static int CheckAbSpecificMetadata(const std::map& metadata) { +static bool CheckAbSpecificMetadata(const std::map& metadata) { // Incremental updates should match the current build. auto device_pre_build = android::base::GetProperty("ro.build.version.incremental", ""); auto pkg_pre_build = get_value(metadata, "pre-build-incremental"); if (!pkg_pre_build.empty() && pkg_pre_build != device_pre_build) { LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected " << device_pre_build; - return INSTALL_ERROR; + return false; } auto device_fingerprint = android::base::GetProperty("ro.build.fingerprint", ""); @@ -154,7 +154,7 @@ static int CheckAbSpecificMetadata(const std::map& met if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != device_fingerprint) { LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected " << device_fingerprint; - return INSTALL_ERROR; + return false; } // Check for downgrade version. @@ -172,36 +172,36 @@ static int CheckAbSpecificMetadata(const std::map& met "newer than timestamp " << build_timestamp << " but package has timestamp " << pkg_post_timestamp << " and downgrade not allowed."; - return INSTALL_ERROR; + return false; } if (pkg_pre_build_fingerprint.empty()) { LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed."; - return INSTALL_ERROR; + return false; } } - return 0; + return true; } -int CheckPackageMetadata(const std::map& metadata, OtaType ota_type) { +bool CheckPackageMetadata(const std::map& metadata, OtaType ota_type) { auto package_ota_type = get_value(metadata, "ota-type"); auto expected_ota_type = OtaTypeToString(ota_type); if (ota_type != OtaType::AB && ota_type != OtaType::BRICK) { LOG(INFO) << "Skip package metadata check for ota type " << expected_ota_type; - return 0; + return true; } if (package_ota_type != expected_ota_type) { LOG(ERROR) << "Unexpected ota package type, expects " << expected_ota_type << ", actual " << package_ota_type; - return INSTALL_ERROR; + return false; } auto device = android::base::GetProperty("ro.product.device", ""); auto pkg_device = get_value(metadata, "pre-device"); if (pkg_device != device || pkg_device.empty()) { LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << device; - return INSTALL_ERROR; + return false; } // We allow the package to not have any serialno; and we also allow it to carry multiple serial @@ -218,7 +218,7 @@ int CheckPackageMetadata(const std::map& metadata, Ota } if (!serial_number_match) { LOG(ERROR) << "Package is for serial " << pkg_serial_no; - return INSTALL_ERROR; + return false; } } @@ -226,11 +226,11 @@ int CheckPackageMetadata(const std::map& metadata, Ota return CheckAbSpecificMetadata(metadata); } - return 0; + return true; } -int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd, - std::vector* cmd) { +bool SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd, + std::vector* cmd) { CHECK(cmd != nullptr); // For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset @@ -240,7 +240,7 @@ int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int ZipEntry properties_entry; if (FindEntry(zip, property_name, &properties_entry) != 0) { LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD_PROPERTIES; - return INSTALL_CORRUPT; + return false; } uint32_t properties_entry_length = properties_entry.uncompressed_length; std::vector payload_properties(properties_entry_length); @@ -248,7 +248,7 @@ int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int ExtractToMemory(zip, &properties_entry, payload_properties.data(), properties_entry_length); if (err != 0) { LOG(ERROR) << "Failed to extract " << AB_OTA_PAYLOAD_PROPERTIES << ": " << ErrorCodeString(err); - return INSTALL_CORRUPT; + return false; } static constexpr const char* AB_OTA_PAYLOAD = "payload.bin"; @@ -256,7 +256,7 @@ int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int ZipEntry payload_entry; if (FindEntry(zip, payload_name, &payload_entry) != 0) { LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD; - return INSTALL_CORRUPT; + return false; } long payload_offset = payload_entry.offset; *cmd = { @@ -266,11 +266,11 @@ int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int "--headers=" + std::string(payload_properties.begin(), payload_properties.end()), android::base::StringPrintf("--status_fd=%d", status_fd), }; - return 0; + return true; } -int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count, - int status_fd, std::vector* cmd) { +bool SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count, + int status_fd, std::vector* cmd) { CHECK(cmd != nullptr); // In non-A/B updates we extract the update binary from the package. @@ -279,7 +279,7 @@ int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, i ZipEntry binary_entry; if (FindEntry(zip, binary_name, &binary_entry) != 0) { LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME; - return INSTALL_CORRUPT; + return false; } const std::string binary_path = Paths::Get().temporary_update_binary(); @@ -288,13 +288,12 @@ int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, i open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755)); if (fd == -1) { PLOG(ERROR) << "Failed to create " << binary_path; - return INSTALL_ERROR; + return false; } - int32_t error = ExtractEntryToFile(zip, &binary_entry, fd); - if (error != 0) { + if (auto error = ExtractEntryToFile(zip, &binary_entry, fd); error != 0) { LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error); - return INSTALL_ERROR; + return false; } // When executing the update binary contained in the package, the arguments passed are: @@ -311,7 +310,7 @@ int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, i if (retry_count > 0) { cmd->push_back("retry"); } - return 0; + return true; } static void log_max_temperature(int* max_temperature, const std::atomic& logger_finished) { @@ -335,11 +334,10 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b } bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false); - // Verifies against the metadata in the package first. - if (int check_status = is_ab ? CheckPackageMetadata(metadata, OtaType::AB) : 0; - check_status != 0) { + // Verify against the metadata in the package first. + if (is_ab && !CheckPackageMetadata(metadata, OtaType::AB)) { log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); - return check_status; + return INSTALL_ERROR; } ReadSourceTargetBuild(metadata, log_buffer); @@ -386,12 +384,12 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b // std::vector args; - if (int update_status = + if (auto setup_result = is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args) : SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args); - update_status != 0) { + !setup_result) { log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); - return update_status; + return INSTALL_CORRUPT; } pid_t pid = fork(); diff --git a/install/wipe_device.cpp b/install/wipe_device.cpp index 72b96f7b..5a9b512c 100644 --- a/install/wipe_device.cpp +++ b/install/wipe_device.cpp @@ -165,7 +165,7 @@ static bool CheckWipePackage(Package* wipe_package, RecoveryUI* ui) { return false; } - return CheckPackageMetadata(metadata, OtaType::BRICK) == 0; + return CheckPackageMetadata(metadata, OtaType::BRICK); } bool WipeAbDevice(Device* device, size_t wipe_package_size) { diff --git a/tests/unit/install_test.cpp b/tests/unit/install_test.cpp index 36820f83..c1d77fb7 100644 --- a/tests/unit/install_test.cpp +++ b/tests/unit/install_test.cpp @@ -205,7 +205,7 @@ TEST(InstallTest, SetUpNonAbUpdateCommands) { std::string binary_path = std::string(td.path) + "/update_binary"; Paths::Get().set_temporary_update_binary(binary_path); std::vector cmd; - ASSERT_EQ(0, SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd)); + ASSERT_TRUE(SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd)); ASSERT_EQ(4U, cmd.size()); ASSERT_EQ(binary_path, cmd[0]); ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION @@ -217,7 +217,7 @@ TEST(InstallTest, SetUpNonAbUpdateCommands) { // With non-zero retry count. update_binary will be removed automatically. cmd.clear(); - ASSERT_EQ(0, SetUpNonAbUpdateCommands(package, zip, 2, status_fd, &cmd)); + ASSERT_TRUE(SetUpNonAbUpdateCommands(package, zip, 2, status_fd, &cmd)); ASSERT_EQ(5U, cmd.size()); ASSERT_EQ(binary_path, cmd[0]); ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION @@ -244,7 +244,7 @@ TEST(InstallTest, SetUpNonAbUpdateCommands_MissingUpdateBinary) { TemporaryDir td; Paths::Get().set_temporary_update_binary(std::string(td.path) + "/update_binary"); std::vector cmd; - ASSERT_EQ(INSTALL_CORRUPT, SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd)); + ASSERT_FALSE(SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd)); CloseArchive(zip); } @@ -278,12 +278,12 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t std::map metadata; ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); if (success) { - ASSERT_EQ(0, CheckPackageMetadata(metadata, OtaType::AB)); + ASSERT_TRUE(CheckPackageMetadata(metadata, OtaType::AB)); int status_fd = 10; std::string package = "/path/to/update.zip"; std::vector cmd; - ASSERT_EQ(0, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); + ASSERT_TRUE(SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); ASSERT_EQ(5U, cmd.size()); ASSERT_EQ("/system/bin/update_engine_sideload", cmd[0]); ASSERT_EQ("--payload=file://" + package, cmd[1]); @@ -291,7 +291,7 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t ASSERT_EQ("--headers=" + properties, cmd[3]); ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); } else { - ASSERT_EQ(INSTALL_ERROR, CheckPackageMetadata(metadata, OtaType::AB)); + ASSERT_FALSE(CheckPackageMetadata(metadata, OtaType::AB)); } CloseArchive(zip); } @@ -326,7 +326,7 @@ TEST(InstallTest, SetUpAbUpdateCommands_MissingPayloadPropertiesTxt) { int status_fd = 10; std::string package = "/path/to/update.zip"; std::vector cmd; - ASSERT_EQ(INSTALL_CORRUPT, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); + ASSERT_FALSE(SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); CloseArchive(zip); } @@ -359,8 +359,8 @@ TEST(InstallTest, SetUpAbUpdateCommands_MultipleSerialnos) { VerifyAbUpdateCommands(long_serialno); } -static void test_check_package_metadata(const std::string& metadata_string, OtaType ota_type, - int exptected_result) { +static void TestCheckPackageMetadata(const std::string& metadata_string, OtaType ota_type, + bool exptected_result) { TemporaryFile temp_file; BuildZipArchive( { @@ -388,7 +388,7 @@ TEST(InstallTest, CheckPackageMetadata_ota_type) { "post-timestamp=" + std::to_string(std::numeric_limits::max()), }, "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + TestCheckPackageMetadata(metadata, OtaType::AB, false); // Checks if ota-type matches metadata = android::base::Join( @@ -398,9 +398,9 @@ TEST(InstallTest, CheckPackageMetadata_ota_type) { "post-timestamp=" + std::to_string(std::numeric_limits::max()), }, "\n"); - test_check_package_metadata(metadata, OtaType::AB, 0); + TestCheckPackageMetadata(metadata, OtaType::AB, true); - test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + TestCheckPackageMetadata(metadata, OtaType::BRICK, false); } TEST(InstallTest, CheckPackageMetadata_device_type) { @@ -410,7 +410,7 @@ TEST(InstallTest, CheckPackageMetadata_device_type) { "ota-type=BRICK", }, "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + TestCheckPackageMetadata(metadata, OtaType::BRICK, false); // device type mismatches metadata = android::base::Join( @@ -419,7 +419,7 @@ TEST(InstallTest, CheckPackageMetadata_device_type) { "pre-device=dummy_device_type", }, "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + TestCheckPackageMetadata(metadata, OtaType::BRICK, false); } TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) { @@ -433,7 +433,7 @@ TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) { "pre-device=" + device, }, "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, 0); + TestCheckPackageMetadata(metadata, OtaType::BRICK, true); // Serial number mismatches metadata = android::base::Join( @@ -443,7 +443,7 @@ TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) { "serialno=dummy_serial", }, "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + TestCheckPackageMetadata(metadata, OtaType::BRICK, false); std::string serialno = android::base::GetProperty("ro.serialno", ""); ASSERT_NE("", serialno); @@ -454,7 +454,7 @@ TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) { "serialno=" + serialno, }, "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, 0); + TestCheckPackageMetadata(metadata, OtaType::BRICK, true); } TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) { @@ -478,7 +478,7 @@ TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) { "serialno=" + android::base::Join(serial_numbers, '|'), }, "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + TestCheckPackageMetadata(metadata, OtaType::BRICK, false); serial_numbers.emplace_back(serialno); std::shuffle(serial_numbers.begin(), serial_numbers.end(), std::default_random_engine()); @@ -489,7 +489,7 @@ TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) { "serialno=" + android::base::Join(serial_numbers, '|'), }, "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, 0); + TestCheckPackageMetadata(metadata, OtaType::BRICK, true); } TEST(InstallTest, CheckPackageMetadata_ab_build_version) { @@ -507,7 +507,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_build_version) { "post-timestamp=" + std::to_string(std::numeric_limits::max()), }, "\n"); - test_check_package_metadata(metadata, OtaType::AB, 0); + TestCheckPackageMetadata(metadata, OtaType::AB, true); metadata = android::base::Join( std::vector{ @@ -517,7 +517,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_build_version) { "post-timestamp=" + std::to_string(std::numeric_limits::max()), }, "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + TestCheckPackageMetadata(metadata, OtaType::AB, false); } TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) { @@ -535,7 +535,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) { "post-timestamp=" + std::to_string(std::numeric_limits::max()), }, "\n"); - test_check_package_metadata(metadata, OtaType::AB, 0); + TestCheckPackageMetadata(metadata, OtaType::AB, true); metadata = android::base::Join( std::vector{ @@ -545,7 +545,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) { "post-timestamp=" + std::to_string(std::numeric_limits::max()), }, "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + TestCheckPackageMetadata(metadata, OtaType::AB, false); } TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { @@ -559,7 +559,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { "pre-device=" + device, }, "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + TestCheckPackageMetadata(metadata, OtaType::AB, false); // post timestamp should be larger than the timestamp on device. metadata = android::base::Join( @@ -569,7 +569,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { "post-timestamp=0", }, "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + TestCheckPackageMetadata(metadata, OtaType::AB, false); // fingerprint is required for downgrade metadata = android::base::Join( @@ -580,7 +580,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { "ota-downgrade=yes", }, "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + TestCheckPackageMetadata(metadata, OtaType::AB, false); std::string finger_print = android::base::GetProperty("ro.build.fingerprint", ""); ASSERT_NE("", finger_print); @@ -594,5 +594,5 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { "ota-downgrade=yes", }, "\n"); - test_check_package_metadata(metadata, OtaType::AB, 0); + TestCheckPackageMetadata(metadata, OtaType::AB, true); } From adc99efd1cfd94283c6a1c5c9e9a4ce4af2c5daf Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 29 Apr 2019 23:48:02 -0700 Subject: [PATCH 024/171] install: Install functions return InstallResult. Test: `atest recovery_unit_test recovery_component_test` Test: Sideload a package on taimen. Change-Id: I2d42f55a89931ee495ea5c5d9e6b5ee1058e8e52 --- install/adb_install.cpp | 10 +++---- install/fuse_sdcard_install.cpp | 9 +++---- install/include/install/adb_install.h | 11 ++++---- install/include/install/fuse_sdcard_install.h | 3 ++- install/include/install/install.h | 4 +-- install/install.cpp | 27 ++++++++++--------- recovery.cpp | 26 ++++++++++++------ 7 files changed, 51 insertions(+), 39 deletions(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 4dd1f1b0..2de1075d 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -90,7 +90,7 @@ static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { // Installs the package from FUSE. Returns the installation result and whether it should continue // waiting for new commands. -static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) { +static auto AdbInstallPackageHandler(RecoveryUI* ui, InstallResult* result) { // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long // because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME // will start to exist once the host connects and starts serving a package. Poll for its @@ -110,7 +110,7 @@ static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) { break; } } - *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui); + *result = InstallPackage(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui); break; } @@ -120,7 +120,7 @@ static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) { return std::make_pair(*result == INSTALL_SUCCESS, should_continue); } -static auto AdbRebootHandler(MinadbdCommand command, int* result, +static auto AdbRebootHandler(MinadbdCommand command, InstallResult* result, Device::BuiltinAction* reboot_action) { // Use Device::REBOOT_{FASTBOOT,RECOVERY,RESCUE}, instead of the ones with ENTER_. This allows // rebooting back into fastboot/recovery/rescue mode through bootloader, which may use a newly @@ -331,7 +331,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) { +InstallResult ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) { // Save the usb state to restore after the sideload operation. std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); // Clean up state and stop adbd. @@ -342,7 +342,7 @@ int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot RecoveryUI* ui = device->GetUI(); - int install_result = INSTALL_ERROR; + InstallResult install_result = INSTALL_ERROR; std::map command_map{ { MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, { MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid, diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_sdcard_install.cpp index 1aa8768e..a5caa6e7 100644 --- a/install/fuse_sdcard_install.cpp +++ b/install/fuse_sdcard_install.cpp @@ -133,7 +133,7 @@ static bool StartSdcardFuse(const std::string& path) { return run_fuse_sideload(std::move(file_data_reader)) == 0; } -int ApplyFromSdcard(Device* device, RecoveryUI* ui) { +InstallResult ApplyFromSdcard(Device* device, RecoveryUI* ui) { if (ensure_path_mounted(SDCARD_ROOT) != 0) { LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n"; return INSTALL_ERROR; @@ -159,9 +159,8 @@ int ApplyFromSdcard(Device* device, RecoveryUI* ui) { _exit(status ? EXIT_SUCCESS : EXIT_FAILURE); } - // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child - // process is ready. - int result = INSTALL_ERROR; + // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child process is ready. + InstallResult result = INSTALL_ERROR; int status; bool waited = false; for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) { @@ -184,7 +183,7 @@ int ApplyFromSdcard(Device* device, RecoveryUI* ui) { } } - result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui); + result = InstallPackage(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /* retry_count */, ui); break; } diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index 3a0a8174..88002236 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -16,9 +16,10 @@ #pragma once -#include +#include "install/install.h" +#include "recovery_ui/device.h" -// Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum -// InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with -// the reboot target set in reboot_action. -int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action); +// Applies a package via `adb sideload` or `adb rescue`. Returns the install result. When a reboot +// has been requested, INSTALL_REBOOT will be the return value, with the reboot target set in +// reboot_action. +InstallResult ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action); diff --git a/install/include/install/fuse_sdcard_install.h b/install/include/install/fuse_sdcard_install.h index d9214ca3..e5bb01f0 100644 --- a/install/include/install/fuse_sdcard_install.h +++ b/install/include/install/fuse_sdcard_install.h @@ -16,7 +16,8 @@ #pragma once +#include "install/install.h" #include "recovery_ui/device.h" #include "recovery_ui/ui.h" -int ApplyFromSdcard(Device* device, RecoveryUI* ui); +InstallResult ApplyFromSdcard(Device* device, RecoveryUI* ui); diff --git a/install/include/install/install.h b/install/include/install/install.h index cc595f6b..44a5cde9 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -47,8 +47,8 @@ enum class OtaType { // Installs the given update package. This function should also wipe the cache partition after a // successful installation if |should_wipe_cache| is true or an updater command asks to wipe the // cache. -int install_package(const std::string& package, bool should_wipe_cache, bool needs_mount, - int retry_count, RecoveryUI* ui); +InstallResult InstallPackage(const std::string& package, bool should_wipe_cache, bool needs_mount, + int retry_count, RecoveryUI* ui); // Verifies the package by ota keys. Returns true if the package is verified successfully, // otherwise returns false. diff --git a/install/install.cpp b/install/install.cpp index 3f4df13e..5d514fa2 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -324,9 +324,9 @@ static void log_max_temperature(int* max_temperature, const std::atomic& l } // If the package contains an update binary, extract it and run it. -static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache, - std::vector* log_buffer, int retry_count, - int* max_temperature, RecoveryUI* ui) { +static InstallResult TryUpdateBinary(const std::string& package, ZipArchiveHandle zip, + bool* wipe_cache, std::vector* log_buffer, + int retry_count, int* max_temperature, RecoveryUI* ui) { std::map metadata; if (!ReadMetadataFromPackage(zip, &metadata)) { LOG(ERROR) << "Failed to parse metadata in the zip file"; @@ -569,9 +569,10 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) { return false; } -static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount, - std::vector* log_buffer, int retry_count, - int* max_temperature, RecoveryUI* ui) { +static InstallResult VerifyAndInstallPackage(const std::string& path, bool* wipe_cache, + bool needs_mount, std::vector* log_buffer, + int retry_count, int* max_temperature, + RecoveryUI* ui) { ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); ui->Print("Finding update package...\n"); // Give verification half the progress bar... @@ -622,16 +623,16 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo ui->Print("Retry attempt: %d\n", retry_count); } ui->SetEnableReboot(false); - int result = - try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui); + auto result = + TryUpdateBinary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui); ui->SetEnableReboot(true); ui->Print("\n"); return result; } -int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount, - int retry_count, RecoveryUI* ui) { +InstallResult InstallPackage(const std::string& path, bool should_wipe_cache, bool needs_mount, + int retry_count, RecoveryUI* ui) { CHECK(!path.empty()); auto start = std::chrono::system_clock::now(); @@ -639,15 +640,15 @@ int install_package(const std::string& path, bool should_wipe_cache, bool needs_ int start_temperature = GetMaxValueFromThermalZone(); int max_temperature = start_temperature; - int result; + InstallResult result; std::vector log_buffer; if (setup_install_mounts() != 0) { LOG(ERROR) << "failed to set up expected mounts for install; aborting"; result = INSTALL_ERROR; } else { bool updater_wipe_cache = false; - result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer, - retry_count, &max_temperature, ui); + result = VerifyAndInstallPackage(path, &updater_wipe_cache, needs_mount, &log_buffer, + retry_count, &max_temperature, ui); should_wipe_cache = should_wipe_cache || updater_wipe_cache; } diff --git a/recovery.cpp b/recovery.cpp index 36813904..dbac3e01 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -113,12 +113,12 @@ const char* reason = nullptr; * 3. main system reboots into recovery * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..." * -- after this, rebooting will attempt to reinstall the update -- - * 5. install_package() attempts to install the update + * 5. InstallPackage() attempts to install the update * NOTE: the package install must itself be restartable from any point * 6. finish_recovery() erases BCB * -- after this, rebooting will (try to) restart the main system -- * 7. ** if install failed ** - * 7a. prompt_and_wait() shows an error icon and waits for the user + * 7a. PromptAndWait() shows an error icon and waits for the user * 7b. the user reboots (pulling the battery, etc) into the main system */ @@ -312,14 +312,18 @@ static void run_graphics_test() { ui->ShowText(true); } -// Returns REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default, -// which is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery. -static Device::BuiltinAction prompt_and_wait(Device* device, int status) { +// Shows the recovery UI and waits for user input. Returns one of the device builtin actions, such +// as REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default, which +// is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery. +static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) { for (;;) { finish_recovery(); switch (status) { case INSTALL_SUCCESS: case INSTALL_NONE: + case INSTALL_SKIPPED: + case INSTALL_RETRY: + case INSTALL_KEY_INTERRUPTED: ui->SetBackground(RecoveryUI::NO_COMMAND); break; @@ -327,6 +331,12 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { case INSTALL_CORRUPT: ui->SetBackground(RecoveryUI::ERROR); break; + + case INSTALL_REBOOT: + // All the reboots should have been handled prior to entering PromptAndWait() or immediately + // after installing a package. + LOG(FATAL) << "Invalid status code of INSTALL_REBOOT"; + break; } ui->SetProgressType(RecoveryUI::EMPTY); @@ -690,7 +700,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Supported API: %d\n", kRecoveryApiVersion); - int status = INSTALL_SUCCESS; + InstallResult status = INSTALL_SUCCESS; // next_action indicates the next target to reboot into upon finishing the install. It could be // overridden to a different reboot target per user request. Device::BuiltinAction next_action = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; @@ -720,7 +730,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Installation aborted.\n"); @@ -828,7 +838,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorIsTextVisible()) { - Device::BuiltinAction temp = prompt_and_wait(device, status); + auto temp = PromptAndWait(device, status); if (temp != Device::NO_ACTION) { next_action = temp; } From 311e6ca7b609e578c0ec132440a4ad981db70a9e Mon Sep 17 00:00:00 2001 From: xunchang Date: Fri, 22 Mar 2019 08:54:35 -0700 Subject: [PATCH 025/171] Implement FuseBlockDataProvider Adds a fuse data provider that parses the metadata from a block map, reads the data from the given ranges of the block device; and provides the data to the fuse. Bug: 127071893 Test: unit tests pass, install a package from block map Change-Id: Ie9925ee9144e98642505b3f5e1a4a186d2b21ed0 --- fuse_sideload/Android.bp | 4 + fuse_sideload/fuse_provider.cpp | 79 ++++++++++++++++++++ fuse_sideload/include/fuse_provider.h | 28 +++++++ minadbd/Android.bp | 5 ++ otautil/include/otautil/rangeset.h | 7 ++ otautil/rangeset.cpp | 52 +++++++++++++ otautil/sysutil.cpp | 5 ++ tests/Android.bp | 1 + tests/unit/fuse_provider_test.cpp | 103 ++++++++++++++++++++++++++ tests/unit/rangeset_test.cpp | 24 ++++++ tests/unit/sysutil_test.cpp | 2 +- 11 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 tests/unit/fuse_provider_test.cpp diff --git a/fuse_sideload/Android.bp b/fuse_sideload/Android.bp index 8548548d..9bf19eb8 100644 --- a/fuse_sideload/Android.bp +++ b/fuse_sideload/Android.bp @@ -34,6 +34,10 @@ cc_library { "include", ], + static_libs: [ + "libotautil", + ], + shared_libs: [ "libbase", "libcrypto", diff --git a/fuse_sideload/fuse_provider.cpp b/fuse_sideload/fuse_provider.cpp index 58786f5f..5ee6e247 100644 --- a/fuse_sideload/fuse_provider.cpp +++ b/fuse_sideload/fuse_provider.cpp @@ -27,8 +27,11 @@ #include #include +#include +#include #include "fuse_sideload.h" +#include "otautil/sysutil.h" FuseFileDataProvider::FuseFileDataProvider(const std::string& path, uint32_t block_size) { struct stat sb; @@ -69,3 +72,79 @@ bool FuseFileDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_ void FuseFileDataProvider::Close() { fd_.reset(); } + +FuseBlockDataProvider::FuseBlockDataProvider(uint64_t file_size, uint32_t fuse_block_size, + android::base::unique_fd&& fd, + uint32_t source_block_size, RangeSet ranges) + : FuseDataProvider(file_size, fuse_block_size), + fd_(std::move(fd)), + source_block_size_(source_block_size), + ranges_(std::move(ranges)) { + // Make sure the offset is also aligned with the blocks on the block device when we call + // ReadBlockAlignedData(). + CHECK_EQ(0, fuse_block_size_ % source_block_size_); +} + +bool FuseBlockDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, + uint32_t start_block) const { + uint64_t offset = static_cast(start_block) * fuse_block_size_; + if (fetch_size > file_size_ || offset > file_size_ - fetch_size) { + LOG(ERROR) << "Out of bound read, offset: " << offset << ", fetch size: " << fetch_size + << ", file size " << file_size_; + return false; + } + + auto read_ranges = + ranges_.GetSubRanges(offset / source_block_size_, fetch_size / source_block_size_); + if (!read_ranges) { + return false; + } + + uint8_t* next_out = buffer; + for (const auto& [range_start, range_end] : read_ranges.value()) { + uint64_t bytes_start = static_cast(range_start) * source_block_size_; + uint64_t bytes_to_read = static_cast(range_end - range_start) * source_block_size_; + if (!android::base::ReadFullyAtOffset(fd_, next_out, bytes_to_read, bytes_start)) { + PLOG(ERROR) << "Failed to read " << bytes_to_read << " bytes at offset " << bytes_start; + return false; + } + + next_out += bytes_to_read; + } + + if (uint64_t tailing_bytes = fetch_size % source_block_size_; tailing_bytes != 0) { + // Calculate the offset to last partial block. + uint64_t tailing_offset = + read_ranges.value() + ? static_cast((read_ranges->cend() - 1)->second) * source_block_size_ + : static_cast(start_block) * source_block_size_; + if (!android::base::ReadFullyAtOffset(fd_, next_out, tailing_bytes, tailing_offset)) { + PLOG(ERROR) << "Failed to read tailing " << tailing_bytes << " bytes at offset " + << tailing_offset; + return false; + } + } + return true; +} + +std::unique_ptr FuseBlockDataProvider::CreateFromBlockMap( + const std::string& block_map_path, uint32_t fuse_block_size) { + auto block_map = BlockMapData::ParseBlockMapFile(block_map_path); + if (!block_map) { + return nullptr; + } + + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_map.path().c_str(), O_RDONLY))); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << block_map.path(); + return nullptr; + } + + return std::unique_ptr( + new FuseBlockDataProvider(block_map.file_size(), fuse_block_size, std::move(fd), + block_map.block_size(), block_map.block_ranges())); +} + +void FuseBlockDataProvider::Close() { + fd_.reset(); +} diff --git a/fuse_sideload/include/fuse_provider.h b/fuse_sideload/include/fuse_provider.h index 59059cf9..8d4ea407 100644 --- a/fuse_sideload/include/fuse_provider.h +++ b/fuse_sideload/include/fuse_provider.h @@ -18,10 +18,13 @@ #include +#include #include #include +#include "otautil/rangeset.h" + // This is the base class to read data from source and provide the data to FUSE. class FuseDataProvider { public: @@ -70,3 +73,28 @@ class FuseFileDataProvider : public FuseDataProvider { // The underlying source to read data from. android::base::unique_fd fd_; }; + +// This class parses a block map and reads data from the underlying block device. +class FuseBlockDataProvider : public FuseDataProvider { + public: + // Constructs the fuse provider from the block map. + static std::unique_ptr CreateFromBlockMap( + const std::string& block_map_path, uint32_t fuse_block_size); + + RangeSet ranges() const { + return ranges_; + } + bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, + uint32_t start_block) const override; + void Close() override; + + private: + FuseBlockDataProvider(uint64_t file_size, uint32_t fuse_block_size, android::base::unique_fd&& fd, + uint32_t source_block_size, RangeSet ranges); + // The underlying block device to read data from. + android::base::unique_fd fd_; + // The block size of the source block device. + uint32_t source_block_size_; + // The block ranges from the source block device that consist of the file + RangeSet ranges_; +}; diff --git a/minadbd/Android.bp b/minadbd/Android.bp index 007e5057..afd57ad2 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -43,6 +43,10 @@ cc_library { "minadbd_services.cpp", ], + static_libs: [ + "libotautil", + ], + shared_libs: [ "libadbd", "libbase", @@ -96,6 +100,7 @@ cc_test { static_libs: [ "libminadbd_services", "libfusesideload", + "libotautil", "libadbd", "libcrypto", ], diff --git a/otautil/include/otautil/rangeset.h b/otautil/include/otautil/rangeset.h index e91d02ca..a18c30e2 100644 --- a/otautil/include/otautil/rangeset.h +++ b/otautil/include/otautil/rangeset.h @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -49,6 +50,12 @@ class RangeSet { // bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" and "5,7" are not overlapped. bool Overlaps(const RangeSet& other) const; + // Returns a subset of ranges starting from |start_index| with respect to the original range. The + // output range will have |num_of_blocks| blocks in size. Returns std::nullopt if the input is + // invalid. e.g. RangeSet({{0, 5}, {10, 15}}).GetSubRanges(1, 5) returns + // RangeSet({{1, 5}, {10, 11}}). + std::optional GetSubRanges(size_t start_index, size_t num_of_blocks) const; + // Returns a vector of RangeSets that contain the same set of blocks represented by the current // RangeSet. The RangeSets in the vector contain similar number of blocks, with a maximum delta // of 1-block between any two of them. For example, 14 blocks would be split into 4 + 4 + 3 + 3, diff --git a/otautil/rangeset.cpp b/otautil/rangeset.cpp index 5ab8e08f..8ee99dd7 100644 --- a/otautil/rangeset.cpp +++ b/otautil/rangeset.cpp @@ -184,6 +184,58 @@ bool RangeSet::Overlaps(const RangeSet& other) const { return false; } +std::optional RangeSet::GetSubRanges(size_t start_index, size_t num_of_blocks) const { + size_t end_index = start_index + num_of_blocks; // The index of final block to read plus one + if (start_index > end_index || end_index > blocks_) { + LOG(ERROR) << "Failed to get the sub ranges for start_index " << start_index + << " num_of_blocks " << num_of_blocks + << " total number of blocks the range contains is " << blocks_; + return std::nullopt; + } + + if (num_of_blocks == 0) { + LOG(WARNING) << "num_of_blocks is zero when calling GetSubRanges()"; + return RangeSet(); + } + + RangeSet result; + size_t current_index = 0; + for (const auto& [range_start, range_end] : ranges_) { + CHECK_LT(range_start, range_end); + size_t blocks_in_range = range_end - range_start; + // Linear search to skip the ranges until we reach start_block. + if (current_index + blocks_in_range <= start_index) { + current_index += blocks_in_range; + continue; + } + + size_t trimmed_range_start = range_start; + // We have found the first block range to read, trim the heading blocks. + if (current_index < start_index) { + trimmed_range_start += start_index - current_index; + } + // Trim the trailing blocks if the last range has more blocks than desired; also return the + // result. + if (current_index + blocks_in_range >= end_index) { + size_t trimmed_range_end = range_end - (current_index + blocks_in_range - end_index); + if (!result.PushBack({ trimmed_range_start, trimmed_range_end })) { + return std::nullopt; + } + + return result; + } + + if (!result.PushBack({ trimmed_range_start, range_end })) { + return std::nullopt; + } + current_index += blocks_in_range; + } + + LOG(ERROR) << "Failed to construct byte ranges to read, start_block: " << start_index + << ", num_of_blocks: " << num_of_blocks << " total number of blocks: " << blocks_; + return std::nullopt; +} + // Ranges in the the set should be mutually exclusive; and they're sorted by the start block. SortedRangeSet::SortedRangeSet(std::vector&& pairs) : RangeSet(std::move(pairs)) { std::sort(ranges_.begin(), ranges_.end()); diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp index 2b486180..420db4ca 100644 --- a/otautil/sysutil.cpp +++ b/otautil/sysutil.cpp @@ -94,6 +94,11 @@ BlockMapData BlockMapData::ParseBlockMapFile(const std::string& block_map_path) remaining_blocks -= range_blocks; } + if (remaining_blocks != 0) { + LOG(ERROR) << "Invalid ranges: remaining blocks " << remaining_blocks; + return {}; + } + return BlockMapData(block_dev, file_size, blksize, std::move(ranges)); } diff --git a/tests/Android.bp b/tests/Android.bp index ec2124a5..67a65ae9 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -118,6 +118,7 @@ cc_test { static_libs: libapplypatch_static_libs + librecovery_static_libs + [ "librecovery_ui", + "libfusesideload", "libminui", "libotautil", "libupdater", diff --git a/tests/unit/fuse_provider_test.cpp b/tests/unit/fuse_provider_test.cpp new file mode 100644 index 00000000..c5995dd7 --- /dev/null +++ b/tests/unit/fuse_provider_test.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "fuse_provider.h" +#include "fuse_sideload.h" +#include "install/install.h" + +TEST(FuseBlockMapTest, CreateFromBlockMap_smoke) { + TemporaryFile fake_block_device; + std::vector lines = { + fake_block_device.path, "10000 4096", "3", "10 11", "20 21", "22 23", + }; + + TemporaryFile temp_file; + android::base::WriteStringToFile(android::base::Join(lines, '\n'), temp_file.path); + auto block_map_data = FuseBlockDataProvider::CreateFromBlockMap(temp_file.path, 4096); + + ASSERT_TRUE(block_map_data); + ASSERT_EQ(10000, block_map_data->file_size()); + ASSERT_EQ(4096, block_map_data->fuse_block_size()); + ASSERT_EQ(RangeSet({ { 10, 11 }, { 20, 21 }, { 22, 23 } }), block_map_data->ranges()); +} + +TEST(FuseBlockMapTest, ReadBlockAlignedData_smoke) { + std::string content; + content.reserve(40960); + for (char c = 0; c < 10; c++) { + content += std::string(4096, c); + } + TemporaryFile fake_block_device; + ASSERT_TRUE(android::base::WriteStringToFile(content, fake_block_device.path)); + + std::vector lines = { + fake_block_device.path, + "20000 4096", + "1", + "0 5", + }; + TemporaryFile temp_file; + android::base::WriteStringToFile(android::base::Join(lines, '\n'), temp_file.path); + auto block_map_data = FuseBlockDataProvider::CreateFromBlockMap(temp_file.path, 4096); + + std::vector result(2000); + ASSERT_TRUE(block_map_data->ReadBlockAlignedData(result.data(), 2000, 1)); + ASSERT_EQ(std::vector(content.begin() + 4096, content.begin() + 6096), result); + + result.resize(20000); + ASSERT_TRUE(block_map_data->ReadBlockAlignedData(result.data(), 20000, 0)); + ASSERT_EQ(std::vector(content.begin(), content.begin() + 20000), result); +} + +TEST(FuseBlockMapTest, ReadBlockAlignedData_large_fuse_block) { + std::string content; + for (char c = 0; c < 10; c++) { + content += std::string(4096, c); + } + + TemporaryFile temp_file; + ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); + + std::vector lines = { + temp_file.path, "36384 4096", "2", "0 5", "6 10", + }; + TemporaryFile block_map; + ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(lines, '\n'), block_map.path)); + + auto block_map_data = FuseBlockDataProvider::CreateFromBlockMap(block_map.path, 16384); + ASSERT_TRUE(block_map_data); + + std::vector result(20000); + // Out of bound read + ASSERT_FALSE(block_map_data->ReadBlockAlignedData(result.data(), 20000, 2)); + ASSERT_TRUE(block_map_data->ReadBlockAlignedData(result.data(), 20000, 1)); + // expected source block contains: 4, 6-9 + std::string expected = content.substr(16384, 4096) + content.substr(24576, 15904); + ASSERT_EQ(std::vector(expected.begin(), expected.end()), result); +} diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp index fc72f2f6..699f933a 100644 --- a/tests/unit/rangeset_test.cpp +++ b/tests/unit/rangeset_test.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -248,6 +249,29 @@ TEST(RangeSetTest, ToString) { ASSERT_EQ("6,1,3,4,6,15,22", RangeSet::Parse("6,1,3,4,6,15,22").ToString()); } +TEST(RangeSetTest, GetSubRanges_invalid) { + RangeSet range0({ { 1, 11 }, { 20, 30 } }); + ASSERT_FALSE(range0.GetSubRanges(0, 21)); // too many blocks + ASSERT_FALSE(range0.GetSubRanges(21, 1)); // start block OOB +} + +TEST(RangeSetTest, GetSubRanges_empty) { + RangeSet range0({ { 1, 11 }, { 20, 30 } }); + ASSERT_EQ(RangeSet{}, range0.GetSubRanges(1, 0)); // empty num_of_blocks +} + +TEST(RangeSetTest, GetSubRanges_smoke) { + RangeSet range0({ { 10, 11 } }); + ASSERT_EQ(RangeSet({ { 10, 11 } }), range0.GetSubRanges(0, 1)); + + RangeSet range1({ { 10, 11 }, { 20, 21 }, { 30, 31 } }); + ASSERT_EQ(range1, range1.GetSubRanges(0, 3)); + ASSERT_EQ(RangeSet({ { 20, 21 } }), range1.GetSubRanges(1, 1)); + + RangeSet range2({ { 1, 11 }, { 20, 25 }, { 30, 35 } }); + ASSERT_EQ(RangeSet({ { 10, 11 }, { 20, 25 }, { 30, 31 } }), range2.GetSubRanges(9, 7)); +} + TEST(SortedRangeSetTest, Insert) { SortedRangeSet rs({ { 2, 3 }, { 4, 6 }, { 8, 14 } }); rs.Insert({ 1, 2 }); diff --git a/tests/unit/sysutil_test.cpp b/tests/unit/sysutil_test.cpp index 3466e8ee..64b8956f 100644 --- a/tests/unit/sysutil_test.cpp +++ b/tests/unit/sysutil_test.cpp @@ -67,7 +67,7 @@ TEST(SysUtilTest, ParseBlockMapFile_invalid_size) { "/dev/abc", "42949672950 4294967295", "1", - "0 9", + "0 10", }; TemporaryFile temp_file; From c784ce50e8c10eaf70e1f97e24e8324aef45faf5 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Wed, 1 May 2019 13:13:58 -0700 Subject: [PATCH 026/171] libbootloader_message: write recovery to any device Test: cuttlefish Bug: 79094284 Change-Id: If1a6460a8cbed2e2d22fa9e16e6d7ca84f4592d0 --- bootloader_message/bootloader_message.cpp | 8 ++++++++ .../include/bootloader_message/bootloader_message.h | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index 8c1d63bd..0ebc04a3 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -168,6 +168,14 @@ bool write_bootloader_message(const std::vector& options, std::stri return write_bootloader_message(boot, err); } +bool write_bootloader_message_to(const std::vector& options, + const std::string& misc_blk_device, std::string* err) { + bootloader_message boot = {}; + update_bootloader_message_in_struct(&boot, options); + + return write_bootloader_message_to(boot, misc_blk_device, err); +} + bool update_bootloader_message(const std::vector& options, std::string* err) { bootloader_message boot; if (!read_bootloader_message(&boot, err)) { diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 95c19ae5..2207d4cb 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -207,6 +207,11 @@ bool write_bootloader_message_to(const bootloader_message& boot, // set the command and recovery fields, and reset the rest. bool write_bootloader_message(const std::vector& options, std::string* err); +// Write bootloader message (boots into recovery with the options) to the specific BCB device. Will +// set the command and recovery fields, and reset the rest. +bool write_bootloader_message_to(const std::vector& options, + const std::string& misc_blk_device, std::string* err); + // Update bootloader message (boots into recovery with the options) to BCB. Will // only update the command and recovery fields. bool update_bootloader_message(const std::vector& options, std::string* err); From d83070ddb902a5319cf9cdfcc71127b2ef6e8d1e Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Wed, 1 May 2019 13:12:57 -0700 Subject: [PATCH 027/171] libbootloader_message: host_supported. Also, strlcat is not available on host, so use std::string::operator+= instead. Test: cuttlefish Bug: 79094284 Change-Id: I1e69daeb522ca73f43e0c4855cf099a021ed4d47 --- bootloader_message/Android.bp | 18 +++++++++++++++++- bootloader_message/bootloader_message.cpp | 12 +++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp index 5cd21323..4ea7c868 100644 --- a/bootloader_message/Android.bp +++ b/bootloader_message/Android.bp @@ -17,6 +17,7 @@ cc_library { name: "libbootloader_message", recovery_available: true, + host_supported: true, srcs: ["bootloader_message.cpp"], cflags: [ "-Wall", @@ -24,7 +25,22 @@ cc_library { ], shared_libs: [ "libbase", - "libfs_mgr", ], export_include_dirs: ["include"], + + target: { + android: { + shared_libs: [ + "libfs_mgr", + ], + }, + host: { + shared_libs: [ + "libcutils", // for strlcpy + ], + static_libs: [ + "libfstab", + ], + } + } } diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index 0ebc04a3..331a42b2 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -29,6 +29,10 @@ #include #include +#ifndef __ANDROID__ +#include // for strlcpy +#endif + using android::fs_mgr::Fstab; using android::fs_mgr::ReadDefaultFstab; @@ -194,13 +198,15 @@ bool update_bootloader_message_in_struct(bootloader_message* boot, memset(boot->recovery, 0, sizeof(boot->recovery)); strlcpy(boot->command, "boot-recovery", sizeof(boot->command)); - strlcpy(boot->recovery, "recovery\n", sizeof(boot->recovery)); + + std::string recovery = "recovery\n"; for (const auto& s : options) { - strlcat(boot->recovery, s.c_str(), sizeof(boot->recovery)); + recovery += s; if (s.back() != '\n') { - strlcat(boot->recovery, "\n", sizeof(boot->recovery)); + recovery += '\n'; } } + strlcpy(boot->recovery, recovery.c_str(), sizeof(boot->recovery)); return true; } From 93838f6e425fcc03e54e712665dfe15f88fe0a0d Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 3 May 2019 10:33:04 -0700 Subject: [PATCH 028/171] ConsumePrefix is now in libbase. Test: treehugger Change-Id: I2feecabb77986d3e007de1009b123c2d98454631 --- minadbd/minadbd_services.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 1c4c0f49..8f2b71aa 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -230,7 +230,7 @@ static void WipeDeviceService(unique_fd fd, const std::string& args) { unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { // Common services that are supported both in sideload and rescue modes. - if (ConsumePrefix(&name, "reboot:")) { + if (android::base::ConsumePrefix(&name, "reboot:")) { // "reboot:", where target must be one of the following. std::string args(name); if (args.empty() || args == "bootloader" || args == "rescue" || args == "recovery" || @@ -243,17 +243,17 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport * // Rescue-specific services. if (rescue_mode) { - if (ConsumePrefix(&name, "rescue-install:")) { + if (android::base::ConsumePrefix(&name, "rescue-install:")) { // rescue-install:: std::string args(name); return create_service_thread( "rescue-install", std::bind(RescueInstallHostService, std::placeholders::_1, args)); - } else if (ConsumePrefix(&name, "rescue-getprop:")) { + } else if (android::base::ConsumePrefix(&name, "rescue-getprop:")) { // rescue-getprop: std::string args(name); return create_service_thread( "rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args)); - } else if (ConsumePrefix(&name, "rescue-wipe:")) { + } else if (android::base::ConsumePrefix(&name, "rescue-wipe:")) { // rescue-wipe:target: std::string args(name); return create_service_thread("rescue-wipe", @@ -268,7 +268,7 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport * // This exit status causes recovery to print a special error message saying to use a newer adb // (that supports sideload-host). exit(kMinadbdAdbVersionError); - } else if (ConsumePrefix(&name, "sideload-host:")) { + } else if (android::base::ConsumePrefix(&name, "sideload-host:")) { // sideload-host:: std::string args(name); return create_service_thread("sideload-host", From fb68d38fba81c4a9b2a1b328e89d817a1f1d1cdf Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2019 09:48:15 -0700 Subject: [PATCH 029/171] Disable libbootloader_message for darwin libfstab is not built for darwin, don't build libbootloader_message either. Bug: 131709594 Test: m PRODUCT-sdk-sdk sdk_repo Change-Id: I6e3b04f1c3e97d5aa6ac0452bf13e714f8dae437 --- bootloader_message/Android.bp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp index 4ea7c868..e76305fd 100644 --- a/bootloader_message/Android.bp +++ b/bootloader_message/Android.bp @@ -41,6 +41,9 @@ cc_library { static_libs: [ "libfstab", ], - } + }, + darwin: { + enabled: false, + }, } } From a86dddbfa593ae1227806af9692fbb6223d4e435 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 3 May 2019 22:52:37 -0700 Subject: [PATCH 030/171] Track libziparchive API change. Bug: http://b/129068177 Test: treehugger Change-Id: Ie5b2b0cff087f2e9e65a4e77c187e3173357f3ad --- install/install.cpp | 15 +++++---------- install/wipe_device.cpp | 3 +-- tests/unit/install_test.cpp | 3 +-- tests/unit/package_test.cpp | 5 ++--- tests/unit/zip_test.cpp | 3 +-- updater/blockimg.cpp | 4 ++-- updater/install.cpp | 6 ++---- updater/updater.cpp | 3 +-- 8 files changed, 15 insertions(+), 27 deletions(-) diff --git a/install/install.cpp b/install/install.cpp index 5d514fa2..d62bffc5 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -73,9 +73,8 @@ bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map GetWipePartitionList(Package* wipe_package) { constexpr 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) { + if (FindEntry(zip, RECOVERY_WIPE_ENTRY_NAME, &entry) == 0) { uint32_t length = entry.uncompressed_length; partition_list_content = std::string(length, '\0'); if (auto err = ExtractToMemory( diff --git a/tests/unit/install_test.cpp b/tests/unit/install_test.cpp index c1d77fb7..4ec40990 100644 --- a/tests/unit/install_test.cpp +++ b/tests/unit/install_test.cpp @@ -271,9 +271,8 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - ZipString payload_name("payload.bin"); ZipEntry payload_entry; - ASSERT_EQ(0, FindEntry(zip, payload_name, &payload_entry)); + ASSERT_EQ(0, FindEntry(zip, "payload.bin", &payload_entry)); std::map metadata; ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); diff --git a/tests/unit/package_test.cpp b/tests/unit/package_test.cpp index a735a699..5e31f7fa 100644 --- a/tests/unit/package_test.cpp +++ b/tests/unit/package_test.cpp @@ -105,10 +105,9 @@ TEST_F(PackageTest, GetZipArchiveHandle_extract_entry) { ASSERT_TRUE(zip); // Check that we can extract one zip entry. - std::string entry_name = "dir1/file3.txt"; - ZipString path(entry_name.c_str()); + std::string_view entry_name = "dir1/file3.txt"; ZipEntry entry; - ASSERT_EQ(0, FindEntry(zip, path, &entry)); + ASSERT_EQ(0, FindEntry(zip, entry_name, &entry)); std::vector extracted(entry_name.size()); ASSERT_EQ(0, ExtractToMemory(zip, &entry, extracted.data(), extracted.size())); diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp index dfe617eb..0753d64e 100644 --- a/tests/unit/zip_test.cpp +++ b/tests/unit/zip_test.cpp @@ -37,10 +37,9 @@ TEST(ZipTest, OpenFromMemory) { ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_path.c_str(), &handle)); static constexpr const char* BINARY_PATH = "META-INF/com/google/android/update-binary"; - ZipString binary_path(BINARY_PATH); ZipEntry binary_entry; // Make sure the package opens correctly and its entry can be read. - ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry)); + ASSERT_EQ(0, FindEntry(handle, BINARY_PATH, &binary_entry)); TemporaryFile tmp_binary; ASSERT_NE(-1, tmp_binary.fd); diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 07c3c7b5..b008c28b 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -1680,7 +1680,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, return StringValue(""); } - ZipString path_data(patch_data_fn->data.c_str()); + std::string_view path_data(patch_data_fn->data); ZipEntry patch_entry; if (FindEntry(za, path_data, &patch_entry) != 0) { LOG(ERROR) << name << "(): no file \"" << patch_data_fn->data << "\" in package"; @@ -1688,7 +1688,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, } params.patch_start = ui->package_zip_addr + patch_entry.offset; - ZipString new_data(new_data_fn->data.c_str()); + std::string_view new_data(new_data_fn->data); ZipEntry new_entry; if (FindEntry(za, new_data, &new_entry) != 0) { LOG(ERROR) << name << "(): no file \"" << new_data_fn->data << "\" in package"; diff --git a/updater/install.cpp b/updater/install.cpp index 8eba64f5..c30f6396 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -130,9 +130,8 @@ Value* PackageExtractFileFn(const char* name, State* state, const std::string& dest_path = args[1]; ZipArchiveHandle za = static_cast(state->cookie)->package_zip; - ZipString zip_string_path(zip_path.c_str()); ZipEntry entry; - if (FindEntry(za, zip_string_path, &entry) != 0) { + if (FindEntry(za, zip_path, &entry) != 0) { LOG(ERROR) << name << ": no " << zip_path << " in package"; return StringValue(""); } @@ -174,9 +173,8 @@ Value* PackageExtractFileFn(const char* name, State* state, const std::string& zip_path = args[0]; ZipArchiveHandle za = static_cast(state->cookie)->package_zip; - ZipString zip_string_path(zip_path.c_str()); ZipEntry entry; - if (FindEntry(za, zip_string_path, &entry) != 0) { + if (FindEntry(za, zip_path, &entry) != 0) { return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name, zip_path.c_str()); } diff --git a/updater/updater.cpp b/updater/updater.cpp index 7b5a3f93..a020699c 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -101,9 +101,8 @@ int main(int argc, char** argv) { return 3; } - ZipString script_name(SCRIPT_NAME); ZipEntry script_entry; - int find_err = FindEntry(za, script_name, &script_entry); + int find_err = FindEntry(za, SCRIPT_NAME, &script_entry); if (find_err != 0) { LOG(ERROR) << "failed to find " << SCRIPT_NAME << " in " << package_filename << ": " << ErrorCodeString(find_err); From 143a03fa034eb7b522a0f734689874907f5c99a0 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 7 May 2019 14:59:09 -0700 Subject: [PATCH 031/171] Track libziparchive API change. Bug: http://b/129068177 Test: treehugger Change-Id: I618bbcf38914dd81e042e0cfd1976ff26274dc30 --- applypatch/imgdiff.cpp | 2 +- install/install.cpp | 2 +- install/verifier.cpp | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 415d95f1..cb34d469 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -675,7 +675,7 @@ bool ZipModeImage::Initialize(const std::string& filename) { // Iterate the zip entries and compose the image chunks accordingly. bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandle handle) { void* cookie; - int ret = StartIteration(handle, &cookie, nullptr, nullptr); + int ret = StartIteration(handle, &cookie); if (ret != 0) { LOG(ERROR) << "Failed to iterate over entries in " << filename << ": " << ErrorCodeString(ret); return false; diff --git a/install/install.cpp b/install/install.cpp index d62bffc5..1b51b424 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -529,7 +529,7 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) { // Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents. void* cookie; - ret = StartIteration(zip_handle, &cookie, nullptr, nullptr); + ret = StartIteration(zip_handle, &cookie); if (ret != 0) { LOG(ERROR) << "Failed to start iterating zip entries: " << ErrorCodeString(ret); CloseArchive(zip_handle); diff --git a/install/verifier.cpp b/install/verifier.cpp index 6ba1d77c..3de58e4b 100644 --- a/install/verifier.cpp +++ b/install/verifier.cpp @@ -311,8 +311,7 @@ int verify_file(VerifierInterface* package, const std::vector& keys static std::vector IterateZipEntriesAndSearchForKeys(const ZipArchiveHandle& handle) { void* cookie; - ZipString suffix("x509.pem"); - int32_t iter_status = StartIteration(handle, &cookie, nullptr, &suffix); + int32_t iter_status = StartIteration(handle, &cookie, "", "x509.pem"); if (iter_status != 0) { LOG(ERROR) << "Failed to iterate over entries in the certificate zipfile: " << ErrorCodeString(iter_status); From 58d59129e1420982f2b184e3fc1e0f5c7c4cf601 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 3 May 2019 01:05:04 -0700 Subject: [PATCH 032/171] Add Updater class and remove UpdaterInfo The UpdaterInfo class is merely a collection of pointers and POD types. We can replace it with a Updater class that has the ownership of the resources. This also makes this class extensible as we plan to add more functionality in the host simulator. Bug: 131911365 Test: unit tests pass, run an update on cuttlefish and check last_install Change-Id: I07ca5963bbee8ae3cb85ccc184464910aa73d4e4 --- tests/unit/updater_test.cpp | 178 +++++++++--------- updater/Android.bp | 1 + updater/Android.mk | 2 +- updater/blockimg.cpp | 40 ++-- updater/include/updater/install.h | 11 +- updater/include/updater/updater.h | 77 ++++++-- updater/install.cpp | 68 +++---- updater/updater.cpp | 302 +++++++++++++----------------- updater/updater_main.cpp | 108 +++++++++++ 9 files changed, 433 insertions(+), 354 deletions(-) create mode 100644 updater/updater_main.cpp diff --git a/tests/unit/updater_test.cpp b/tests/unit/updater_test.cpp index a0a7b66a..4a8d1e6f 100644 --- a/tests/unit/updater_test.cpp +++ b/tests/unit/updater_test.cpp @@ -57,16 +57,14 @@ using namespace std::string_literals; using PackageEntries = std::unordered_map; -struct selabel_handle* sehandle = nullptr; - static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code, - UpdaterInfo* info = nullptr) { + Updater* updater = nullptr) { std::unique_ptr e; int error_count = 0; ASSERT_EQ(0, ParseString(expr_str, &e, &error_count)); ASSERT_EQ(0, error_count); - State state(expr_str, info); + State state(expr_str, updater); std::string result; bool status = Evaluate(&state, e, &result); @@ -102,38 +100,6 @@ static void BuildUpdatePackage(const PackageEntries& entries, int fd) { ASSERT_EQ(0, fclose(zip_file_ptr)); } -static void RunBlockImageUpdate(bool is_verify, const PackageEntries& entries, - const std::string& image_file, const std::string& result, - CauseCode cause_code = kNoCause) { - CHECK(entries.find("transfer_list") != entries.end()); - - // Build the update package. - TemporaryFile zip_file; - BuildUpdatePackage(entries, zip_file.release()); - - MemMapping map; - ASSERT_TRUE(map.MapFile(zip_file.path)); - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); - - // Set up the handler, command_pipe, patch offset & length. - UpdaterInfo updater_info; - updater_info.package_zip = handle; - TemporaryFile temp_pipe; - updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); - updater_info.package_zip_addr = map.addr; - updater_info.package_zip_len = map.length; - - std::string new_data = entries.find("new_data.br") != entries.end() ? "new_data.br" : "new_data"; - std::string script = is_verify ? "block_image_verify" : "block_image_update"; - script += R"((")" + image_file + R"(", package_extract_file("transfer_list"), ")" + new_data + - R"(", "patch_data"))"; - expect(result.c_str(), script, cause_code, &updater_info); - - ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); - CloseArchive(handle); -} - static std::string GetSha1(std::string_view content) { uint8_t digest[SHA_DIGEST_LENGTH]; SHA1(reinterpret_cast(content.data()), content.size(), digest); @@ -159,29 +125,24 @@ static Value* BlobToString(const char* name, State* state, return args[0].release(); } -class UpdaterTest : public ::testing::Test { +class UpdaterTestBase { protected: - void SetUp() override { + void SetUp() { RegisterBuiltins(); RegisterInstallFunctions(); RegisterBlockImageFunctions(); - RegisterFunction("blob_to_string", BlobToString); - // Each test is run in a separate process (isolated mode). Shared temporary files won't cause // conflicts. Paths::Get().set_cache_temp_source(temp_saved_source_.path); Paths::Get().set_last_command_file(temp_last_command_.path); Paths::Get().set_stash_directory_base(temp_stash_base_.path); - // Enable a special command "abort" to simulate interruption. - Command::abort_allowed_ = true; - last_command_file_ = temp_last_command_.path; image_file_ = image_temp_file_.path; } - void TearDown() override { + void TearDown() { // Clean up the last_command_file if any. ASSERT_TRUE(android::base::RemoveFileIfExists(last_command_file_)); @@ -191,16 +152,80 @@ class UpdaterTest : public ::testing::Test { ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker)); } + void RunBlockImageUpdate(bool is_verify, PackageEntries entries, const std::string& image_file, + const std::string& result, CauseCode cause_code = kNoCause) { + CHECK(entries.find("transfer_list") != entries.end()); + std::string new_data = + entries.find("new_data.br") != entries.end() ? "new_data.br" : "new_data"; + std::string script = is_verify ? "block_image_verify" : "block_image_update"; + script += R"((")" + image_file + R"(", package_extract_file("transfer_list"), ")" + new_data + + R"(", "patch_data"))"; + entries.emplace(Updater::SCRIPT_NAME, script); + + // Build the update package. + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); + + // Set up the handler, command_pipe, patch offset & length. + TemporaryFile temp_pipe; + ASSERT_TRUE(updater_.Init(temp_pipe.release(), zip_file.path, false, nullptr)); + ASSERT_TRUE(updater_.RunUpdate()); + ASSERT_EQ(result, updater_.result()); + + // Parse the cause code written to the command pipe. + int received_cause_code = kNoCause; + std::string pipe_content; + ASSERT_TRUE(android::base::ReadFileToString(temp_pipe.path, &pipe_content)); + auto lines = android::base::Split(pipe_content, "\n"); + for (std::string_view line : lines) { + if (android::base::ConsumePrefix(&line, "log cause: ")) { + ASSERT_TRUE(android::base::ParseInt(line.data(), &received_cause_code)); + } + } + ASSERT_EQ(cause_code, received_cause_code); + } + TemporaryFile temp_saved_source_; TemporaryDir temp_stash_base_; std::string last_command_file_; std::string image_file_; + Updater updater_; + private: TemporaryFile temp_last_command_; TemporaryFile image_temp_file_; }; +class UpdaterTest : public UpdaterTestBase, public ::testing::Test { + protected: + void SetUp() override { + UpdaterTestBase::SetUp(); + + RegisterFunction("blob_to_string", BlobToString); + // Enable a special command "abort" to simulate interruption. + Command::abort_allowed_ = true; + } + + void TearDown() override { + UpdaterTestBase::TearDown(); + } + + void SetUpdaterCmdPipe(int fd) { + FILE* cmd_pipe = fdopen(fd, "w"); + ASSERT_NE(nullptr, cmd_pipe); + updater_.cmd_pipe_.reset(cmd_pipe); + } + + void SetUpdaterOtaPackageHandle(ZipArchiveHandle handle) { + updater_.package_handle_ = handle; + } + + void FlushUpdaterCommandPipe() const { + fflush(updater_.cmd_pipe_.get()); + } +}; + TEST_F(UpdaterTest, getprop) { expect(android::base::GetProperty("ro.product.device", "").c_str(), "getprop(\"ro.product.device\")", @@ -317,13 +342,12 @@ TEST_F(UpdaterTest, package_extract_file) { ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); // Need to set up the ziphandle. - UpdaterInfo updater_info; - updater_info.package_zip = handle; + SetUpdaterOtaPackageHandle(handle); // Two-argument version. TemporaryFile temp_file1; std::string script("package_extract_file(\"a.txt\", \"" + std::string(temp_file1.path) + "\")"); - expect("t", script, kNoCause, &updater_info); + expect("t", script, kNoCause, &updater_); // Verify the extracted entry. std::string data; @@ -332,32 +356,30 @@ TEST_F(UpdaterTest, package_extract_file) { // Now extract another entry to the same location, which should overwrite. script = "package_extract_file(\"b.txt\", \"" + std::string(temp_file1.path) + "\")"; - expect("t", script, kNoCause, &updater_info); + expect("t", script, kNoCause, &updater_); ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data)); ASSERT_EQ(kBTxtContents, data); // Missing zip entry. The two-argument version doesn't abort. script = "package_extract_file(\"doesntexist\", \"" + std::string(temp_file1.path) + "\")"; - expect("", script, kNoCause, &updater_info); + expect("", script, kNoCause, &updater_); // Extract to /dev/full should fail. script = "package_extract_file(\"a.txt\", \"/dev/full\")"; - expect("", script, kNoCause, &updater_info); + expect("", script, kNoCause, &updater_); // One-argument version. package_extract_file() gives a VAL_BLOB, which needs to be converted to // VAL_STRING for equality test. script = "blob_to_string(package_extract_file(\"a.txt\")) == \"" + kATxtContents + "\""; - expect("t", script, kNoCause, &updater_info); + expect("t", script, kNoCause, &updater_); script = "blob_to_string(package_extract_file(\"b.txt\")) == \"" + kBTxtContents + "\""; - expect("t", script, kNoCause, &updater_info); + expect("t", script, kNoCause, &updater_); // Missing entry. The one-argument version aborts the evaluation. script = "package_extract_file(\"doesntexist\")"; - expect(nullptr, script, kPackageExtractFileFailure, &updater_info); - - CloseArchive(handle); + expect(nullptr, script, kPackageExtractFileFailure, &updater_); } TEST_F(UpdaterTest, read_file) { @@ -563,17 +585,15 @@ TEST_F(UpdaterTest, set_progress) { expect(nullptr, "set_progress(\".3.5\")", kArgsParsingFailure); TemporaryFile tf; - UpdaterInfo updater_info; - updater_info.cmd_pipe = fdopen(tf.release(), "w"); - expect(".52", "set_progress(\".52\")", kNoCause, &updater_info); - fflush(updater_info.cmd_pipe); + SetUpdaterCmdPipe(tf.release()); + expect(".52", "set_progress(\".52\")", kNoCause, &updater_); + FlushUpdaterCommandPipe(); std::string cmd; ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd)); ASSERT_EQ(android::base::StringPrintf("set_progress %f\n", .52), cmd); // recovery-updater protocol expects 2 tokens ("set_progress "). ASSERT_EQ(2U, android::base::Split(cmd, " ").size()); - ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); } TEST_F(UpdaterTest, show_progress) { @@ -588,17 +608,15 @@ TEST_F(UpdaterTest, show_progress) { expect(nullptr, "show_progress(\".3\", \"5a\")", kArgsParsingFailure); TemporaryFile tf; - UpdaterInfo updater_info; - updater_info.cmd_pipe = fdopen(tf.release(), "w"); - expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_info); - fflush(updater_info.cmd_pipe); + SetUpdaterCmdPipe(tf.release()); + expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_); + FlushUpdaterCommandPipe(); std::string cmd; ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd)); ASSERT_EQ(android::base::StringPrintf("progress %f %d\n", .52, 10), cmd); // recovery-updater protocol expects 3 tokens ("progress "). ASSERT_EQ(3U, android::base::Split(cmd, " ").size()); - ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); } TEST_F(UpdaterTest, block_image_update_parsing_error) { @@ -993,44 +1011,20 @@ TEST_F(UpdaterTest, last_command_verify) { ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK)); } -class ResumableUpdaterTest : public testing::TestWithParam { +class ResumableUpdaterTest : public UpdaterTestBase, public testing::TestWithParam { protected: void SetUp() override { - RegisterBuiltins(); - RegisterInstallFunctions(); - RegisterBlockImageFunctions(); - - Paths::Get().set_cache_temp_source(temp_saved_source_.path); - Paths::Get().set_last_command_file(temp_last_command_.path); - Paths::Get().set_stash_directory_base(temp_stash_base_.path); - + UpdaterTestBase::SetUp(); // Enable a special command "abort" to simulate interruption. Command::abort_allowed_ = true; - index_ = GetParam(); - image_file_ = image_temp_file_.path; - last_command_file_ = temp_last_command_.path; } void TearDown() override { - // Clean up the last_command_file if any. - ASSERT_TRUE(android::base::RemoveFileIfExists(last_command_file_)); - - // Clear partition updated marker if any. - std::string updated_marker{ temp_stash_base_.path }; - updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED"; - ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker)); + UpdaterTestBase::TearDown(); } - TemporaryFile temp_saved_source_; - TemporaryDir temp_stash_base_; - std::string last_command_file_; - std::string image_file_; size_t index_; - - private: - TemporaryFile temp_last_command_; - TemporaryFile image_temp_file_; }; static std::string g_source_image; diff --git a/updater/Android.bp b/updater/Android.bp index b80cdb3a..daf7e327 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -70,6 +70,7 @@ cc_library_static { "commands.cpp", "dynamic_partitions.cpp", "install.cpp", + "updater.cpp", ], include_dirs: [ diff --git a/updater/Android.mk b/updater/Android.mk index c7a6ba98..0178239e 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -59,7 +59,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := updater LOCAL_SRC_FILES := \ - updater.cpp + updater_main.cpp LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index b008c28b..3089865c 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -1668,15 +1669,9 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, return StringValue(""); } - UpdaterInfo* ui = static_cast(state->cookie); - if (ui == nullptr) { - return StringValue(""); - } - - FILE* cmd_pipe = ui->cmd_pipe; - ZipArchiveHandle za = ui->package_zip; - - if (cmd_pipe == nullptr || za == nullptr) { + auto updater = static_cast(state->cookie); + ZipArchiveHandle za = updater->package_handle(); + if (za == nullptr) { return StringValue(""); } @@ -1686,8 +1681,8 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, LOG(ERROR) << name << "(): no file \"" << patch_data_fn->data << "\" in package"; return StringValue(""); } + params.patch_start = updater->GetMappedPackageAddress() + patch_entry.offset; - params.patch_start = ui->package_zip_addr + patch_entry.offset; std::string_view new_data(new_data_fn->data); ZipEntry new_entry; if (FindEntry(za, new_data, &new_entry) != 0) { @@ -1887,8 +1882,10 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, LOG(WARNING) << "Failed to update the last command file."; } - fprintf(cmd_pipe, "set_progress %.4f\n", static_cast(params.written) / total_blocks); - fflush(cmd_pipe); + updater->WriteToCommandPipe( + android::base::StringPrintf("set_progress %.4f", + static_cast(params.written) / total_blocks), + true); } } @@ -1915,11 +1912,13 @@ pbiudone: const char* partition = strrchr(blockdev_filename->data.c_str(), '/'); if (partition != nullptr && *(partition + 1) != 0) { - fprintf(cmd_pipe, "log bytes_written_%s: %" PRIu64 "\n", partition + 1, - static_cast(params.written) * BLOCKSIZE); - fprintf(cmd_pipe, "log bytes_stashed_%s: %" PRIu64 "\n", partition + 1, - static_cast(params.stashed) * BLOCKSIZE); - fflush(cmd_pipe); + updater->WriteToCommandPipe( + android::base::StringPrintf("log bytes_written_%s: %" PRIu64, partition + 1, + static_cast(params.written) * BLOCKSIZE)); + updater->WriteToCommandPipe( + android::base::StringPrintf("log bytes_stashed_%s: %" PRIu64, partition + 1, + static_cast(params.stashed) * BLOCKSIZE), + true); } // Delete stash only after successfully completing the update, as it may contain blocks needed // to complete the update later. @@ -2172,8 +2171,11 @@ Value* CheckFirstBlockFn(const char* name, State* state, uint16_t mount_count = *reinterpret_cast(&block0_buffer[0x400 + 0x34]); if (mount_count > 0) { - uiPrintf(state, "Device was remounted R/W %" PRIu16 " times", mount_count); - uiPrintf(state, "Last remount happened on %s", ctime(&mount_time)); + auto updater = static_cast(state->cookie); + updater->UiPrint( + android::base::StringPrintf("Device was remounted R/W %" PRIu16 " times", mount_count)); + updater->UiPrint( + android::base::StringPrintf("Last remount happened on %s", ctime(&mount_time))); } return StringValue("t"); diff --git a/updater/include/updater/install.h b/updater/include/updater/install.h index 8d6ca472..9fe20314 100644 --- a/updater/include/updater/install.h +++ b/updater/include/updater/install.h @@ -14,15 +14,6 @@ * limitations under the License. */ -#ifndef _UPDATER_INSTALL_H_ -#define _UPDATER_INSTALL_H_ - -struct State; +#pragma once void RegisterInstallFunctions(); - -// uiPrintf function prints msg to screen as well as logs -void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) - __attribute__((__format__(printf, 2, 3))); - -#endif diff --git a/updater/include/updater/updater.h b/updater/include/updater/updater.h index f4a2fe87..d5468292 100644 --- a/updater/include/updater/updater.h +++ b/updater/include/updater/updater.h @@ -14,22 +14,75 @@ * limitations under the License. */ -#ifndef _UPDATER_UPDATER_H_ -#define _UPDATER_UPDATER_H_ +#pragma once +#include #include + +#include +#include + #include -typedef struct { - FILE* cmd_pipe; - ZipArchiveHandle package_zip; - int version; - - uint8_t* package_zip_addr; - size_t package_zip_len; -} UpdaterInfo; +#include "edify/expr.h" +#include "otautil/error_code.h" +#include "otautil/sysutil.h" struct selabel_handle; -extern struct selabel_handle *sehandle; -#endif +class Updater { + public: + ~Updater(); + + // Memory-maps the OTA package and opens it as a zip file. Also sets up the command pipe and + // selabel handle. TODO(xunchang) implement a run time environment class and move sehandle there. + bool Init(int fd, const std::string& package_filename, bool is_retry, + struct selabel_handle* sehandle); + + // Parses and evaluates the updater-script in the OTA package. Reports the error code if the + // evaluation fails. + bool RunUpdate(); + + // Writes the message to command pipe, adds a new line in the end. + void WriteToCommandPipe(const std::string& message, bool flush = false) const; + + // Sends over the message to recovery to print it on the screen. + void UiPrint(const std::string& message) const; + + ZipArchiveHandle package_handle() const { + return package_handle_; + } + struct selabel_handle* sehandle() const { + return sehandle_; + } + std::string result() const { + return result_; + } + + uint8_t* GetMappedPackageAddress() const { + return mapped_package_.addr; + } + + private: + friend class UpdaterTestBase; + friend class UpdaterTest; + // Where in the package we expect to find the edify script to execute. + // (Note it's "updateR-script", not the older "update-script".) + static constexpr const char* SCRIPT_NAME = "META-INF/com/google/android/updater-script"; + + // Reads the entry |name| in the zip archive and put the result in |content|. + bool ReadEntryToString(ZipArchiveHandle za, const std::string& entry_name, std::string* content); + + // Parses the error code embedded in state->errmsg; and reports the error code and cause code. + void ParseAndReportErrorCode(State* state); + + MemMapping mapped_package_; + ZipArchiveHandle package_handle_{ nullptr }; + std::string updater_script_; + + bool is_retry_{ false }; + std::unique_ptr cmd_pipe_{ nullptr, fclose }; + struct selabel_handle* sehandle_{ nullptr }; + + std::string result_; +}; diff --git a/updater/install.cpp b/updater/install.cpp index c30f6396..b4d88403 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -64,36 +64,6 @@ #include "otautil/sysutil.h" #include "updater/updater.h" -// Send over the buffer to recovery though the command pipe. -static void uiPrint(State* state, const std::string& buffer) { - UpdaterInfo* ui = static_cast(state->cookie); - - // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "". - // So skip sending empty strings to UI. - std::vector lines = android::base::Split(buffer, "\n"); - for (auto& line : lines) { - if (!line.empty()) { - fprintf(ui->cmd_pipe, "ui_print %s\n", line.c_str()); - } - } - - // On the updater side, we need to dump the contents to stderr (which has - // been redirected to the log file). Because the recovery will only print - // the contents to screen when processing pipe command ui_print. - LOG(INFO) << buffer; -} - -void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) { - std::string error_msg; - - va_list ap; - va_start(ap, format); - android::base::StringAppendV(&error_msg, format, ap); - va_end(ap); - - uiPrint(state, error_msg); -} - // This is the updater side handler for ui_print() in edify script. Contents will be sent over to // the recovery side for on-screen display. Value* UIPrintFn(const char* name, State* state, const std::vector>& argv) { @@ -103,7 +73,7 @@ Value* UIPrintFn(const char* name, State* state, const std::vector(state->cookie)->UiPrint(buffer); return StringValue(buffer); } @@ -129,7 +99,7 @@ Value* PackageExtractFileFn(const char* name, State* state, const std::string& zip_path = args[0]; const std::string& dest_path = args[1]; - ZipArchiveHandle za = static_cast(state->cookie)->package_zip; + ZipArchiveHandle za = static_cast(state->cookie)->package_handle(); ZipEntry entry; if (FindEntry(za, zip_path, &entry) != 0) { LOG(ERROR) << name << ": no " << zip_path << " in package"; @@ -172,7 +142,7 @@ Value* PackageExtractFileFn(const char* name, State* state, } const std::string& zip_path = args[0]; - ZipArchiveHandle za = static_cast(state->cookie)->package_zip; + ZipArchiveHandle za = static_cast(state->cookie)->package_handle(); ZipEntry entry; if (FindEntry(za, zip_path, &entry) != 0) { return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name, @@ -311,11 +281,11 @@ Value* MountFn(const char* name, State* state, const std::vector(state->cookie); { char* secontext = nullptr; - - if (sehandle) { - selabel_lookup(sehandle, &secontext, mount_point.c_str(), 0755); + if (updater->sehandle()) { + selabel_lookup(updater->sehandle(), &secontext, mount_point.c_str(), 0755); setfscreatecon(secontext); } @@ -329,8 +299,9 @@ Value* MountFn(const char* name, State* state, const std::vectorUiPrint(android::base::StringPrintf("%s: Failed to mount %s at %s: %s", name, + location.c_str(), mount_point.c_str(), + strerror(errno))); return StringValue(""); } @@ -376,15 +347,18 @@ Value* UnmountFn(const char* name, State* state, const std::vector(state->cookie); scan_mounted_volumes(); MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str()); if (vol == nullptr) { - uiPrintf(state, "Failed to unmount %s: No such volume", mount_point.c_str()); + updater->UiPrint( + android::base::StringPrintf("Failed to unmount %s: No such volume", mount_point.c_str())); return nullptr; } else { int ret = unmount_mounted_volume(vol); if (ret != 0) { - uiPrintf(state, "Failed to unmount %s: %s", mount_point.c_str(), strerror(errno)); + updater->UiPrint(android::base::StringPrintf("Failed to unmount %s: %s", mount_point.c_str(), + strerror(errno))); } } @@ -529,8 +503,8 @@ Value* ShowProgressFn(const char* name, State* state, sec_str.c_str()); } - UpdaterInfo* ui = static_cast(state->cookie); - fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec); + auto updater = static_cast(state->cookie); + updater->WriteToCommandPipe(android::base::StringPrintf("progress %f %d", frac, sec)); return StringValue(frac_str); } @@ -553,8 +527,8 @@ Value* SetProgressFn(const char* name, State* state, frac_str.c_str()); } - UpdaterInfo* ui = static_cast(state->cookie); - fprintf(ui->cmd_pipe, "set_progress %f\n", frac); + auto updater = static_cast(state->cookie); + updater->WriteToCommandPipe(android::base::StringPrintf("set_progress %f", frac)); return StringValue(frac_str); } @@ -653,7 +627,8 @@ Value* WipeCacheFn(const char* name, State* state, const std::vector(state->cookie)->cmd_pipe, "wipe_cache\n"); + + static_cast(state->cookie)->WriteToCommandPipe("wipe_cache"); return StringValue("t"); } @@ -881,8 +856,7 @@ Value* EnableRebootFn(const char* name, State* state, const std::vector(state->cookie); - fprintf(ui->cmd_pipe, "enable_reboot\n"); + static_cast(state->cookie)->WriteToCommandPipe("enable_reboot"); return StringValue("t"); } diff --git a/updater/updater.cpp b/updater/updater.cpp index a020699c..e0679fb0 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -16,8 +16,6 @@ #include "updater/updater.h" -#include -#include #include #include @@ -25,197 +23,155 @@ #include #include -#include -#include -#include -#include -#include "edify/expr.h" -#include "otautil/dirutil.h" -#include "otautil/error_code.h" -#include "otautil/sysutil.h" -#include "updater/blockimg.h" -#include "updater/dynamic_partitions.h" -#include "updater/install.h" - -// Generated by the makefile, this function defines the -// RegisterDeviceExtensions() function, which calls all the -// registration functions for device-specific extensions. -#include "register.inc" - -// Where in the package we expect to find the edify script to execute. -// (Note it's "updateR-script", not the older "update-script".) -static constexpr const char* SCRIPT_NAME = "META-INF/com/google/android/updater-script"; - -struct selabel_handle *sehandle; - -static void UpdaterLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */, - const char* /* tag */, const char* /* file */, unsigned int /* line */, - const char* message) { - fprintf(stdout, "%s\n", message); +Updater::~Updater() { + if (package_handle_) { + CloseArchive(package_handle_); + } } -int main(int argc, char** argv) { - // Various things log information to stdout or stderr more or less - // at random (though we've tried to standardize on stdout). The - // log file makes more sense if buffering is turned off so things - // appear in the right order. - setbuf(stdout, nullptr); - setbuf(stderr, nullptr); - - // We don't have logcat yet under recovery. Update logs will always be written to stdout - // (which is redirected to recovery.log). - android::base::InitLogging(argv, &UpdaterLogger); - - if (argc != 4 && argc != 5) { - LOG(ERROR) << "unexpected number of arguments: " << argc; - return 1; - } - - char* version = argv[1]; - if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') { - // We support version 1, 2, or 3. - LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1]; - return 2; - } - +bool Updater::Init(int fd, const std::string& package_filename, bool is_retry, + struct selabel_handle* sehandle) { // Set up the pipe for sending commands back to the parent process. - - int fd = atoi(argv[2]); - FILE* cmd_pipe = fdopen(fd, "wb"); - setlinebuf(cmd_pipe); - - // Extract the script from the package. - - const char* package_filename = argv[3]; - MemMapping map; - if (!map.MapFile(package_filename)) { - LOG(ERROR) << "failed to map package " << argv[3]; - return 3; - } - ZipArchiveHandle za; - int open_err = OpenArchiveFromMemory(map.addr, map.length, argv[3], &za); - if (open_err != 0) { - LOG(ERROR) << "failed to open package " << argv[3] << ": " << ErrorCodeString(open_err); - CloseArchive(za); - return 3; + cmd_pipe_.reset(fdopen(fd, "wb")); + if (!cmd_pipe_) { + LOG(ERROR) << "Failed to open the command pipe"; + return false; } - ZipEntry script_entry; - int find_err = FindEntry(za, SCRIPT_NAME, &script_entry); - if (find_err != 0) { - LOG(ERROR) << "failed to find " << SCRIPT_NAME << " in " << package_filename << ": " - << ErrorCodeString(find_err); - CloseArchive(za); - return 4; + setlinebuf(cmd_pipe_.get()); + + if (!mapped_package_.MapFile(package_filename)) { + LOG(ERROR) << "failed to map package " << package_filename; + return false; + } + if (int open_err = OpenArchiveFromMemory(mapped_package_.addr, mapped_package_.length, + package_filename.c_str(), &package_handle_); + open_err != 0) { + LOG(ERROR) << "failed to open package " << package_filename << ": " + << ErrorCodeString(open_err); + return false; + } + if (!ReadEntryToString(package_handle_, SCRIPT_NAME, &updater_script_)) { + return false; } - std::string script; - script.resize(script_entry.uncompressed_length); - int extract_err = ExtractToMemory(za, &script_entry, reinterpret_cast(&script[0]), - script_entry.uncompressed_length); - if (extract_err != 0) { - LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err); - CloseArchive(za); - return 5; + is_retry_ = is_retry; + + sehandle_ = sehandle; + if (!sehandle_) { + fprintf(cmd_pipe_.get(), "ui_print Warning: No file_contexts\n"); } + return true; +} - // Configure edify's functions. - - RegisterBuiltins(); - RegisterInstallFunctions(); - RegisterBlockImageFunctions(); - RegisterDynamicPartitionsFunctions(); - RegisterDeviceExtensions(); - +bool Updater::RunUpdate() { // Parse the script. - std::unique_ptr root; int error_count = 0; - int error = ParseString(script, &root, &error_count); + int error = ParseString(updater_script_, &root, &error_count); if (error != 0 || error_count > 0) { LOG(ERROR) << error_count << " parse errors"; - CloseArchive(za); - return 6; - } - - sehandle = selinux_android_file_context_handle(); - selinux_android_set_sehandle(sehandle); - - if (!sehandle) { - fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n"); + return false; } // Evaluate the parsed script. + State state(updater_script_, this); + state.is_retry = is_retry_; - UpdaterInfo updater_info; - updater_info.cmd_pipe = cmd_pipe; - updater_info.package_zip = za; - updater_info.version = atoi(version); - updater_info.package_zip_addr = map.addr; - updater_info.package_zip_len = map.length; - - State state(script, &updater_info); - - if (argc == 5) { - if (strcmp(argv[4], "retry") == 0) { - state.is_retry = true; - } else { - printf("unexpected argument: %s", argv[4]); + bool status = Evaluate(&state, root, &result_); + if (status) { + fprintf(cmd_pipe_.get(), "ui_print script succeeded: result was [%s]\n", result_.c_str()); + // Even though the script doesn't abort, still log the cause code if result is empty. + if (result_.empty() && state.cause_code != kNoCause) { + fprintf(cmd_pipe_.get(), "log cause: %d\n", state.cause_code); } + return true; } - std::string result; - bool status = Evaluate(&state, root, &result); - - if (!status) { - if (state.errmsg.empty()) { - LOG(ERROR) << "script aborted (no error message)"; - fprintf(cmd_pipe, "ui_print script aborted (no error message)\n"); - } else { - LOG(ERROR) << "script aborted: " << state.errmsg; - const std::vector lines = android::base::Split(state.errmsg, "\n"); - for (const std::string& line : lines) { - // Parse the error code in abort message. - // Example: "E30: This package is for bullhead devices." - if (!line.empty() && line[0] == 'E') { - if (sscanf(line.c_str(), "E%d: ", &state.error_code) != 1) { - LOG(ERROR) << "Failed to parse error code: [" << line << "]"; - } - } - fprintf(cmd_pipe, "ui_print %s\n", line.c_str()); - } - } - - // Installation has been aborted. Set the error code to kScriptExecutionFailure unless - // a more specific code has been set in errmsg. - if (state.error_code == kNoError) { - state.error_code = kScriptExecutionFailure; - } - fprintf(cmd_pipe, "log error: %d\n", state.error_code); - // Cause code should provide additional information about the abort. - if (state.cause_code != kNoCause) { - fprintf(cmd_pipe, "log cause: %d\n", state.cause_code); - if (state.cause_code == kPatchApplicationFailure) { - LOG(INFO) << "Patch application failed, retry update."; - fprintf(cmd_pipe, "retry_update\n"); - } else if (state.cause_code == kEioFailure) { - LOG(INFO) << "Update failed due to EIO, retry update."; - fprintf(cmd_pipe, "retry_update\n"); - } - } - - if (updater_info.package_zip) { - CloseArchive(updater_info.package_zip); - } - return 7; - } else { - fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]\n", result.c_str()); - } - - if (updater_info.package_zip) { - CloseArchive(updater_info.package_zip); - } - - return 0; + ParseAndReportErrorCode(&state); + return false; +} + +void Updater::WriteToCommandPipe(const std::string& message, bool flush) const { + fprintf(cmd_pipe_.get(), "%s\n", message.c_str()); + if (flush) { + fflush(cmd_pipe_.get()); + } +} + +void Updater::UiPrint(const std::string& message) const { + // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "". + // so skip sending empty strings to ui. + std::vector lines = android::base::Split(message, "\n"); + for (const auto& line : lines) { + if (!line.empty()) { + fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str()); + } + } + + // on the updater side, we need to dump the contents to stderr (which has + // been redirected to the log file). because the recovery will only print + // the contents to screen when processing pipe command ui_print. + LOG(INFO) << message; +} + +void Updater::ParseAndReportErrorCode(State* state) { + CHECK(state); + if (state->errmsg.empty()) { + LOG(ERROR) << "script aborted (no error message)"; + fprintf(cmd_pipe_.get(), "ui_print script aborted (no error message)\n"); + } else { + LOG(ERROR) << "script aborted: " << state->errmsg; + const std::vector lines = android::base::Split(state->errmsg, "\n"); + for (const std::string& line : lines) { + // Parse the error code in abort message. + // Example: "E30: This package is for bullhead devices." + if (!line.empty() && line[0] == 'E') { + if (sscanf(line.c_str(), "E%d: ", &state->error_code) != 1) { + LOG(ERROR) << "Failed to parse error code: [" << line << "]"; + } + } + fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str()); + } + } + + // Installation has been aborted. Set the error code to kScriptExecutionFailure unless + // a more specific code has been set in errmsg. + if (state->error_code == kNoError) { + state->error_code = kScriptExecutionFailure; + } + fprintf(cmd_pipe_.get(), "log error: %d\n", state->error_code); + // Cause code should provide additional information about the abort. + if (state->cause_code != kNoCause) { + fprintf(cmd_pipe_.get(), "log cause: %d\n", state->cause_code); + if (state->cause_code == kPatchApplicationFailure) { + LOG(INFO) << "Patch application failed, retry update."; + fprintf(cmd_pipe_.get(), "retry_update\n"); + } else if (state->cause_code == kEioFailure) { + LOG(INFO) << "Update failed due to EIO, retry update."; + fprintf(cmd_pipe_.get(), "retry_update\n"); + } + } +} + +bool Updater::ReadEntryToString(ZipArchiveHandle za, const std::string& entry_name, + std::string* content) { + ZipEntry entry; + int find_err = FindEntry(za, entry_name, &entry); + if (find_err != 0) { + LOG(ERROR) << "failed to find " << entry_name + << " in the package: " << ErrorCodeString(find_err); + return false; + } + + content->resize(entry.uncompressed_length); + int extract_err = ExtractToMemory(za, &entry, reinterpret_cast(&content->at(0)), + entry.uncompressed_length); + if (extract_err != 0) { + LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err); + return false; + } + + return true; } diff --git a/updater/updater_main.cpp b/updater/updater_main.cpp new file mode 100644 index 00000000..dd22c137 --- /dev/null +++ b/updater/updater_main.cpp @@ -0,0 +1,108 @@ +/* + * 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 +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "edify/expr.h" +#include "updater/blockimg.h" +#include "updater/dynamic_partitions.h" +#include "updater/install.h" +#include "updater/updater.h" + +// Generated by the makefile, this function defines the +// RegisterDeviceExtensions() function, which calls all the +// registration functions for device-specific extensions. +#include "register.inc" + +static void UpdaterLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */, + const char* /* tag */, const char* /* file */, unsigned int /* line */, + const char* message) { + fprintf(stdout, "%s\n", message); +} + +int main(int argc, char** argv) { + // Various things log information to stdout or stderr more or less + // at random (though we've tried to standardize on stdout). The + // log file makes more sense if buffering is turned off so things + // appear in the right order. + setbuf(stdout, nullptr); + setbuf(stderr, nullptr); + + // We don't have logcat yet under recovery. Update logs will always be written to stdout + // (which is redirected to recovery.log). + android::base::InitLogging(argv, &UpdaterLogger); + + if (argc != 4 && argc != 5) { + LOG(ERROR) << "unexpected number of arguments: " << argc; + return 1; + } + + char* version = argv[1]; + if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') { + // We support version 1, 2, or 3. + LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1]; + return 1; + } + + int fd; + if (!android::base::ParseInt(argv[2], &fd)) { + LOG(ERROR) << "Failed to parse fd in " << argv[2]; + return 1; + } + + std::string package_name = argv[3]; + + bool is_retry = false; + if (argc == 5) { + if (strcmp(argv[4], "retry") == 0) { + is_retry = true; + } else { + LOG(ERROR) << "unexpected argument: " << argv[4]; + return 1; + } + } + + // Configure edify's functions. + RegisterBuiltins(); + RegisterInstallFunctions(); + RegisterBlockImageFunctions(); + RegisterDynamicPartitionsFunctions(); + RegisterDeviceExtensions(); + + auto sehandle = selinux_android_file_context_handle(); + selinux_android_set_sehandle(sehandle); + + Updater updater; + if (!updater.Init(fd, package_name, is_retry, sehandle)) { + return 1; + } + + if (!updater.RunUpdate()) { + return 1; + } + + return 0; +} \ No newline at end of file From 3fda5d650886301eb324083511392f42676e1b85 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 9 May 2019 10:58:10 -0700 Subject: [PATCH 033/171] Skip hashtree computation during block image verify The hashtree computation is designed to execute after we write all the bytes to the target block device. And executing the command during block image verify will almost always fail since we are still on the source build. Test: run simulator Change-Id: If8ebb66739969520367a0815f5f8f89f6fae47cf --- updater/blockimg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 3089865c..3b2b2c02 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -2018,7 +2018,7 @@ Value* BlockImageVerifyFn(const char* name, State* state, // clang-format off { Command::Type::ABORT, PerformCommandAbort }, { Command::Type::BSDIFF, PerformCommandDiff }, - { Command::Type::COMPUTE_HASH_TREE, PerformCommandComputeHashTree }, + { Command::Type::COMPUTE_HASH_TREE, nullptr }, { Command::Type::ERASE, nullptr }, { Command::Type::FREE, PerformCommandFree }, { Command::Type::IMGDIFF, PerformCommandDiff }, From 84efc1ba7d7c2b4cdbc8dc7b154476cd7b50ea40 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 9 May 2019 17:26:20 -0700 Subject: [PATCH 034/171] Import translations. DO NOT MERGE Change-Id: Ia35cdac295a1ce6f8b53638c908c0caf2a97477c Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-in/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/recovery_l10n/res/values-in/strings.xml b/tools/recovery_l10n/res/values-in/strings.xml index 15a78ec4..43c9deb9 100644 --- a/tools/recovery_l10n/res/values-in/strings.xml +++ b/tools/recovery_l10n/res/values-in/strings.xml @@ -9,6 +9,6 @@ "Tidak dapat memuat sistem Android. Data Anda mungkin rusak. Jika terus mendapatkan pesan ini, Anda mungkin perlu melakukan reset ke setelan pabrik dan menghapus semua data pengguna yang disimpan di perangkat ini." "Coba lagi" "Reset ke setelan pabrik" - "Wipe semua data pengguna?\n\n TINDAKAN INI TIDAK DAPAT DIURUNGKAN!" + "Hapus total semua data pengguna?\n\n TINDAKAN INI TIDAK DAPAT DIURUNGKAN!" "Batal" From f90d9a102f8669560b19340536596018c63ce1b2 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 10 May 2019 10:40:59 -0700 Subject: [PATCH 035/171] roots: Remove get_system_root and logical_partitions_mapped. Test: TreeHugger Test: Boot into recovery on blueline. Choose "Mount system partition". Change-Id: Iac475d18ce2415de09dc0bf009ad4cf0383ffede --- fsck_unshare_blocks.cpp | 3 ++- otautil/include/otautil/roots.h | 4 ---- otautil/roots.cpp | 8 -------- recovery.cpp | 4 ++-- recovery_main.cpp | 3 ++- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp index 0f8fffac..9dc0fd8e 100644 --- a/fsck_unshare_blocks.cpp +++ b/fsck_unshare_blocks.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "otautil/roots.h" @@ -119,7 +120,7 @@ bool do_fsck_unshare_blocks() { std::vector partitions = { "/odm", "/oem", "/product", "/vendor" }; // Temporarily mount system so we can copy e2fsck_static. - std::string system_root = get_system_root(); + auto system_root = android::fs_mgr::GetSystemRoot(); bool mounted = ensure_path_mounted_at(system_root, "/mnt/system") != -1; partitions.push_back(system_root); diff --git a/otautil/include/otautil/roots.h b/otautil/include/otautil/roots.h index 482f3d05..2ab3f454 100644 --- a/otautil/include/otautil/roots.h +++ b/otautil/include/otautil/roots.h @@ -53,7 +53,3 @@ int format_volume(const std::string& volume, const std::string& directory); // Ensure that all and only the volumes that packages expect to find // mounted (/tmp and /cache) are mounted. Returns 0 on success. int setup_install_mounts(); - -bool logical_partitions_mapped(); - -std::string get_system_root(); diff --git a/otautil/roots.cpp b/otautil/roots.cpp index 815d644e..a778e05f 100644 --- a/otautil/roots.cpp +++ b/otautil/roots.cpp @@ -275,11 +275,3 @@ int setup_install_mounts() { } return 0; } - -bool logical_partitions_mapped() { - return android::fs_mgr::LogicalPartitionsMapped(); -} - -std::string get_system_root() { - return android::fs_mgr::GetSystemRoot(); -} diff --git a/recovery.cpp b/recovery.cpp index dbac3e01..20e5a1be 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -41,6 +41,7 @@ #include #include #include /* for property_list */ +#include #include #include @@ -437,8 +438,7 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) break; } case Device::MOUNT_SYSTEM: - // the system partition is mounted at /mnt/system - if (ensure_path_mounted_at(get_system_root(), "/mnt/system") != -1) { + if (ensure_path_mounted_at(android::fs_mgr::GetSystemRoot(), "/mnt/system") != -1) { ui->Print("Mounted /system.\n"); } break; diff --git a/recovery_main.cpp b/recovery_main.cpp index b999505f..aba9c5d7 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include /* private pmsg functions */ #include #include @@ -505,7 +506,7 @@ int main(int argc, char** argv) { } case Device::ENTER_FASTBOOT: - if (logical_partitions_mapped()) { + if (android::fs_mgr::LogicalPartitionsMapped()) { ui->Print("Partitions may be mounted - rebooting to enter fastboot."); Reboot("fastboot"); } else { From 6f4e4db4f9e0911a07c6393d01e4380e844f7891 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Mon, 20 May 2019 10:36:16 -0700 Subject: [PATCH 036/171] recovery: report compliant reboot reason shutdown and reboot should have a corresponding sub-reason. Adding: "reboot,fastboot_menu" "reboot,recovery_menu" "reboot,recovery_ui" "shutdown,fastboot" "shutdown,recovery" "reboot,unknown#" Test: none Change-Id: Icf1ab0d462ec2de2272914a36994a095998d6186 --- fastboot/fastboot.cpp | 4 ++-- otautil/include/otautil/sysutil.h | 4 ++-- otautil/sysutil.cpp | 6 +++--- recovery.cpp | 2 ++ recovery_main.cpp | 19 +++++++++++++++++-- recovery_ui/include/recovery_ui/device.h | 2 ++ recovery_ui/ui.cpp | 2 +- 7 files changed, 29 insertions(+), 10 deletions(-) diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 14f5e4bd..20233499 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -30,10 +30,10 @@ #include "recovery_ui/ui.h" static const std::vector> kFastbootMenuActions{ - { "Reboot system now", Device::REBOOT }, + { "Reboot system now", Device::REBOOT_FROM_FASTBOOT }, { "Enter recovery", Device::ENTER_RECOVERY }, { "Reboot to bootloader", Device::REBOOT_BOOTLOADER }, - { "Power off", Device::SHUTDOWN }, + { "Power off", Device::SHUTDOWN_FROM_FASTBOOT }, }; Device::BuiltinAction StartFastboot(Device* device, const std::vector& /* args */) { diff --git a/otautil/include/otautil/sysutil.h b/otautil/include/otautil/sysutil.h index 48e9011e..326db864 100644 --- a/otautil/include/otautil/sysutil.h +++ b/otautil/include/otautil/sysutil.h @@ -102,11 +102,11 @@ class MemMapping { }; // Reboots the device into the specified target, by additionally handling quiescent reboot mode. -// 'target' can be an empty string, which indicates booting into Android. +// All unknown targets reboot into Android. bool Reboot(std::string_view target); // Triggers a shutdown. -bool Shutdown(); +bool Shutdown(std::string_view target); // Returns a null-terminated char* array, where the elements point to the C-strings in the given // vector, plus an additional nullptr at the end. This is a helper function that facilitates diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp index 420db4ca..a8829858 100644 --- a/otautil/sysutil.cpp +++ b/otautil/sysutil.cpp @@ -229,9 +229,9 @@ bool Reboot(std::string_view target) { return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd); } -bool Shutdown() { - // "shutdown" doesn't need a "reason" arg nor a comma. - return android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown"); +bool Shutdown(std::string_view target) { + std::string cmd = "shutdown," + std::string(target); + return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd); } std::vector StringVectorToNullTerminatedArray(const std::vector& args) { diff --git a/recovery.cpp b/recovery.cpp index 20e5a1be..eb0c2b21 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -356,6 +356,8 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) : device->InvokeMenuItem(chosen_item); switch (chosen_action) { + case Device::REBOOT_FROM_FASTBOOT: // Can not happen + case Device::SHUTDOWN_FROM_FASTBOOT: // Can not happen case Device::NO_ACTION: break; diff --git a/recovery_main.cpp b/recovery_main.cpp index aba9c5d7..f78c1c78 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -471,7 +471,12 @@ int main(int argc, char** argv) { switch (ret) { case Device::SHUTDOWN: ui->Print("Shutting down...\n"); - Shutdown(); + Shutdown("recovery"); + break; + + case Device::SHUTDOWN_FROM_FASTBOOT: + ui->Print("Shutting down...\n"); + Shutdown("fastboot"); break; case Device::REBOOT_BOOTLOADER: @@ -520,9 +525,19 @@ int main(int argc, char** argv) { fastboot = false; break; + case Device::REBOOT: + ui->Print("Rebooting...\n"); + Reboot("recovery_menu"); + break; + + case Device::REBOOT_FROM_FASTBOOT: + ui->Print("Rebooting...\n"); + Reboot("fastboot_menu"); + break; + default: ui->Print("Rebooting...\n"); - Reboot(""); + Reboot("unknown" + std::to_string(ret)); break; } } diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 7c76cdb0..9a4edf26 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -58,6 +58,8 @@ class Device { REBOOT_FASTBOOT = 17, REBOOT_RECOVERY = 18, REBOOT_RESCUE = 19, + REBOOT_FROM_FASTBOOT = 20, + SHUTDOWN_FROM_FASTBOOT = 21, }; explicit Device(RecoveryUI* ui); diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index 7ea9307c..fc517757 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -375,7 +375,7 @@ void RecoveryUI::ProcessKey(int key_code, int updown) { case RecoveryUI::REBOOT: if (reboot_enabled) { - Reboot(""); + Reboot("recovery_ui"); while (true) { pause(); } From 7ae01698424cc3adf635c324961b1405594f5156 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 16 May 2019 14:42:42 -0700 Subject: [PATCH 037/171] Add misc_writer. bootloader_message.h currently divides /misc into four segments. The space between 2K and 16K is reserved for vendor use (e.g. bootloader persists flags). This CL adds a vendor tool "misc_writer", to allow writing data to the vendor space in /misc, before getting a dedicated HAL for accessing /misc partition (b/131775112). Targets need to explicitly include the module, then invoke the executable to write data. For example, the following command will write 3-byte data ("0xABCDEF") to offset 4 in vendor space (i.e. 2048 + 4 in /misc). $ /vendor/bin/misc_writer --vendor-space-offset 4 --hex-string 0xABCDEF Bug: 132906936 Test: Run recovery_unit_test on crosshatch. Test: Call the command via init.hardware.rc on crosshatch. Check that the call finishes successfully. Then check the contents written to /misc (`dd bs=1 skip=2048 if=/dev/block/sda2 count=32 | xxd`). Change-Id: I79548fc63fc79b705a0320868690569c3106949f --- bootloader_message/Android.bp | 34 ++++-- bootloader_message/bootloader_message.cpp | 42 +++++++ .../bootloader_message/bootloader_message.h | 13 ++- misc_writer/Android.bp | 40 +++++++ misc_writer/misc_writer.cpp | 106 ++++++++++++++++++ tests/unit/bootloader_message_test.cpp | 38 +++++++ 6 files changed, 259 insertions(+), 14 deletions(-) create mode 100644 misc_writer/Android.bp create mode 100644 misc_writer/misc_writer.cpp diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp index e76305fd..8d72a116 100644 --- a/bootloader_message/Android.bp +++ b/bootloader_message/Android.bp @@ -14,10 +14,8 @@ // limitations under the License. // -cc_library { - name: "libbootloader_message", - recovery_available: true, - host_supported: true, +cc_defaults { + name: "libbootloader_message_defaults", srcs: ["bootloader_message.cpp"], cflags: [ "-Wall", @@ -26,24 +24,36 @@ cc_library { shared_libs: [ "libbase", ], + static_libs: [ + "libfstab", + ], export_include_dirs: ["include"], +} + +cc_library { + name: "libbootloader_message", + defaults: [ + "libbootloader_message_defaults", + ], + recovery_available: true, + host_supported: true, target: { - android: { - shared_libs: [ - "libfs_mgr", - ], - }, host: { shared_libs: [ "libcutils", // for strlcpy ], - static_libs: [ - "libfstab", - ], }, darwin: { enabled: false, }, } } + +cc_library_static { + name: "libbootloader_message_vendor", + defaults: [ + "libbootloader_message_defaults", + ], + vendor: true, +} diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index 331a42b2..e684abba 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -36,7 +37,17 @@ using android::fs_mgr::Fstab; using android::fs_mgr::ReadDefaultFstab; +static std::string g_misc_device_for_test; + +// Exposed for test purpose. +void SetMiscBlockDeviceForTest(std::string_view misc_device) { + g_misc_device_for_test = misc_device; +} + static std::string get_misc_blk_device(std::string* err) { + if (!g_misc_device_for_test.empty()) { + return g_misc_device_for_test; + } Fstab fstab; if (!ReadDefaultFstab(&fstab)) { *err = "failed to read default fstab"; @@ -242,6 +253,37 @@ bool write_wipe_package(const std::string& package_data, std::string* err) { WIPE_PACKAGE_OFFSET_IN_MISC, err); } +static bool OffsetAndSizeInVendorSpace(size_t offset, size_t size) { + auto total_size = WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC; + return size <= total_size && offset <= total_size - size; +} + +bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err) { + if (!OffsetAndSizeInVendorSpace(offset, size)) { + *err = android::base::StringPrintf("Out of bound read (offset %zu size %zu)", offset, size); + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return read_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, + err); +} + +bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err) { + if (!OffsetAndSizeInVendorSpace(offset, size)) { + *err = android::base::StringPrintf("Out of bound write (offset %zu size %zu)", offset, size); + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return write_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, + err); +} + extern "C" bool write_reboot_bootloader(void) { std::string err; return write_reboot_bootloader(&err); diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 2207d4cb..5c0a450f 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -28,8 +28,9 @@ // 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices // Note that these offsets are admitted by bootloader,recovery and uncrypt, so they // are not configurable without changing all of them. -static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0; -static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024; +constexpr size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0; +constexpr size_t VENDOR_SPACE_OFFSET_IN_MISC = 2 * 1024; +constexpr size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024; /* Bootloader Message (2-KiB) * @@ -233,6 +234,14 @@ bool read_wipe_package(std::string* package_data, size_t size, std::string* err) // Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC). bool write_wipe_package(const std::string& package_data, std::string* err); +// Reads data from the vendor space in /misc partition, with the given offset and size. Note that +// offset is in relative to the start of vendor space. +bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err); + +// Writes the given data to the vendor space in /misc partition, at the given offset. Note that +// offset is in relative to the start of the vendor space. +bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err); + #else #include diff --git a/misc_writer/Android.bp b/misc_writer/Android.bp new file mode 100644 index 00000000..567143c7 --- /dev/null +++ b/misc_writer/Android.bp @@ -0,0 +1,40 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_binary { + name: "misc_writer", + vendor: true, + + srcs: [ + "misc_writer.cpp", + ], + + cpp_std: "experimental", + + cflags: [ + "-Wall", + "-Werror", + ], + + shared_libs: [ + "libbase", + ], + + static_libs: [ + "libbootloader_message_vendor", + "libfstab", + ], +} diff --git a/misc_writer/misc_writer.cpp b/misc_writer/misc_writer.cpp new file mode 100644 index 00000000..1d9702eb --- /dev/null +++ b/misc_writer/misc_writer.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +using namespace std::string_literals; + +static std::vector ParseHexString(std::string_view hex_string) { + auto length = hex_string.size(); + if (length % 2 != 0 || length == 0) { + return {}; + } + + std::vector result(length / 2); + for (size_t i = 0; i < length / 2; i++) { + auto sub = "0x" + std::string(hex_string.substr(i * 2, 2)); + if (!android::base::ParseUint(sub, &result[i])) { + return {}; + } + } + return result; +} + +static int Usage(std::string_view name) { + std::cerr << name << " usage:\n"; + std::cerr << name << " [--vendor-space-offset ] --hex-string 0xABCDEF\n"; + std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc " + "partition. Offset defaults to 0 if unspecified.\n"; + return EXIT_FAILURE; +} + +// misc_writer is a vendor tool that writes data to the vendor space in /misc. +int main(int argc, char** argv) { + constexpr struct option OPTIONS[] = { + { "vendor-space-offset", required_argument, nullptr, 0 }, + { "hex-string", required_argument, nullptr, 0 }, + { nullptr, 0, nullptr, 0 }, + }; + + // Offset defaults to 0 if unspecified. + size_t offset = 0; + std::string_view hex_string; + + int arg; + int option_index; + while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) { + if (arg != 0) { + LOG(ERROR) << "Invalid command argument"; + return Usage(argv[0]); + } + auto option_name = OPTIONS[option_index].name; + if (option_name == "vendor-space-offset"s) { + if (!android::base::ParseUint(optarg, &offset)) { + LOG(ERROR) << "Failed to parse the offset: " << optarg; + return Usage(argv[0]); + } + } else if (option_name == "hex-string"s) { + hex_string = optarg; + } + } + + if (hex_string.starts_with("0x") || hex_string.starts_with("0X")) { + hex_string = hex_string.substr(2); + } + if (hex_string.empty()) { + LOG(ERROR) << "Invalid input hex string: " << hex_string; + return Usage(argv[0]); + } + + auto data = ParseHexString(hex_string); + if (data.empty()) { + LOG(ERROR) << "Failed to parse the input hex string: " << hex_string; + return EXIT_FAILURE; + } + if (std::string err; !WriteMiscPartitionVendorSpace(data.data(), data.size(), offset, &err)) { + LOG(ERROR) << "Failed to write to misc partition: " << err; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/tests/unit/bootloader_message_test.cpp b/tests/unit/bootloader_message_test.cpp index b005d199..95d875e6 100644 --- a/tests/unit/bootloader_message_test.cpp +++ b/tests/unit/bootloader_message_test.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include @@ -22,6 +23,10 @@ #include #include +using namespace std::string_literals; + +extern void SetMiscBlockDeviceForTest(std::string_view misc_device); + TEST(BootloaderMessageTest, read_and_write_bootloader_message) { TemporaryFile temp_misc; @@ -114,3 +119,36 @@ TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_long) { std::string(boot.reserved, sizeof(boot.reserved))); } +TEST(BootloaderMessageTest, WriteMiscPartitionVendorSpace) { + TemporaryFile temp_misc; + ASSERT_TRUE(android::base::WriteStringToFile(std::string(4096, '\x00'), temp_misc.path)); + SetMiscBlockDeviceForTest(temp_misc.path); + + constexpr std::string_view kTestMessage = "kTestMessage"; + std::string err; + ASSERT_TRUE(WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), 0, &err)); + + std::string message; + message.resize(kTestMessage.size()); + ASSERT_TRUE(ReadMiscPartitionVendorSpace(message.data(), message.size(), 0, &err)); + ASSERT_EQ(kTestMessage, message); + + // Write with an offset. + ASSERT_TRUE(WriteMiscPartitionVendorSpace("\x00\x00", 2, 5, &err)); + ASSERT_TRUE(ReadMiscPartitionVendorSpace(message.data(), message.size(), 0, &err)); + ASSERT_EQ("kTest\x00\x00ssage"s, message); + + // Write with the right size. + auto start_offset = + WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC - kTestMessage.size(); + ASSERT_TRUE( + WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), start_offset, &err)); + + // Out-of-bound write. + ASSERT_FALSE(WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), + start_offset + 1, &err)); + + // Message won't fit. + std::string long_message(WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC + 1, 'a'); + ASSERT_FALSE(WriteMiscPartitionVendorSpace(long_message.data(), long_message.size(), 0, &err)); +} From 1536db887f496b6c50522d62be22d9f6584eaf87 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 14 May 2019 10:54:43 -0700 Subject: [PATCH 038/171] Add UpdaterRuntime class This class adds a wrapper to the runtime dependent functions. Therefore, the behavior of update on device stays the same, while simulators can have their own implementations. Also change the caller side of the registered updater functions to call these runtime wrappers. Bug: 131911365 Test: unit tests pass, sideload an update on cuttlefish Change-Id: Ib3ab67132991d67fc132f27120e4152439d16ac5 --- edify/expr.cpp | 4 +- edify/include/edify/expr.h | 9 +- edify/include/edify/updater_interface.h | 47 +++++ .../include/edify/updater_runtime_interface.h | 69 ++++++++ tests/unit/updater_test.cpp | 12 +- updater/Android.bp | 1 + updater/blockimg.cpp | 74 +++++--- updater/include/updater/updater.h | 40 +++-- updater/include/updater/updater_runtime.h | 58 ++++++ updater/install.cpp | 167 +++++++----------- updater/updater.cpp | 32 ++-- updater/updater_main.cpp | 5 +- updater/updater_runtime.cpp | 132 ++++++++++++++ 13 files changed, 486 insertions(+), 164 deletions(-) create mode 100644 edify/include/edify/updater_interface.h create mode 100644 edify/include/edify/updater_runtime_interface.h create mode 100644 updater/include/updater/updater_runtime.h create mode 100644 updater/updater_runtime.cpp diff --git a/edify/expr.cpp b/edify/expr.cpp index c090eb28..e5e0e240 100644 --- a/edify/expr.cpp +++ b/edify/expr.cpp @@ -421,5 +421,5 @@ Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) { return nullptr; } -State::State(const std::string& script, void* cookie) - : script(script), cookie(cookie), error_code(kNoError), cause_code(kNoCause) {} +State::State(const std::string& script, UpdaterInterface* interface) + : script(script), updater(interface), error_code(kNoError), cause_code(kNoCause) {} diff --git a/edify/include/edify/expr.h b/edify/include/edify/expr.h index 5cbd5e15..cd9c7012 100644 --- a/edify/include/edify/expr.h +++ b/edify/include/edify/expr.h @@ -23,19 +23,20 @@ #include #include +#include "edify/updater_interface.h" + // Forward declaration to avoid including "otautil/error_code.h". enum ErrorCode : int; enum CauseCode : int; struct State { - State(const std::string& script, void* cookie); + State(const std::string& script, UpdaterInterface* cookie); // The source of the original script. const std::string& script; - // Optional pointer to app-specific data; the core of edify never - // uses this value. - void* cookie; + // A pointer to app-specific data; the libedify doesn't use this value. + UpdaterInterface* updater; // The error message (if any) returned if the evaluation aborts. // Should be empty initially, will be either empty or a string that diff --git a/edify/include/edify/updater_interface.h b/edify/include/edify/updater_interface.h new file mode 100644 index 00000000..a4d581ee --- /dev/null +++ b/edify/include/edify/updater_interface.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include + +struct ZipArchive; +typedef ZipArchive* ZipArchiveHandle; + +class UpdaterRuntimeInterface; + +class UpdaterInterface { + public: + virtual ~UpdaterInterface() = default; + + // Writes the message to command pipe, adds a new line in the end. + virtual void WriteToCommandPipe(const std::string_view message, bool flush = false) const = 0; + + // Sends over the message to recovery to print it on the screen. + virtual void UiPrint(const std::string_view message) const = 0; + + // Given the name of the block device, returns |name| for updates on the device; or the file path + // to the fake block device for simulations. + virtual std::string FindBlockDeviceName(const std::string_view name) const = 0; + + virtual UpdaterRuntimeInterface* GetRuntime() const = 0; + virtual ZipArchiveHandle GetPackageHandle() const = 0; + virtual std::string GetResult() const = 0; + virtual uint8_t* GetMappedPackageAddress() const = 0; +}; diff --git a/edify/include/edify/updater_runtime_interface.h b/edify/include/edify/updater_runtime_interface.h new file mode 100644 index 00000000..15ccd832 --- /dev/null +++ b/edify/include/edify/updater_runtime_interface.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +// This class serves as the base to updater runtime. It wraps the runtime dependent functions; and +// updates on device and host simulations can have different implementations. e.g. block devices +// during host simulation merely a temporary file. With this class, the caller side in registered +// updater's functions will stay the same for both update and simulation. +class UpdaterRuntimeInterface { + public: + virtual ~UpdaterRuntimeInterface() = default; + + // Returns true if it's a runtime instance for simulation. + virtual bool IsSimulator() const = 0; + + // Returns the value of system property |key|. If the property doesn't exist, returns + // |default_value|. + virtual std::string GetProperty(const std::string_view key, + const std::string_view default_value) const = 0; + + // Given the name of the block device, returns |name| for updates on the device; or the file path + // to the fake block device for simulations. + virtual std::string FindBlockDeviceName(const std::string_view name) const = 0; + + // Mounts the |location| on |mount_point|. Returns 0 on success. + virtual int Mount(const std::string_view location, const std::string_view mount_point, + const std::string_view fs_type, const std::string_view mount_options) = 0; + + // Returns true if |mount_point| is mounted. + virtual bool IsMounted(const std::string_view mount_point) const = 0; + + // Unmounts the |mount_point|. Returns a pair of results with the first value indicating + // if the |mount_point| is mounted, and the second value indicating the result of umount(2). + virtual std::pair Unmount(const std::string_view mount_point) = 0; + + // Reads |filename| and puts its value to |content|. + virtual bool ReadFileToString(const std::string_view filename, std::string* content) const = 0; + + // Updates the content of |filename| with |content|. + virtual bool WriteStringToFile(const std::string_view content, + const std::string_view filename) const = 0; + + // Wipes the first |len| bytes of block device in |filename|. + virtual int WipeBlockDevice(const std::string_view filename, size_t len) const = 0; + + // Starts a child process and runs the program with |args|. Uses vfork(2) if |is_vfork| is true. + virtual int RunProgram(const std::vector& args, bool is_vfork) const = 0; + + // Runs tune2fs with arguments |args|. + virtual int Tune2Fs(const std::vector& args) const = 0; +}; \ No newline at end of file diff --git a/tests/unit/updater_test.cpp b/tests/unit/updater_test.cpp index 4a8d1e6f..81229f50 100644 --- a/tests/unit/updater_test.cpp +++ b/tests/unit/updater_test.cpp @@ -52,13 +52,14 @@ #include "updater/blockimg.h" #include "updater/install.h" #include "updater/updater.h" +#include "updater/updater_runtime.h" using namespace std::string_literals; using PackageEntries = std::unordered_map; static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code, - Updater* updater = nullptr) { + Updater* updater) { std::unique_ptr e; int error_count = 0; ASSERT_EQ(0, ParseString(expr_str, &e, &error_count)); @@ -83,6 +84,11 @@ static void expect(const char* expected, const std::string& expr_str, CauseCode ASSERT_EQ(cause_code, state.cause_code); } +static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code) { + Updater updater; + expect(expected, expr_str, cause_code, &updater); +} + static void BuildUpdatePackage(const PackageEntries& entries, int fd) { FILE* zip_file_ptr = fdopen(fd, "wb"); ZipWriter zip_writer(zip_file_ptr); @@ -168,9 +174,9 @@ class UpdaterTestBase { // Set up the handler, command_pipe, patch offset & length. TemporaryFile temp_pipe; - ASSERT_TRUE(updater_.Init(temp_pipe.release(), zip_file.path, false, nullptr)); + ASSERT_TRUE(updater_.Init(temp_pipe.release(), zip_file.path, false)); ASSERT_TRUE(updater_.RunUpdate()); - ASSERT_EQ(result, updater_.result()); + ASSERT_EQ(result, updater_.GetResult()); // Parse the cause code written to the command pipe. int received_cause_code = kNoCause; diff --git a/updater/Android.bp b/updater/Android.bp index daf7e327..72f8bc9b 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -71,6 +71,7 @@ cc_library_static { "dynamic_partitions.cpp", "install.cpp", "updater.cpp", + "updater_runtime.cpp", ], include_dirs: [ diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 3b2b2c02..55218b02 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -54,6 +54,7 @@ #include #include "edify/expr.h" +#include "edify/updater_interface.h" #include "otautil/dirutil.h" #include "otautil/error_code.h" #include "otautil/paths.h" @@ -61,7 +62,6 @@ #include "otautil/rangeset.h" #include "private/commands.h" #include "updater/install.h" -#include "updater/updater.h" // Set this to 0 to interpret 'erase' transfers to mean do a // BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret @@ -1669,8 +1669,15 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, return StringValue(""); } - auto updater = static_cast(state->cookie); - ZipArchiveHandle za = updater->package_handle(); + auto updater = state->updater; + auto block_device_path = updater->FindBlockDeviceName(blockdev_filename->data); + if (block_device_path.empty()) { + LOG(ERROR) << "Block device path for " << blockdev_filename->data << " not found. " << name + << " failed."; + return StringValue(""); + } + + ZipArchiveHandle za = updater->GetPackageHandle(); if (za == nullptr) { return StringValue(""); } @@ -1690,15 +1697,15 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, return StringValue(""); } - params.fd.reset(TEMP_FAILURE_RETRY(open(blockdev_filename->data.c_str(), O_RDWR))); + params.fd.reset(TEMP_FAILURE_RETRY(open(block_device_path.c_str(), O_RDWR))); if (params.fd == -1) { failure_type = errno == EIO ? kEioFailure : kFileOpenFailure; - PLOG(ERROR) << "open \"" << blockdev_filename->data << "\" failed"; + PLOG(ERROR) << "open \"" << block_device_path << "\" failed"; return StringValue(""); } uint8_t digest[SHA_DIGEST_LENGTH]; - if (!Sha1DevicePath(blockdev_filename->data, digest)) { + if (!Sha1DevicePath(block_device_path, digest)) { return StringValue(""); } params.stashbase = print_sha1(digest); @@ -1711,8 +1718,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, struct stat sb; int result = stat(updated_marker.c_str(), &sb); if (result == 0) { - LOG(INFO) << "Skipping already updated partition " << blockdev_filename->data - << " based on marker"; + LOG(INFO) << "Skipping already updated partition " << block_device_path << " based on marker"; return StringValue("t"); } } else { @@ -1910,7 +1916,7 @@ pbiudone: LOG(INFO) << "stashed " << params.stashed << " blocks"; LOG(INFO) << "max alloc needed was " << params.buffer.size(); - const char* partition = strrchr(blockdev_filename->data.c_str(), '/'); + const char* partition = strrchr(block_device_path.c_str(), '/'); if (partition != nullptr && *(partition + 1) != 0) { updater->WriteToCommandPipe( android::base::StringPrintf("log bytes_written_%s: %" PRIu64, partition + 1, @@ -2078,10 +2084,17 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vectordata.c_str(), O_RDWR)); + auto block_device_path = state->updater->FindBlockDeviceName(blockdev_filename->data); + if (block_device_path.empty()) { + LOG(ERROR) << "Block device path for " << blockdev_filename->data << " not found. " << name + << " failed."; + return StringValue(""); + } + + android::base::unique_fd fd(open(block_device_path.c_str(), O_RDWR)); if (fd == -1) { CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure; - ErrorAbort(state, cause_code, "open \"%s\" failed: %s", blockdev_filename->data.c_str(), + ErrorAbort(state, cause_code, "open \"%s\" failed: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2095,7 +2108,7 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector buffer(BLOCKSIZE); for (const auto& [begin, end] : rs) { if (!check_lseek(fd, static_cast(begin) * BLOCKSIZE, SEEK_SET)) { - ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(), + ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2103,7 +2116,7 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vectordata.c_str(), + ErrorAbort(state, cause_code, "failed to read %s: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2142,10 +2155,17 @@ Value* CheckFirstBlockFn(const char* name, State* state, return StringValue(""); } - android::base::unique_fd fd(open(arg_filename->data.c_str(), O_RDONLY)); + auto block_device_path = state->updater->FindBlockDeviceName(arg_filename->data); + if (block_device_path.empty()) { + LOG(ERROR) << "Block device path for " << arg_filename->data << " not found. " << name + << " failed."; + return StringValue(""); + } + + android::base::unique_fd fd(open(block_device_path.c_str(), O_RDONLY)); if (fd == -1) { CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure; - ErrorAbort(state, cause_code, "open \"%s\" failed: %s", arg_filename->data.c_str(), + ErrorAbort(state, cause_code, "open \"%s\" failed: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2155,7 +2175,7 @@ Value* CheckFirstBlockFn(const char* name, State* state, if (ReadBlocks(blk0, &block0_buffer, fd) == -1) { CauseCode cause_code = errno == EIO ? kEioFailure : kFreadFailure; - ErrorAbort(state, cause_code, "failed to read %s: %s", arg_filename->data.c_str(), + ErrorAbort(state, cause_code, "failed to read %s: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2171,10 +2191,9 @@ Value* CheckFirstBlockFn(const char* name, State* state, uint16_t mount_count = *reinterpret_cast(&block0_buffer[0x400 + 0x34]); if (mount_count > 0) { - auto updater = static_cast(state->cookie); - updater->UiPrint( + state->updater->UiPrint( android::base::StringPrintf("Device was remounted R/W %" PRIu16 " times", mount_count)); - updater->UiPrint( + state->updater->UiPrint( android::base::StringPrintf("Last remount happened on %s", ctime(&mount_time))); } @@ -2211,14 +2230,21 @@ Value* BlockImageRecoverFn(const char* name, State* state, return StringValue(""); } + auto block_device_path = state->updater->FindBlockDeviceName(filename->data); + if (block_device_path.empty()) { + LOG(ERROR) << "Block device path for " << filename->data << " not found. " << name + << " failed."; + return StringValue(""); + } + // Output notice to log when recover is attempted - LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; + LOG(INFO) << block_device_path << " image corrupted, attempting to recover..."; // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read - fec::io fh(filename->data, O_RDWR); + fec::io fh(block_device_path, O_RDWR); if (!fh) { - ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(), + ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2244,7 +2270,7 @@ Value* BlockImageRecoverFn(const char* name, State* state, if (fh.pread(buffer, BLOCKSIZE, static_cast(j) * BLOCKSIZE) != BLOCKSIZE) { ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s", - filename->data.c_str(), j, strerror(errno)); + block_device_path.c_str(), j, strerror(errno)); return StringValue(""); } @@ -2260,7 +2286,7 @@ Value* BlockImageRecoverFn(const char* name, State* state, // read and check if the errors field value has increased. } } - LOG(INFO) << "..." << filename->data << " image recovered successfully."; + LOG(INFO) << "..." << block_device_path << " image recovered successfully."; return StringValue("t"); } diff --git a/updater/include/updater/updater.h b/updater/include/updater/updater.h index d5468292..7bbecbc5 100644 --- a/updater/include/updater/updater.h +++ b/updater/include/updater/updater.h @@ -21,45 +21,53 @@ #include #include +#include #include #include "edify/expr.h" +#include "edify/updater_interface.h" #include "otautil/error_code.h" #include "otautil/sysutil.h" -struct selabel_handle; +class UpdaterRuntime; -class Updater { +class Updater : public UpdaterInterface { public: - ~Updater(); + explicit Updater(std::unique_ptr run_time) + : runtime_(std::move(run_time)) {} + + Updater(); + + ~Updater() override; // Memory-maps the OTA package and opens it as a zip file. Also sets up the command pipe and - // selabel handle. TODO(xunchang) implement a run time environment class and move sehandle there. - bool Init(int fd, const std::string& package_filename, bool is_retry, - struct selabel_handle* sehandle); + // UpdaterRuntime. + bool Init(int fd, const std::string_view package_filename, bool is_retry); // Parses and evaluates the updater-script in the OTA package. Reports the error code if the // evaluation fails. bool RunUpdate(); // Writes the message to command pipe, adds a new line in the end. - void WriteToCommandPipe(const std::string& message, bool flush = false) const; + void WriteToCommandPipe(const std::string_view message, bool flush = false) const override; // Sends over the message to recovery to print it on the screen. - void UiPrint(const std::string& message) const; + void UiPrint(const std::string_view message) const override; - ZipArchiveHandle package_handle() const { + std::string FindBlockDeviceName(const std::string_view name) const override; + + UpdaterRuntimeInterface* GetRuntime() const override { + return runtime_.get(); + } + ZipArchiveHandle GetPackageHandle() const override { return package_handle_; } - struct selabel_handle* sehandle() const { - return sehandle_; - } - std::string result() const { + std::string GetResult() const override { return result_; } - uint8_t* GetMappedPackageAddress() const { + uint8_t* GetMappedPackageAddress() const override { return mapped_package_.addr; } @@ -76,13 +84,15 @@ class Updater { // Parses the error code embedded in state->errmsg; and reports the error code and cause code. void ParseAndReportErrorCode(State* state); + std::unique_ptr runtime_; + MemMapping mapped_package_; ZipArchiveHandle package_handle_{ nullptr }; std::string updater_script_; bool is_retry_{ false }; std::unique_ptr cmd_pipe_{ nullptr, fclose }; - struct selabel_handle* sehandle_{ nullptr }; std::string result_; + std::vector skipped_functions_; }; diff --git a/updater/include/updater/updater_runtime.h b/updater/include/updater/updater_runtime.h new file mode 100644 index 00000000..6cd0ffb4 --- /dev/null +++ b/updater/include/updater/updater_runtime.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "edify/updater_runtime_interface.h" + +struct selabel_handle; +struct Partition; + +class UpdaterRuntime : public UpdaterRuntimeInterface { + public: + explicit UpdaterRuntime(struct selabel_handle* sehandle) : sehandle_(sehandle) {} + ~UpdaterRuntime() override = default; + + bool IsSimulator() const override { + return false; + } + + std::string GetProperty(const std::string_view key, + const std::string_view default_value) const override; + + std::string FindBlockDeviceName(const std::string_view name) const override; + + int Mount(const std::string_view location, const std::string_view mount_point, + const std::string_view fs_type, const std::string_view mount_options) override; + bool IsMounted(const std::string_view mount_point) const override; + std::pair Unmount(const std::string_view mount_point) override; + + bool ReadFileToString(const std::string_view filename, std::string* content) const override; + bool WriteStringToFile(const std::string_view content, + const std::string_view filename) const override; + + int WipeBlockDevice(const std::string_view filename, size_t len) const override; + int RunProgram(const std::vector& args, bool is_vfork) const override; + int Tune2Fs(const std::vector& args) const override; + + struct selabel_handle* sehandle_{ nullptr }; +}; diff --git a/updater/install.cpp b/updater/install.cpp index b4d88403..6b15eaa3 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -57,12 +57,25 @@ #include #include "edify/expr.h" +#include "edify/updater_interface.h" +#include "edify/updater_runtime_interface.h" #include "otautil/dirutil.h" #include "otautil/error_code.h" #include "otautil/mounts.h" #include "otautil/print_sha1.h" #include "otautil/sysutil.h" -#include "updater/updater.h" + +static bool UpdateBlockDeviceNameForPartition(UpdaterInterface* updater, Partition* partition) { + CHECK(updater); + std::string name = updater->FindBlockDeviceName(partition->name); + if (name.empty()) { + LOG(ERROR) << "Failed to find the block device " << partition->name; + return false; + } + + partition->name = std::move(name); + return true; +} // This is the updater side handler for ui_print() in edify script. Contents will be sent over to // the recovery side for on-screen display. @@ -73,7 +86,7 @@ Value* UIPrintFn(const char* name, State* state, const std::vector(state->cookie)->UiPrint(buffer); + state->updater->UiPrint(buffer); return StringValue(buffer); } @@ -99,7 +112,7 @@ Value* PackageExtractFileFn(const char* name, State* state, const std::string& zip_path = args[0]; const std::string& dest_path = args[1]; - ZipArchiveHandle za = static_cast(state->cookie)->package_handle(); + ZipArchiveHandle za = state->updater->GetPackageHandle(); ZipEntry entry; if (FindEntry(za, zip_path, &entry) != 0) { LOG(ERROR) << name << ": no " << zip_path << " in package"; @@ -142,7 +155,7 @@ Value* PackageExtractFileFn(const char* name, State* state, } const std::string& zip_path = args[0]; - ZipArchiveHandle za = static_cast(state->cookie)->package_handle(); + ZipArchiveHandle za = state->updater->GetPackageHandle(); ZipEntry entry; if (FindEntry(za, zip_path, &entry) != 0) { return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name, @@ -197,6 +210,11 @@ Value* PatchPartitionCheckFn(const char* name, State* state, args[1].c_str(), err.c_str()); } + if (!UpdateBlockDeviceNameForPartition(state->updater, &source) || + !UpdateBlockDeviceNameForPartition(state->updater, &target)) { + return StringValue(""); + } + bool result = PatchPartitionCheck(target, source); return StringValue(result ? "t" : ""); } @@ -238,6 +256,11 @@ Value* PatchPartitionFn(const char* name, State* state, return ErrorAbort(state, kArgsParsingFailure, "%s(): Invalid patch arg", name); } + if (!UpdateBlockDeviceNameForPartition(state->updater, &source) || + !UpdateBlockDeviceNameForPartition(state->updater, &target)) { + return StringValue(""); + } + bool result = PatchPartition(target, source, *values[0], nullptr); return StringValue(result ? "t" : ""); } @@ -281,24 +304,8 @@ Value* MountFn(const char* name, State* state, const std::vector(state->cookie); - { - char* secontext = nullptr; - if (updater->sehandle()) { - selabel_lookup(updater->sehandle(), &secontext, mount_point.c_str(), 0755); - setfscreatecon(secontext); - } - - mkdir(mount_point.c_str(), 0755); - - if (secontext) { - freecon(secontext); - setfscreatecon(nullptr); - } - } - - if (mount(location.c_str(), mount_point.c_str(), fs_type.c_str(), - MS_NOATIME | MS_NODEV | MS_NODIRATIME, mount_options.c_str()) < 0) { + auto updater = state->updater; + if (updater->GetRuntime()->Mount(location, mount_point, fs_type, mount_options) != 0) { updater->UiPrint(android::base::StringPrintf("%s: Failed to mount %s at %s: %s", name, location.c_str(), mount_point.c_str(), strerror(errno))); @@ -324,9 +331,8 @@ Value* IsMountedFn(const char* name, State* state, const std::vectorupdater->GetRuntime(); + if (!updater_runtime->IsMounted(mount_point)) { return StringValue(""); } @@ -347,42 +353,20 @@ Value* UnmountFn(const char* name, State* state, const std::vector(state->cookie); - scan_mounted_volumes(); - MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str()); - if (vol == nullptr) { + auto updater = state->updater; + auto [mounted, result] = updater->GetRuntime()->Unmount(mount_point); + if (!mounted) { updater->UiPrint( android::base::StringPrintf("Failed to unmount %s: No such volume", mount_point.c_str())); return nullptr; - } else { - int ret = unmount_mounted_volume(vol); - if (ret != 0) { - updater->UiPrint(android::base::StringPrintf("Failed to unmount %s: %s", mount_point.c_str(), - strerror(errno))); - } + } else if (result != 0) { + updater->UiPrint(android::base::StringPrintf("Failed to unmount %s: %s", mount_point.c_str(), + strerror(errno))); } return StringValue(mount_point); } -static int exec_cmd(const std::vector& args) { - CHECK(!args.empty()); - auto argv = StringVectorToNullTerminatedArray(args); - - pid_t child; - if ((child = vfork()) == 0) { - execv(argv[0], argv.data()); - _exit(EXIT_FAILURE); - } - - int status; - waitpid(child, &status, 0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - LOG(ERROR) << args[0] << " failed with status " << WEXITSTATUS(status); - } - return WEXITSTATUS(status); -} - // format(fs_type, partition_type, location, fs_size, mount_point) // // fs_type="ext4" partition_type="EMMC" location=device fs_size= mount_point= @@ -427,6 +411,7 @@ Value* FormatFn(const char* name, State* state, const std::vectorupdater->GetRuntime(); if (fs_type == "ext4") { std::vector mke2fs_args = { "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", location @@ -435,12 +420,13 @@ Value* FormatFn(const char* name, State* state, const std::vectorRunProgram(mke2fs_args, true); status != 0) { LOG(ERROR) << name << ": mke2fs failed (" << status << ") on " << location; return StringValue(""); } - if (auto status = exec_cmd({ "/system/bin/e2fsdroid", "-e", "-a", mount_point, location }); + if (auto status = updater_runtime->RunProgram( + { "/system/bin/e2fsdroid", "-e", "-a", mount_point, location }, true); status != 0) { LOG(ERROR) << name << ": e2fsdroid failed (" << status << ") on " << location; return StringValue(""); @@ -459,12 +445,13 @@ Value* FormatFn(const char* name, State* state, const std::vector= 512) { f2fs_args.push_back(std::to_string(size / 512)); } - if (auto status = exec_cmd(f2fs_args); status != 0) { + if (auto status = updater_runtime->RunProgram(f2fs_args, true); status != 0) { LOG(ERROR) << name << ": make_f2fs failed (" << status << ") on " << location; return StringValue(""); } - if (auto status = exec_cmd({ "/system/bin/sload_f2fs", "-t", mount_point, location }); + if (auto status = updater_runtime->RunProgram( + { "/system/bin/sload_f2fs", "-t", mount_point, location }, true); status != 0) { LOG(ERROR) << name << ": sload_f2fs failed (" << status << ") on " << location; return StringValue(""); @@ -503,8 +490,7 @@ Value* ShowProgressFn(const char* name, State* state, sec_str.c_str()); } - auto updater = static_cast(state->cookie); - updater->WriteToCommandPipe(android::base::StringPrintf("progress %f %d", frac, sec)); + state->updater->WriteToCommandPipe(android::base::StringPrintf("progress %f %d", frac, sec)); return StringValue(frac_str); } @@ -527,8 +513,7 @@ Value* SetProgressFn(const char* name, State* state, frac_str.c_str()); } - auto updater = static_cast(state->cookie); - updater->WriteToCommandPipe(android::base::StringPrintf("set_progress %f", frac)); + state->updater->WriteToCommandPipe(android::base::StringPrintf("set_progress %f", frac)); return StringValue(frac_str); } @@ -541,7 +526,9 @@ Value* GetPropFn(const char* name, State* state, const std::vectorupdater->GetRuntime(); + std::string value = updater_runtime->GetProperty(key, ""); return StringValue(value); } @@ -566,7 +553,8 @@ Value* FileGetPropFn(const char* name, State* state, const std::string& key = args[1]; std::string buffer; - if (!android::base::ReadFileToString(filename, &buffer)) { + auto updater_runtime = state->updater->GetRuntime(); + if (!updater_runtime->ReadFileToString(filename, &buffer)) { ErrorAbort(state, kFreadFailure, "%s: failed to read %s", name, filename.c_str()); return nullptr; } @@ -628,7 +616,7 @@ Value* WipeCacheFn(const char* name, State* state, const std::vector(state->cookie)->WriteToCommandPipe("wipe_cache"); + state->updater->WriteToCommandPipe("wipe_cache"); return StringValue("t"); } @@ -642,26 +630,8 @@ Value* RunProgramFn(const char* name, State* state, const std::vectorupdater->GetRuntime(); + auto status = updater_runtime->RunProgram(args, false); return StringValue(std::to_string(status)); } @@ -679,7 +649,8 @@ Value* ReadFileFn(const char* name, State* state, const std::vectorupdater->GetRuntime(); + if (updater_runtime->ReadFileToString(filename, &contents)) { return new Value(Value::Type::STRING, std::move(contents)); } @@ -708,12 +679,12 @@ Value* WriteValueFn(const char* name, State* state, const std::vectorupdater->GetRuntime(); + if (!updater_runtime->WriteStringToFile(value, filename)) { PLOG(ERROR) << name << ": Failed to write to \"" << filename << "\""; return StringValue(""); - } else { - return StringValue("t"); } + return StringValue("t"); } // Immediately reboot the device. Recovery is not finished normally, @@ -839,16 +810,10 @@ Value* WipeBlockDeviceFn(const char* name, State* state, const std::vectorupdater->GetRuntime(); + int status = updater_runtime->WipeBlockDevice(filename, len); + return StringValue(status == 0 ? "t" : ""); } Value* EnableRebootFn(const char* name, State* state, const std::vector>& argv) { @@ -856,7 +821,7 @@ Value* EnableRebootFn(const char* name, State* state, const std::vector(state->cookie)->WriteToCommandPipe("enable_reboot"); + state->updater->WriteToCommandPipe("enable_reboot"); return StringValue("t"); } @@ -872,10 +837,8 @@ Value* Tune2FsFn(const char* name, State* state, const std::vectorupdater->GetRuntime(); + if (auto result = updater_runtime->Tune2Fs(args); result != 0) { return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", name, result); } return StringValue("t"); diff --git a/updater/updater.cpp b/updater/updater.cpp index e0679fb0..dbfa2f42 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -24,14 +24,17 @@ #include #include +#include "updater/updater_runtime.h" + +Updater::Updater() : Updater(std::make_unique(nullptr)) {} + Updater::~Updater() { if (package_handle_) { CloseArchive(package_handle_); } } -bool Updater::Init(int fd, const std::string& package_filename, bool is_retry, - struct selabel_handle* sehandle) { +bool Updater::Init(int fd, const std::string_view package_filename, bool is_retry) { // Set up the pipe for sending commands back to the parent process. cmd_pipe_.reset(fdopen(fd, "wb")); if (!cmd_pipe_) { @@ -41,12 +44,12 @@ bool Updater::Init(int fd, const std::string& package_filename, bool is_retry, setlinebuf(cmd_pipe_.get()); - if (!mapped_package_.MapFile(package_filename)) { + if (!mapped_package_.MapFile(std::string(package_filename))) { LOG(ERROR) << "failed to map package " << package_filename; return false; } if (int open_err = OpenArchiveFromMemory(mapped_package_.addr, mapped_package_.length, - package_filename.c_str(), &package_handle_); + std::string(package_filename).c_str(), &package_handle_); open_err != 0) { LOG(ERROR) << "failed to open package " << package_filename << ": " << ErrorCodeString(open_err); @@ -58,14 +61,12 @@ bool Updater::Init(int fd, const std::string& package_filename, bool is_retry, is_retry_ = is_retry; - sehandle_ = sehandle; - if (!sehandle_) { - fprintf(cmd_pipe_.get(), "ui_print Warning: No file_contexts\n"); - } return true; } bool Updater::RunUpdate() { + CHECK(runtime_); + // Parse the script. std::unique_ptr root; int error_count = 0; @@ -86,6 +87,9 @@ bool Updater::RunUpdate() { if (result_.empty() && state.cause_code != kNoCause) { fprintf(cmd_pipe_.get(), "log cause: %d\n", state.cause_code); } + for (const auto& func : skipped_functions_) { + LOG(WARNING) << "Skipped executing function " << func; + } return true; } @@ -93,17 +97,17 @@ bool Updater::RunUpdate() { return false; } -void Updater::WriteToCommandPipe(const std::string& message, bool flush) const { - fprintf(cmd_pipe_.get(), "%s\n", message.c_str()); +void Updater::WriteToCommandPipe(const std::string_view message, bool flush) const { + fprintf(cmd_pipe_.get(), "%s\n", std::string(message).c_str()); if (flush) { fflush(cmd_pipe_.get()); } } -void Updater::UiPrint(const std::string& message) const { +void Updater::UiPrint(const std::string_view message) const { // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "". // so skip sending empty strings to ui. - std::vector lines = android::base::Split(message, "\n"); + std::vector lines = android::base::Split(std::string(message), "\n"); for (const auto& line : lines) { if (!line.empty()) { fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str()); @@ -116,6 +120,10 @@ void Updater::UiPrint(const std::string& message) const { LOG(INFO) << message; } +std::string Updater::FindBlockDeviceName(const std::string_view name) const { + return runtime_->FindBlockDeviceName(name); +} + void Updater::ParseAndReportErrorCode(State* state) { CHECK(state); if (state->errmsg.empty()) { diff --git a/updater/updater_main.cpp b/updater/updater_main.cpp index dd22c137..055a8ac7 100644 --- a/updater/updater_main.cpp +++ b/updater/updater_main.cpp @@ -31,6 +31,7 @@ #include "updater/dynamic_partitions.h" #include "updater/install.h" #include "updater/updater.h" +#include "updater/updater_runtime.h" // Generated by the makefile, this function defines the // RegisterDeviceExtensions() function, which calls all the @@ -95,8 +96,8 @@ int main(int argc, char** argv) { auto sehandle = selinux_android_file_context_handle(); selinux_android_set_sehandle(sehandle); - Updater updater; - if (!updater.Init(fd, package_name, is_retry, sehandle)) { + Updater updater(std::make_unique(sehandle)); + if (!updater.Init(fd, package_name, is_retry)) { return 1; } diff --git a/updater/updater_runtime.cpp b/updater/updater_runtime.cpp new file mode 100644 index 00000000..761f9997 --- /dev/null +++ b/updater/updater_runtime.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "updater/updater_runtime.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "otautil/mounts.h" +#include "otautil/sysutil.h" + +std::string UpdaterRuntime::GetProperty(const std::string_view key, + const std::string_view default_value) const { + return android::base::GetProperty(std::string(key), std::string(default_value)); +} + +std::string UpdaterRuntime::FindBlockDeviceName(const std::string_view name) const { + return std::string(name); +} + +int UpdaterRuntime::Mount(const std::string_view location, const std::string_view mount_point, + const std::string_view fs_type, const std::string_view mount_options) { + std::string mount_point_string(mount_point); + char* secontext = nullptr; + if (sehandle_) { + selabel_lookup(sehandle_, &secontext, mount_point_string.c_str(), 0755); + setfscreatecon(secontext); + } + + mkdir(mount_point_string.c_str(), 0755); + + if (secontext) { + freecon(secontext); + setfscreatecon(nullptr); + } + + return mount(std::string(location).c_str(), mount_point_string.c_str(), + std::string(fs_type).c_str(), MS_NOATIME | MS_NODEV | MS_NODIRATIME, + std::string(mount_options).c_str()); +} + +bool UpdaterRuntime::IsMounted(const std::string_view mount_point) const { + scan_mounted_volumes(); + MountedVolume* vol = find_mounted_volume_by_mount_point(std::string(mount_point).c_str()); + return vol != nullptr; +} + +std::pair UpdaterRuntime::Unmount(const std::string_view mount_point) { + scan_mounted_volumes(); + MountedVolume* vol = find_mounted_volume_by_mount_point(std::string(mount_point).c_str()); + if (vol == nullptr) { + return { false, -1 }; + } + + int ret = unmount_mounted_volume(vol); + return { true, ret }; +} + +bool UpdaterRuntime::ReadFileToString(const std::string_view filename, std::string* content) const { + return android::base::ReadFileToString(std::string(filename), content); +} + +bool UpdaterRuntime::WriteStringToFile(const std::string_view content, + const std::string_view filename) const { + return android::base::WriteStringToFile(std::string(content), std::string(filename)); +} + +int UpdaterRuntime::WipeBlockDevice(const std::string_view filename, size_t len) const { + android::base::unique_fd fd(open(std::string(filename).c_str(), O_WRONLY)); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << filename; + return false; + } + // The wipe_block_device function in ext4_utils returns 0 on success and 1 for failure. + return wipe_block_device(fd, len); +} + +int UpdaterRuntime::RunProgram(const std::vector& args, bool is_vfork) const { + CHECK(!args.empty()); + auto argv = StringVectorToNullTerminatedArray(args); + LOG(INFO) << "about to run program [" << args[0] << "] with " << argv.size() << " args"; + + pid_t child = is_vfork ? vfork() : fork(); + if (child == 0) { + execv(argv[0], argv.data()); + PLOG(ERROR) << "run_program: execv failed"; + _exit(EXIT_FAILURE); + } + + int status; + waitpid(child, &status, 0); + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + LOG(ERROR) << "run_program: child exited with status " << WEXITSTATUS(status); + } + } else if (WIFSIGNALED(status)) { + LOG(ERROR) << "run_program: child terminated by signal " << WTERMSIG(status); + } + + return status; +} + +int UpdaterRuntime::Tune2Fs(const std::vector& args) const { + auto tune2fs_args = StringVectorToNullTerminatedArray(args); + // tune2fs changes the filesystem parameters on an ext2 filesystem; it returns 0 on success. + return tune2fs_main(tune2fs_args.size() - 1, tune2fs_args.data()); +} From feefbf2f5698a5a56adb80a057dba0bc07050c09 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 22 May 2019 18:03:01 +0000 Subject: [PATCH 039/171] Revert "recovery: report compliant reboot reason" This reverts commit 6f4e4db4f9e0911a07c6393d01e4380e844f7891. Reason for revert: Booting out of recovery (choose `Reboot system now`) on taimen is broken. Device keeps booting back into recovery. Bug: 133326470 Test: Choose `Reboot system now` from recovery menu. Deivce attempts normal boot. Change-Id: I6e85fc248e18953a6fb94513c3abc7e7e0fb0477 --- fastboot/fastboot.cpp | 4 ++-- otautil/include/otautil/sysutil.h | 4 ++-- otautil/sysutil.cpp | 6 +++--- recovery.cpp | 2 -- recovery_main.cpp | 19 ++----------------- recovery_ui/include/recovery_ui/device.h | 2 -- recovery_ui/ui.cpp | 2 +- 7 files changed, 10 insertions(+), 29 deletions(-) diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 20233499..14f5e4bd 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -30,10 +30,10 @@ #include "recovery_ui/ui.h" static const std::vector> kFastbootMenuActions{ - { "Reboot system now", Device::REBOOT_FROM_FASTBOOT }, + { "Reboot system now", Device::REBOOT }, { "Enter recovery", Device::ENTER_RECOVERY }, { "Reboot to bootloader", Device::REBOOT_BOOTLOADER }, - { "Power off", Device::SHUTDOWN_FROM_FASTBOOT }, + { "Power off", Device::SHUTDOWN }, }; Device::BuiltinAction StartFastboot(Device* device, const std::vector& /* args */) { diff --git a/otautil/include/otautil/sysutil.h b/otautil/include/otautil/sysutil.h index 326db864..48e9011e 100644 --- a/otautil/include/otautil/sysutil.h +++ b/otautil/include/otautil/sysutil.h @@ -102,11 +102,11 @@ class MemMapping { }; // Reboots the device into the specified target, by additionally handling quiescent reboot mode. -// All unknown targets reboot into Android. +// 'target' can be an empty string, which indicates booting into Android. bool Reboot(std::string_view target); // Triggers a shutdown. -bool Shutdown(std::string_view target); +bool Shutdown(); // Returns a null-terminated char* array, where the elements point to the C-strings in the given // vector, plus an additional nullptr at the end. This is a helper function that facilitates diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp index a8829858..420db4ca 100644 --- a/otautil/sysutil.cpp +++ b/otautil/sysutil.cpp @@ -229,9 +229,9 @@ bool Reboot(std::string_view target) { return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd); } -bool Shutdown(std::string_view target) { - std::string cmd = "shutdown," + std::string(target); - return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd); +bool Shutdown() { + // "shutdown" doesn't need a "reason" arg nor a comma. + return android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown"); } std::vector StringVectorToNullTerminatedArray(const std::vector& args) { diff --git a/recovery.cpp b/recovery.cpp index eb0c2b21..20e5a1be 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -356,8 +356,6 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) : device->InvokeMenuItem(chosen_item); switch (chosen_action) { - case Device::REBOOT_FROM_FASTBOOT: // Can not happen - case Device::SHUTDOWN_FROM_FASTBOOT: // Can not happen case Device::NO_ACTION: break; diff --git a/recovery_main.cpp b/recovery_main.cpp index f78c1c78..aba9c5d7 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -471,12 +471,7 @@ int main(int argc, char** argv) { switch (ret) { case Device::SHUTDOWN: ui->Print("Shutting down...\n"); - Shutdown("recovery"); - break; - - case Device::SHUTDOWN_FROM_FASTBOOT: - ui->Print("Shutting down...\n"); - Shutdown("fastboot"); + Shutdown(); break; case Device::REBOOT_BOOTLOADER: @@ -525,19 +520,9 @@ int main(int argc, char** argv) { fastboot = false; break; - case Device::REBOOT: - ui->Print("Rebooting...\n"); - Reboot("recovery_menu"); - break; - - case Device::REBOOT_FROM_FASTBOOT: - ui->Print("Rebooting...\n"); - Reboot("fastboot_menu"); - break; - default: ui->Print("Rebooting...\n"); - Reboot("unknown" + std::to_string(ret)); + Reboot(""); break; } } diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 9a4edf26..7c76cdb0 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -58,8 +58,6 @@ class Device { REBOOT_FASTBOOT = 17, REBOOT_RECOVERY = 18, REBOOT_RESCUE = 19, - REBOOT_FROM_FASTBOOT = 20, - SHUTDOWN_FROM_FASTBOOT = 21, }; explicit Device(RecoveryUI* ui); diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index fc517757..7ea9307c 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -375,7 +375,7 @@ void RecoveryUI::ProcessKey(int key_code, int updown) { case RecoveryUI::REBOOT: if (reboot_enabled) { - Reboot("recovery_ui"); + Reboot(""); while (true) { pause(); } From 27556d089f8496c2acb72182ce7add94938eb749 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 22 May 2019 14:48:35 -0700 Subject: [PATCH 040/171] Some clean ups to the updater Remove some unnecessary includes or forward declarations. And include the correct headers to build host executables. Bug: 131911365 Test: unit tests pass Change-Id: I62e75f60678159fe24619a4bd386b1416f1a5b5d --- tests/unit/updater_test.cpp | 4 +++- updater/blockimg.cpp | 12 ++++++++---- updater/include/updater/updater.h | 4 ---- updater/include/updater/updater_runtime.h | 1 - updater/install.cpp | 5 ++++- updater/updater.cpp | 4 +--- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/unit/updater_test.cpp b/tests/unit/updater_test.cpp index 81229f50..8993dd8b 100644 --- a/tests/unit/updater_test.cpp +++ b/tests/unit/updater_test.cpp @@ -85,7 +85,7 @@ static void expect(const char* expected, const std::string& expr_str, CauseCode } static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code) { - Updater updater; + Updater updater(std::make_unique(nullptr)); expect(expected, expr_str, cause_code, &updater); } @@ -133,6 +133,8 @@ static Value* BlobToString(const char* name, State* state, class UpdaterTestBase { protected: + UpdaterTestBase() : updater_(std::make_unique(nullptr)) {} + void SetUp() { RegisterBuiltins(); RegisterInstallFunctions(); diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 55218b02..2d41f610 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include #include @@ -63,10 +62,15 @@ #include "private/commands.h" #include "updater/install.h" -// Set this to 0 to interpret 'erase' transfers to mean do a -// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret -// erase to mean fill the region with zeroes. +#ifdef __ANDROID__ +#include +// Set this to 0 to interpret 'erase' transfers to mean do a BLKDISCARD ioctl (the normal behavior). +// Set to 1 to interpret erase to mean fill the region with zeroes. #define DEBUG_ERASE 0 +#else +#define DEBUG_ERASE 1 +#define AID_SYSTEM -1 +#endif // __ANDROID__ static constexpr size_t BLOCKSIZE = 4096; static constexpr mode_t STASH_DIRECTORY_MODE = 0700; diff --git a/updater/include/updater/updater.h b/updater/include/updater/updater.h index 7bbecbc5..08816bf3 100644 --- a/updater/include/updater/updater.h +++ b/updater/include/updater/updater.h @@ -30,15 +30,11 @@ #include "otautil/error_code.h" #include "otautil/sysutil.h" -class UpdaterRuntime; - class Updater : public UpdaterInterface { public: explicit Updater(std::unique_ptr run_time) : runtime_(std::move(run_time)) {} - Updater(); - ~Updater() override; // Memory-maps the OTA package and opens it as a zip file. Also sets up the command pipe and diff --git a/updater/include/updater/updater_runtime.h b/updater/include/updater/updater_runtime.h index 6cd0ffb4..e97eb49b 100644 --- a/updater/include/updater/updater_runtime.h +++ b/updater/include/updater/updater_runtime.h @@ -25,7 +25,6 @@ #include "edify/updater_runtime_interface.h" struct selabel_handle; -struct Partition; class UpdaterRuntime : public UpdaterRuntimeInterface { public: diff --git a/updater/install.cpp b/updater/install.cpp index 6b15eaa3..c82351ec 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -53,7 +53,6 @@ #include #include #include -#include #include #include "edify/expr.h" @@ -65,6 +64,10 @@ #include "otautil/print_sha1.h" #include "otautil/sysutil.h" +#ifndef __ANDROID__ +#include // for strlcpy +#endif + static bool UpdateBlockDeviceNameForPartition(UpdaterInterface* updater, Partition* partition) { CHECK(updater); std::string name = updater->FindBlockDeviceName(partition->name); diff --git a/updater/updater.cpp b/updater/updater.cpp index dbfa2f42..426c6dce 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -24,9 +24,7 @@ #include #include -#include "updater/updater_runtime.h" - -Updater::Updater() : Updater(std::make_unique(nullptr)) {} +#include "edify/updater_runtime_interface.h" Updater::~Updater() { if (package_handle_) { From 88d8001e752573d8b87f7b0cbc784a18622abddd Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 22 May 2019 18:52:29 -0700 Subject: [PATCH 041/171] Move off the Next ZipString overload. Bug: http://b/129068177 Test: treehugger Change-Id: I3c8f70b0d8cc5dc6b3b4439dbe0b9a5bd85003c4 --- applypatch/imgdiff.cpp | 5 ++--- install/install.cpp | 4 ++-- install/verifier.cpp | 7 +++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index cb34d469..6ad4a610 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -683,12 +683,11 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl // Create a list of deflated zip entries, sorted by offset. std::vector> temp_entries; - ZipString name; + std::string name; ZipEntry entry; while ((ret = Next(cookie, &entry, &name)) == 0) { if (entry.method == kCompressDeflated || limit_ > 0) { - std::string entry_name(name.name, name.name + name.name_length); - temp_entries.emplace_back(entry_name, entry); + temp_entries.emplace_back(name, entry); } } diff --git a/install/install.cpp b/install/install.cpp index 1b51b424..8d46641d 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -539,13 +539,13 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) { std::vector compatibility_info; ZipEntry info_entry; - ZipString info_name; + std::string info_name; while (Next(cookie, &info_entry, &info_name) == 0) { std::string content(info_entry.uncompressed_length, '\0'); int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast(&content[0]), info_entry.uncompressed_length); if (ret != 0) { - LOG(ERROR) << "Failed to read " << info_name.name << ": " << ErrorCodeString(ret); + LOG(ERROR) << "Failed to read " << info_name << ": " << ErrorCodeString(ret); CloseArchive(zip_handle); return false; } diff --git a/install/verifier.cpp b/install/verifier.cpp index 3de58e4b..02759cdc 100644 --- a/install/verifier.cpp +++ b/install/verifier.cpp @@ -320,22 +320,21 @@ static std::vector IterateZipEntriesAndSearchForKeys(const ZipArchi std::vector result; - ZipString name; + std::string name; ZipEntry entry; while ((iter_status = Next(cookie, &entry, &name)) == 0) { std::vector pem_content(entry.uncompressed_length); if (int32_t extract_status = ExtractToMemory(handle, &entry, pem_content.data(), pem_content.size()); extract_status != 0) { - LOG(ERROR) << "Failed to extract " << std::string(name.name, name.name + name.name_length); + LOG(ERROR) << "Failed to extract " << name; return {}; } Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); // Aborts the parsing if we fail to load one of the key file. if (!LoadCertificateFromBuffer(pem_content, &cert)) { - LOG(ERROR) << "Failed to load keys from " - << std::string(name.name, name.name + name.name_length); + LOG(ERROR) << "Failed to load keys from " << name; return {}; } From 488cc05c9685277bfba148ad259bde10bdc4a439 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Mon, 20 May 2019 10:36:16 -0700 Subject: [PATCH 042/171] recovery: report compliant reboot reason (Part Deux) shutdown and reboot should have a corresponding sub-reason. Adding: "reboot,userrequested,fastboot" "reboot,userrequested,recovery" "reboot,userrequested,recovery,ui" "shutdown,userrequested,fastboot" "shutdown,userrequested,recovery" "reboot,unknown#" (Can't happen, debug) Test: manual, multiple targets, enter recovery, be able to exit recovery Bug: 133326470 Change-Id: Ibfcb2a23158e8e99922e8053edd815fb592150f2 --- fastboot/fastboot.cpp | 4 ++-- otautil/include/otautil/sysutil.h | 4 ++-- otautil/sysutil.cpp | 6 +++--- recovery.cpp | 2 ++ recovery_main.cpp | 19 +++++++++++++++++-- recovery_ui/include/recovery_ui/device.h | 2 ++ recovery_ui/ui.cpp | 2 +- 7 files changed, 29 insertions(+), 10 deletions(-) diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 14f5e4bd..20233499 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -30,10 +30,10 @@ #include "recovery_ui/ui.h" static const std::vector> kFastbootMenuActions{ - { "Reboot system now", Device::REBOOT }, + { "Reboot system now", Device::REBOOT_FROM_FASTBOOT }, { "Enter recovery", Device::ENTER_RECOVERY }, { "Reboot to bootloader", Device::REBOOT_BOOTLOADER }, - { "Power off", Device::SHUTDOWN }, + { "Power off", Device::SHUTDOWN_FROM_FASTBOOT }, }; Device::BuiltinAction StartFastboot(Device* device, const std::vector& /* args */) { diff --git a/otautil/include/otautil/sysutil.h b/otautil/include/otautil/sysutil.h index 48e9011e..326db864 100644 --- a/otautil/include/otautil/sysutil.h +++ b/otautil/include/otautil/sysutil.h @@ -102,11 +102,11 @@ class MemMapping { }; // Reboots the device into the specified target, by additionally handling quiescent reboot mode. -// 'target' can be an empty string, which indicates booting into Android. +// All unknown targets reboot into Android. bool Reboot(std::string_view target); // Triggers a shutdown. -bool Shutdown(); +bool Shutdown(std::string_view target); // Returns a null-terminated char* array, where the elements point to the C-strings in the given // vector, plus an additional nullptr at the end. This is a helper function that facilitates diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp index 420db4ca..a8829858 100644 --- a/otautil/sysutil.cpp +++ b/otautil/sysutil.cpp @@ -229,9 +229,9 @@ bool Reboot(std::string_view target) { return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd); } -bool Shutdown() { - // "shutdown" doesn't need a "reason" arg nor a comma. - return android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown"); +bool Shutdown(std::string_view target) { + std::string cmd = "shutdown," + std::string(target); + return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd); } std::vector StringVectorToNullTerminatedArray(const std::vector& args) { diff --git a/recovery.cpp b/recovery.cpp index 20e5a1be..eb0c2b21 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -356,6 +356,8 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) : device->InvokeMenuItem(chosen_item); switch (chosen_action) { + case Device::REBOOT_FROM_FASTBOOT: // Can not happen + case Device::SHUTDOWN_FROM_FASTBOOT: // Can not happen case Device::NO_ACTION: break; diff --git a/recovery_main.cpp b/recovery_main.cpp index aba9c5d7..7fbdf9a0 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -471,7 +471,12 @@ int main(int argc, char** argv) { switch (ret) { case Device::SHUTDOWN: ui->Print("Shutting down...\n"); - Shutdown(); + Shutdown("userrequested,recovery"); + break; + + case Device::SHUTDOWN_FROM_FASTBOOT: + ui->Print("Shutting down...\n"); + Shutdown("userrequested,fastboot"); break; case Device::REBOOT_BOOTLOADER: @@ -520,9 +525,19 @@ int main(int argc, char** argv) { fastboot = false; break; + case Device::REBOOT: + ui->Print("Rebooting...\n"); + Reboot("userrequested,recovery"); + break; + + case Device::REBOOT_FROM_FASTBOOT: + ui->Print("Rebooting...\n"); + Reboot("userrequested,fastboot"); + break; + default: ui->Print("Rebooting...\n"); - Reboot(""); + Reboot("unknown" + std::to_string(ret)); break; } } diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 7c76cdb0..9a4edf26 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -58,6 +58,8 @@ class Device { REBOOT_FASTBOOT = 17, REBOOT_RECOVERY = 18, REBOOT_RESCUE = 19, + REBOOT_FROM_FASTBOOT = 20, + SHUTDOWN_FROM_FASTBOOT = 21, }; explicit Device(RecoveryUI* ui); diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index 7ea9307c..78b014a8 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -375,7 +375,7 @@ void RecoveryUI::ProcessKey(int key_code, int updown) { case RecoveryUI::REBOOT: if (reboot_enabled) { - Reboot(""); + Reboot("userrequested,recovery,ui"); while (true) { pause(); } From c1a5e26fd905df829c6e2bbc24b0500af4a5b357 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 22 May 2019 14:34:12 -0700 Subject: [PATCH 043/171] Implement an update simulator to verify BB OTA packages on host Implement the simulator runtime and build the updater simulator as a host executable. The code to parse the target-files and mocks the block devices will be submitted in the follow-up. Bug: 131911365 Test: unit tests pass Change-Id: Ib1ba939aec8333ca68a45139514d772ad7a27ad8 --- otautil/Android.bp | 8 +- tests/Android.bp | 4 +- updater/Android.bp | 61 ++++++++++++- updater/Android.mk | 85 +++++++++++++----- updater/include/updater/simulator_runtime.h | 58 ++++++++++++ updater/include/updater/target_files.h | 36 ++++++++ updater/simulator_runtime.cpp | 97 +++++++++++++++++++++ updater/target_files.cpp | 26 ++++++ updater/update_simulator_main.cpp | 76 ++++++++++++++++ updater/updater.cpp | 3 +- 10 files changed, 424 insertions(+), 30 deletions(-) create mode 100644 updater/include/updater/simulator_runtime.h create mode 100644 updater/include/updater/target_files.h create mode 100644 updater/simulator_runtime.cpp create mode 100644 updater/target_files.cpp create mode 100644 updater/update_simulator_main.cpp diff --git a/otautil/Android.bp b/otautil/Android.bp index 73398c3a..871dcae9 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -24,12 +24,16 @@ cc_library_static { // Minimal set of files to support host build. srcs: [ + "dirutil.cpp", "paths.cpp", "rangeset.cpp", + "sysutil.cpp", ], shared_libs: [ "libbase", + "libcutils", + "libselinux", ], export_include_dirs: [ @@ -39,12 +43,10 @@ cc_library_static { target: { android: { srcs: [ - "dirutil.cpp", "logging.cpp", "mounts.cpp", "parse_install_logs.cpp", "roots.cpp", - "sysutil.cpp", "thermalutil.cpp", ], @@ -57,10 +59,8 @@ cc_library_static { ], shared_libs: [ - "libcutils", "libext4_utils", "libfs_mgr", - "libselinux", ], export_static_lib_headers: [ diff --git a/tests/Android.bp b/tests/Android.bp index 67a65ae9..4969c087 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -108,6 +108,7 @@ cc_test { defaults: [ "recovery_test_defaults", "libupdater_defaults", + "libupdater_device_defaults", ], test_suites: ["device-tests"], @@ -121,7 +122,8 @@ cc_test { "libfusesideload", "libminui", "libotautil", - "libupdater", + "libupdater_device", + "libupdater_core", "libupdate_verifier", "libgtest_prod", diff --git a/updater/Android.bp b/updater/Android.bp index 72f8bc9b..a66155b3 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -30,7 +30,6 @@ cc_defaults { "libfec", "libfec_rs", "libverity_tree", - "libfs_mgr", "libgtest_prod", "liblog", "liblp", @@ -46,6 +45,14 @@ cc_defaults { "libcrypto_utils", "libcutils", "libutils", + ], +} + +cc_defaults { + name: "libupdater_device_defaults", + + static_libs: [ + "libfs_mgr", "libtune2fs", "libext2_com_err", @@ -54,11 +61,13 @@ cc_defaults { "libext2_uuid", "libext2_e2p", "libext2fs", - ], + ] } cc_library_static { - name: "libupdater", + name: "libupdater_core", + + host_supported: true, defaults: [ "recovery_defaults", @@ -68,12 +77,33 @@ cc_library_static { srcs: [ "blockimg.cpp", "commands.cpp", - "dynamic_partitions.cpp", "install.cpp", "updater.cpp", + ], + + export_include_dirs: [ + "include", + ], +} + +cc_library_static { + name: "libupdater_device", + + defaults: [ + "recovery_defaults", + "libupdater_defaults", + "libupdater_device_defaults", + ], + + srcs: [ + "dynamic_partitions.cpp", "updater_runtime.cpp", ], + static_libs: [ + "libupdater_core", + ], + include_dirs: [ "external/e2fsprogs/misc", ], @@ -82,3 +112,26 @@ cc_library_static { "include", ], } + +cc_library_host_static { + name: "libupdater_host", + + defaults: [ + "recovery_defaults", + "libupdater_defaults", + ], + + srcs: [ + "simulator_runtime.cpp", + "target_files.cpp", + ], + + static_libs: [ + "libupdater_core", + "libfstab", + ], + + export_include_dirs: [ + "include", + ], +} diff --git a/updater/Android.mk b/updater/Android.mk index 0178239e..b8b5e5a7 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -33,7 +33,6 @@ updater_common_static_libraries := \ libfec \ libfec_rs \ libverity_tree \ - libfs_mgr \ libgtest_prod \ liblog \ liblp \ @@ -48,9 +47,24 @@ updater_common_static_libraries := \ libcrypto \ libcrypto_utils \ libcutils \ - libutils \ - libtune2fs \ - $(tune2fs_static_libraries) + libutils + + +# Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function +# named "Register_()". Here we emit a little C function that +# gets #included by updater.cpp. It calls all those registration +# functions. +# $(1): the path to the register.inc file +# $(2): a list of TARGET_RECOVERY_UPDATER_LIBS +define generate-register-inc + $(hide) mkdir -p $(dir $(1)) + $(hide) echo "" > $(1) + $(hide) $(foreach lib,$(2),echo "extern void Register_$(lib)(void);" >> $(1);) + $(hide) echo "void RegisterDeviceExtensions() {" >> $(1) + $(hide) $(foreach lib,$(2),echo " Register_$(lib)();" >> $(1);) + $(hide) echo "}" >> $(1) +endef + # updater (static executable) # =============================== @@ -69,33 +83,26 @@ LOCAL_CFLAGS := \ -Werror LOCAL_STATIC_LIBRARIES := \ - libupdater \ + libupdater_device \ + libupdater_core \ $(TARGET_RECOVERY_UPDATER_LIBS) \ $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS) \ - $(updater_common_static_libraries) + $(updater_common_static_libraries) \ + libfs_mgr \ + libtune2fs \ + $(tune2fs_static_libraries) -# Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function -# named "Register_()". Here we emit a little C function that -# gets #included by updater.c. It calls all those registration -# functions. +LOCAL_MODULE_CLASS := EXECUTABLES +inc := $(call local-generated-sources-dir)/register.inc # Devices can also add libraries to TARGET_RECOVERY_UPDATER_EXTRA_LIBS. # These libs are also linked in with updater, but we don't try to call # any sort of registration function for these. Use this variable for # any subsidiary static libraries required for your registered # extension libs. - -LOCAL_MODULE_CLASS := EXECUTABLES -inc := $(call local-generated-sources-dir)/register.inc - $(inc) : libs := $(TARGET_RECOVERY_UPDATER_LIBS) $(inc) : - $(hide) mkdir -p $(dir $@) - $(hide) echo "" > $@ - $(hide) $(foreach lib,$(libs),echo "extern void Register_$(lib)(void);" >> $@;) - $(hide) echo "void RegisterDeviceExtensions() {" >> $@ - $(hide) $(foreach lib,$(libs),echo " Register_$(lib)();" >> $@;) - $(hide) echo "}" >> $@ + $(call generate-register-inc,$@,$(libs)) LOCAL_GENERATED_SOURCES := $(inc) @@ -104,3 +111,41 @@ inc := LOCAL_FORCE_STATIC_EXECUTABLE := true include $(BUILD_EXECUTABLE) + + +# update_host_simulator (static executable) +# =============================== +include $(CLEAR_VARS) + +LOCAL_MODULE := update_host_simulator + +LOCAL_SRC_FILES := \ + update_simulator_main.cpp + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include + +LOCAL_CFLAGS := \ + -Wall \ + -Werror + +LOCAL_STATIC_LIBRARIES := \ + libupdater_host \ + libupdater_core \ + $(TARGET_RECOVERY_UPDATER_HOST_LIBS) \ + $(TARGET_RECOVERY_UPDATER_HOST_EXTRA_LIBS) \ + $(updater_common_static_libraries) \ + libfstab + +LOCAL_MODULE_CLASS := EXECUTABLES +inc := $(call local-generated-sources-dir)/register.inc + +$(inc) : libs := $(TARGET_RECOVERY_UPDATER_HOST_LIBS) +$(inc) : + $(call generate-register-inc,$@,$(libs)) + +LOCAL_GENERATED_SOURCES := $(inc) + +inc := + +include $(BUILD_HOST_EXECUTABLE) diff --git a/updater/include/updater/simulator_runtime.h b/updater/include/updater/simulator_runtime.h new file mode 100644 index 00000000..93fa2a4e --- /dev/null +++ b/updater/include/updater/simulator_runtime.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "edify/updater_runtime_interface.h" +#include "updater/target_files.h" + +class SimulatorRuntime : public UpdaterRuntimeInterface { + public: + explicit SimulatorRuntime(TargetFiles* source) : source_(source) {} + + bool IsSimulator() const override { + return true; + } + + std::string GetProperty(const std::string_view key, + const std::string_view default_value) const override; + + int Mount(const std::string_view location, const std::string_view mount_point, + const std::string_view fs_type, const std::string_view mount_options) override; + bool IsMounted(const std::string_view mount_point) const override; + std::pair Unmount(const std::string_view mount_point) override; + + bool ReadFileToString(const std::string_view filename, std::string* content) const override; + bool WriteStringToFile(const std::string_view content, + const std::string_view filename) const override; + + int WipeBlockDevice(const std::string_view filename, size_t len) const override; + int RunProgram(const std::vector& args, bool is_vfork) const override; + int Tune2Fs(const std::vector& args) const override; + + private: + std::string FindBlockDeviceName(const std::string_view name) const override; + + TargetFiles* source_; + std::map> mounted_partitions_; +}; diff --git a/updater/include/updater/target_files.h b/updater/include/updater/target_files.h new file mode 100644 index 00000000..9ef1a5bf --- /dev/null +++ b/updater/include/updater/target_files.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +// This class parses a given target file for the build properties and image files. Then it creates +// and maintains the temporary files to simulate the block devices on host. +class TargetFiles { + public: + TargetFiles(std::string path, std::string work_dir) + : path_(std::move(path)), work_dir_(std::move(work_dir)) {} + + std::string GetProperty(const std::string_view key, const std::string_view default_value) const; + + std::string FindBlockDeviceName(const std::string_view name) const; + + private: + std::string path_; // Path to the target file. + + std::string work_dir_; // A temporary directory to store the extracted image files +}; diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp new file mode 100644 index 00000000..c3b3d951 --- /dev/null +++ b/updater/simulator_runtime.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "updater/simulator_runtime.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "otautil/mounts.h" +#include "otautil/sysutil.h" + +std::string SimulatorRuntime::GetProperty(const std::string_view key, + const std::string_view default_value) const { + return source_->GetProperty(key, default_value); +} + +int SimulatorRuntime::Mount(const std::string_view location, const std::string_view mount_point, + const std::string_view /* fs_type */, + const std::string_view /* mount_options */) { + if (auto mounted_location = mounted_partitions_.find(mount_point); + mounted_location != mounted_partitions_.end() && mounted_location->second != location) { + LOG(ERROR) << mount_point << " has been mounted at " << mounted_location->second; + return -1; + } + + mounted_partitions_.emplace(mount_point, location); + return 0; +} + +bool SimulatorRuntime::IsMounted(const std::string_view mount_point) const { + return mounted_partitions_.find(mount_point) != mounted_partitions_.end(); +} + +std::pair SimulatorRuntime::Unmount(const std::string_view mount_point) { + if (!IsMounted(mount_point)) { + return { false, -1 }; + } + + mounted_partitions_.erase(std::string(mount_point)); + return { true, 0 }; +} + +std::string SimulatorRuntime::FindBlockDeviceName(const std::string_view name) const { + return source_->FindBlockDeviceName(name); +} + +// TODO(xunchang) implement the utility functions in simulator. +int SimulatorRuntime::RunProgram(const std::vector& args, bool /* is_vfork */) const { + LOG(INFO) << "Running program with args " << android::base::Join(args, " "); + return 0; +} + +int SimulatorRuntime::Tune2Fs(const std::vector& args) const { + LOG(INFO) << "Running Tune2Fs with args " << android::base::Join(args, " "); + return 0; +} + +int SimulatorRuntime::WipeBlockDevice(const std::string_view filename, size_t /* len */) const { + LOG(INFO) << "SKip wiping block device " << filename; + return 0; +} + +bool SimulatorRuntime::ReadFileToString(const std::string_view filename, + std::string* /* content */) const { + LOG(INFO) << "SKip reading filename " << filename; + return true; +} + +bool SimulatorRuntime::WriteStringToFile(const std::string_view content, + const std::string_view filename) const { + LOG(INFO) << "SKip writing " << content.size() << " bytes to file " << filename; + return true; +} diff --git a/updater/target_files.cpp b/updater/target_files.cpp new file mode 100644 index 00000000..53671dd9 --- /dev/null +++ b/updater/target_files.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "updater/target_files.h" + +std::string TargetFiles::GetProperty(const std::string_view /*key*/, + const std::string_view default_value) const { + return std::string(default_value); +} + +std::string TargetFiles::FindBlockDeviceName(const std::string_view name) const { + return std::string(name); +} diff --git a/updater/update_simulator_main.cpp b/updater/update_simulator_main.cpp new file mode 100644 index 00000000..d10453c2 --- /dev/null +++ b/updater/update_simulator_main.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include "otautil/error_code.h" +#include "otautil/paths.h" +#include "updater/blockimg.h" +#include "updater/install.h" +#include "updater/simulator_runtime.h" +#include "updater/target_files.h" +#include "updater/updater.h" + +int main(int argc, char** argv) { + // Write the logs to stdout. + android::base::InitLogging(argv, &android::base::StderrLogger); + + if (argc != 3 && argc != 4) { + LOG(ERROR) << "unexpected number of arguments: " << argc << std::endl + << "Usage: " << argv[0] << " "; + return 1; + } + + // TODO(xunchang) implement a commandline parser, e.g. it can take an oem property so that the + // file_getprop() will return correct value. + + std::string source_target_file = argv[1]; + std::string package_name = argv[2]; + + // Configure edify's functions. + RegisterBuiltins(); + RegisterInstallFunctions(); + RegisterBlockImageFunctions(); + + TemporaryFile temp_saved_source; + TemporaryFile temp_last_command; + TemporaryDir temp_stash_base; + + Paths::Get().set_cache_temp_source(temp_saved_source.path); + Paths::Get().set_last_command_file(temp_last_command.path); + Paths::Get().set_stash_directory_base(temp_stash_base.path); + + TemporaryFile cmd_pipe; + + TemporaryDir source_temp_dir; + TargetFiles source(source_target_file, source_temp_dir.path); + + Updater updater(std::make_unique(&source)); + if (!updater.Init(cmd_pipe.release(), package_name, false)) { + return 1; + } + + if (!updater.RunUpdate()) { + return 1; + } + + LOG(INFO) << "\nscript succeeded, result: " << updater.GetResult(); + + return 0; +} diff --git a/updater/updater.cpp b/updater/updater.cpp index 426c6dce..8f4a6ede 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -175,7 +175,8 @@ bool Updater::ReadEntryToString(ZipArchiveHandle za, const std::string& entry_na int extract_err = ExtractToMemory(za, &entry, reinterpret_cast(&content->at(0)), entry.uncompressed_length); if (extract_err != 0) { - LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err); + LOG(ERROR) << "failed to read " << entry_name + << " from package: " << ErrorCodeString(extract_err); return false; } From 76e165d143ea1486382371e008e3bd66c4da4bce Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 30 May 2019 10:24:47 -0700 Subject: [PATCH 044/171] Disable building simulator for mac Disable building the simulator and its support libraries to resolve the breakage on mac host targets. Because the simulator is not intended to use on mac anyway. Bug: 134047992 Test: mma Change-Id: I488ab50cab1282f03250010b5334f1895d44f98b --- updater/Android.bp | 12 ++++++++++++ updater/Android.mk | 1 + 2 files changed, 13 insertions(+) diff --git a/updater/Android.bp b/updater/Android.bp index a66155b3..b279068a 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -81,6 +81,12 @@ cc_library_static { "updater.cpp", ], + target: { + darwin: { + enabled: false, + }, + }, + export_include_dirs: [ "include", ], @@ -131,6 +137,12 @@ cc_library_host_static { "libfstab", ], + target: { + darwin: { + enabled: false, + }, + }, + export_include_dirs: [ "include", ], diff --git a/updater/Android.mk b/updater/Android.mk index b8b5e5a7..e969d1c8 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -118,6 +118,7 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_MODULE := update_host_simulator +LOCAL_MODULE_HOST_OS := linux LOCAL_SRC_FILES := \ update_simulator_main.cpp From 74b0f7cce0cb52d0095d461edfd5731f4d45157f Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 22 May 2019 13:59:57 -0700 Subject: [PATCH 045/171] Implement the TargetFile and BuildInfo The TargetFile class parses a target-file and provides functions to read its contents. And the BuildInfo tries to simulate the device with files on host. Some work it does includes parsing the build properties, and extracting the image files for partitions specified in the fstab. Bug: 131911365 Test: unit tests pass, run simulator with cuttlefish, wear devices and from extracted TF. Change-Id: Iefe4a96d619d2e4b3d038e31480f11a0f9a70afa --- updater/Android.bp | 2 + updater/Android.mk | 3 +- updater/build_info.cpp | 126 ++++++++++ updater/include/updater/build_info.h | 63 +++++ updater/include/updater/simulator_runtime.h | 6 +- updater/include/updater/target_files.h | 53 +++- updater/target_files.cpp | 266 +++++++++++++++++++- updater/update_simulator_main.cpp | 19 +- 8 files changed, 513 insertions(+), 25 deletions(-) create mode 100644 updater/build_info.cpp create mode 100644 updater/include/updater/build_info.h diff --git a/updater/Android.bp b/updater/Android.bp index b279068a..93eeece5 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -128,6 +128,7 @@ cc_library_host_static { ], srcs: [ + "build_info.cpp", "simulator_runtime.cpp", "target_files.cpp", ], @@ -135,6 +136,7 @@ cc_library_host_static { static_libs: [ "libupdater_core", "libfstab", + "libc++fs", ], target: { diff --git a/updater/Android.mk b/updater/Android.mk index e969d1c8..63fd7bdf 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -136,7 +136,8 @@ LOCAL_STATIC_LIBRARIES := \ $(TARGET_RECOVERY_UPDATER_HOST_LIBS) \ $(TARGET_RECOVERY_UPDATER_HOST_EXTRA_LIBS) \ $(updater_common_static_libraries) \ - libfstab + libfstab \ + libc++fs LOCAL_MODULE_CLASS := EXECUTABLES inc := $(call local-generated-sources-dir)/register.inc diff --git a/updater/build_info.cpp b/updater/build_info.cpp new file mode 100644 index 00000000..8e87bd3e --- /dev/null +++ b/updater/build_info.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "updater/build_info.h" + +#include +#include + +#include +#include +#include + +#include "updater/target_files.h" + +bool BuildInfo::ParseTargetFile(const std::string_view target_file_path, bool extracted_input) { + TargetFile target_file(std::string(target_file_path), extracted_input); + if (!target_file.Open()) { + return false; + } + + if (!target_file.GetBuildProps(&build_props_)) { + return false; + } + + std::vector fstab_info_list; + if (!target_file.ParseFstabInfo(&fstab_info_list)) { + return false; + } + + for (const auto& fstab_info : fstab_info_list) { + for (const auto& directory : { "IMAGES", "RADIO" }) { + std::string entry_name = directory + fstab_info.mount_point + ".img"; + if (!target_file.EntryExists(entry_name)) { + LOG(WARNING) << "Failed to find the image entry in the target file: " << entry_name; + continue; + } + + temp_files_.emplace_back(work_dir_); + auto& image_file = temp_files_.back(); + if (!target_file.ExtractImage(entry_name, fstab_info, work_dir_, &image_file)) { + LOG(ERROR) << "Failed to set up source image files."; + return false; + } + + LOG(INFO) << "Mounted " << fstab_info.mount_point << "\nMapping: " << fstab_info.blockdev_name + << " to " << image_file.path; + + blockdev_map_.emplace( + fstab_info.blockdev_name, + FakeBlockDevice(fstab_info.blockdev_name, fstab_info.mount_point, image_file.path)); + break; + } + } + + return true; +} + +std::string BuildInfo::GetProperty(const std::string_view key, + const std::string_view default_value) const { + // The logic to parse the ro.product properties should be in line with the generation script. + // More details in common.py BuildInfo.GetBuildProp. + // TODO(xunchang) handle the oem property and the source order defined in + // ro.product.property_source_order + const std::set> ro_product_props = { + "ro.product.brand", "ro.product.device", "ro.product.manufacturer", "ro.product.model", + "ro.product.name" + }; + const std::vector source_order = { + "product", "product_services", "odm", "vendor", "system", + }; + if (ro_product_props.find(key) != ro_product_props.end()) { + std::string_view key_suffix(key); + CHECK(android::base::ConsumePrefix(&key_suffix, "ro.product")); + for (const auto& source : source_order) { + std::string resolved_key = "ro.product." + source + std::string(key_suffix); + if (auto entry = build_props_.find(resolved_key); entry != build_props_.end()) { + return entry->second; + } + } + LOG(WARNING) << "Failed to find property: " << key; + return std::string(default_value); + } else if (key == "ro.build.fingerprint") { + // clang-format off + return android::base::StringPrintf("%s/%s/%s:%s/%s/%s:%s/%s", + GetProperty("ro.product.brand", "").c_str(), + GetProperty("ro.product.name", "").c_str(), + GetProperty("ro.product.device", "").c_str(), + GetProperty("ro.build.version.release", "").c_str(), + GetProperty("ro.build.id", "").c_str(), + GetProperty("ro.build.version.incremental", "").c_str(), + GetProperty("ro.build.type", "").c_str(), + GetProperty("ro.build.tags", "").c_str()); + // clang-format on + } + + auto entry = build_props_.find(key); + if (entry == build_props_.end()) { + LOG(WARNING) << "Failed to find property: " << key; + return std::string(default_value); + } + + return entry->second; +} + +std::string BuildInfo::FindBlockDeviceName(const std::string_view name) const { + auto entry = blockdev_map_.find(name); + if (entry == blockdev_map_.end()) { + LOG(WARNING) << "Failed to find path to block device " << name; + return ""; + } + + return entry->second.mounted_file_path; +} diff --git a/updater/include/updater/build_info.h b/updater/include/updater/build_info.h new file mode 100644 index 00000000..a1355e89 --- /dev/null +++ b/updater/include/updater/build_info.h @@ -0,0 +1,63 @@ +/* + * 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 +#include +#include +#include + +#include + +// This class serves as the aggregation of the fake block device information during update +// simulation on host. In specific, it has the name of the block device, its mount point, and the +// path to the temporary file that fakes this block device. +class FakeBlockDevice { + public: + FakeBlockDevice(std::string block_device, std::string mount_point, std::string temp_file_path) + : blockdev_name(std::move(block_device)), + mount_point(std::move(mount_point)), + mounted_file_path(std::move(temp_file_path)) {} + + std::string blockdev_name; + std::string mount_point; + std::string mounted_file_path; // path to the temp file that mocks the block device +}; + +// This class stores the information of the source build. For example, it creates and maintains +// the temporary files to simulate the block devices on host. Therefore, the simulator runtime can +// query the information and run the update on host. +class BuildInfo { + public: + explicit BuildInfo(const std::string_view work_dir) : work_dir_(work_dir) {} + // Returns the value of the build properties. + std::string GetProperty(const std::string_view key, const std::string_view default_value) const; + // Returns the path to the mock block device. + std::string FindBlockDeviceName(const std::string_view name) const; + // Parses the given target-file, initializes the build properties and extracts the images. + bool ParseTargetFile(const std::string_view target_file_path, bool extracted_input); + + private: + // A map to store the system properties during simulation. + std::map> build_props_; + // A map from the blockdev_name to the FakeBlockDevice object, which contains the path to the + // temporary file. + std::map> blockdev_map_; + + std::list temp_files_; + std::string work_dir_; // A temporary directory to store the extracted image files +}; diff --git a/updater/include/updater/simulator_runtime.h b/updater/include/updater/simulator_runtime.h index 93fa2a4e..62909588 100644 --- a/updater/include/updater/simulator_runtime.h +++ b/updater/include/updater/simulator_runtime.h @@ -24,11 +24,11 @@ #include #include "edify/updater_runtime_interface.h" -#include "updater/target_files.h" +#include "updater/build_info.h" class SimulatorRuntime : public UpdaterRuntimeInterface { public: - explicit SimulatorRuntime(TargetFiles* source) : source_(source) {} + explicit SimulatorRuntime(BuildInfo* source) : source_(source) {} bool IsSimulator() const override { return true; @@ -53,6 +53,6 @@ class SimulatorRuntime : public UpdaterRuntimeInterface { private: std::string FindBlockDeviceName(const std::string_view name) const override; - TargetFiles* source_; + BuildInfo* source_; std::map> mounted_partitions_; }; diff --git a/updater/include/updater/target_files.h b/updater/include/updater/target_files.h index 9ef1a5bf..860d47a3 100644 --- a/updater/include/updater/target_files.h +++ b/updater/include/updater/target_files.h @@ -16,21 +16,56 @@ #pragma once +#include #include +#include +#include -// This class parses a given target file for the build properties and image files. Then it creates -// and maintains the temporary files to simulate the block devices on host. -class TargetFiles { +#include +#include + +// This class represents the mount information for each line in a fstab file. +class FstabInfo { public: - TargetFiles(std::string path, std::string work_dir) - : path_(std::move(path)), work_dir_(std::move(work_dir)) {} + FstabInfo(std::string blockdev_name, std::string mount_point, std::string fs_type) + : blockdev_name(std::move(blockdev_name)), + mount_point(std::move(mount_point)), + fs_type(std::move(fs_type)) {} - std::string GetProperty(const std::string_view key, const std::string_view default_value) const; + std::string blockdev_name; + std::string mount_point; + std::string fs_type; +}; - std::string FindBlockDeviceName(const std::string_view name) const; +// This class parses a target file from a zip file or an extracted directory. It also provides the +// function to read the its content for simulation. +class TargetFile { + public: + TargetFile(std::string path, bool extracted_input) + : path_(std::move(path)), extracted_input_(extracted_input) {} + + // Opens the input target file (or extracted directory) and parses the misc_info.txt. + bool Open(); + // Parses the build properties in all possible locations and save them in |props_map| + bool GetBuildProps(std::map>* props_map) const; + // Parses the fstab and save the information about each partition to mount into |fstab_info_list|. + bool ParseFstabInfo(std::vector* fstab_info_list) const; + // Returns true if the given entry exists in the target file. + bool EntryExists(const std::string_view name) const; + // Extracts the image file |entry_name|. Returns true on success. + bool ExtractImage(const std::string_view entry_name, const FstabInfo& fstab_info, + const std::string_view work_dir, TemporaryFile* image_file) const; private: - std::string path_; // Path to the target file. + // Wrapper functions to read the entry from either the zipped target-file, or the extracted input + // directory. + bool ReadEntryToString(const std::string_view name, std::string* content) const; + bool ExtractEntryToTempFile(const std::string_view name, TemporaryFile* temp_file) const; - std::string work_dir_; // A temporary directory to store the extracted image files + std::string path_; // Path to the zipped target-file or an extracted directory. + bool extracted_input_; // True if the target-file has been extracted. + ZipArchiveHandle handle_{ nullptr }; + + // The properties under META/misc_info.txt + std::map> misc_info_; }; diff --git a/updater/target_files.cpp b/updater/target_files.cpp index 53671dd9..93540b2e 100644 --- a/updater/target_files.cpp +++ b/updater/target_files.cpp @@ -16,11 +16,267 @@ #include "updater/target_files.h" -std::string TargetFiles::GetProperty(const std::string_view /*key*/, - const std::string_view default_value) const { - return std::string(default_value); +#include + +#include +#include +#include + +#include +#include +#include + +static bool SimgToImg(int input_fd, int output_fd) { + if (lseek64(input_fd, 0, SEEK_SET) == -1) { + PLOG(ERROR) << "Failed to lseek64 on the input sparse image"; + return false; + } + + if (lseek64(output_fd, 0, SEEK_SET) == -1) { + PLOG(ERROR) << "Failed to lseek64 on the output raw image"; + return false; + } + + std::unique_ptr s_file( + sparse_file_import(input_fd, true, false), sparse_file_destroy); + if (!s_file) { + LOG(ERROR) << "Failed to import the sparse image."; + return false; + } + + if (sparse_file_write(s_file.get(), output_fd, false, false, false) < 0) { + PLOG(ERROR) << "Failed to output the raw image file."; + return false; + } + + return true; } -std::string TargetFiles::FindBlockDeviceName(const std::string_view name) const { - return std::string(name); +static bool ParsePropertyFile(const std::string_view prop_content, + std::map>* props_map) { + LOG(INFO) << "Start parsing build property\n"; + std::vector lines = android::base::Split(std::string(prop_content), "\n"); + for (const auto& line : lines) { + if (line.empty() || line[0] == '#') continue; + auto pos = line.find('='); + if (pos == std::string::npos) continue; + std::string key = line.substr(0, pos); + std::string value = line.substr(pos + 1); + LOG(INFO) << key << ": " << value; + props_map->emplace(key, value); + } + + return true; +} + +static bool ParseFstab(const std::string_view fstab, std::vector* fstab_info_list) { + LOG(INFO) << "parsing fstab\n"; + std::vector lines = android::base::Split(std::string(fstab), "\n"); + for (const auto& line : lines) { + if (line.empty() || line[0] == '#') continue; + + // optional: + std::vector tokens = android::base::Split(line, " "); + tokens.erase(std::remove(tokens.begin(), tokens.end(), ""), tokens.end()); + if (tokens.size() != 4 && tokens.size() != 5) { + LOG(ERROR) << "Unexpected token size: " << tokens.size() << std::endl + << "Error parsing fstab line: " << line; + return false; + } + + const auto& blockdev = tokens[0]; + const auto& mount_point = tokens[1]; + const auto& fs_type = tokens[2]; + if (!android::base::StartsWith(mount_point, "/")) { + LOG(WARNING) << "mount point '" << mount_point << "' does not start with '/'"; + continue; + } + + // The simulator only supports ext4 and emmc for now. + if (fs_type != "ext4" && fs_type != "emmc") { + LOG(WARNING) << "Unsupported fs_type in " << line; + continue; + } + + fstab_info_list->emplace_back(blockdev, mount_point, fs_type); + } + + return true; +} + +bool TargetFile::EntryExists(const std::string_view name) const { + if (extracted_input_) { + std::string entry_path = path_ + "/" + std::string(name); + if (access(entry_path.c_str(), O_RDONLY) != 0) { + PLOG(WARNING) << "Failed to access " << entry_path; + return false; + } + return true; + } + + CHECK(handle_); + ZipEntry img_entry; + return FindEntry(handle_, name, &img_entry) == 0; +} + +bool TargetFile::ReadEntryToString(const std::string_view name, std::string* content) const { + if (extracted_input_) { + std::string entry_path = path_ + "/" + std::string(name); + return android::base::ReadFileToString(entry_path, content); + } + + CHECK(handle_); + ZipEntry entry; + if (auto find_err = FindEntry(handle_, name, &entry); find_err != 0) { + LOG(ERROR) << "failed to find " << name << " in the package: " << ErrorCodeString(find_err); + return false; + } + + content->resize(entry.uncompressed_length); + if (auto extract_err = ExtractToMemory( + handle_, &entry, reinterpret_cast(&content->at(0)), entry.uncompressed_length); + extract_err != 0) { + LOG(ERROR) << "failed to read " << name << " from package: " << ErrorCodeString(extract_err); + return false; + } + + return true; +} + +bool TargetFile::ExtractEntryToTempFile(const std::string_view name, + TemporaryFile* temp_file) const { + if (extracted_input_) { + std::string entry_path = path_ + "/" + std::string(name); + return std::filesystem::copy_file(entry_path, temp_file->path, + std::filesystem::copy_options::overwrite_existing); + } + + CHECK(handle_); + ZipEntry entry; + if (auto find_err = FindEntry(handle_, name, &entry); find_err != 0) { + LOG(ERROR) << "failed to find " << name << " in the package: " << ErrorCodeString(find_err); + return false; + } + + if (auto status = ExtractEntryToFile(handle_, &entry, temp_file->fd); status != 0) { + LOG(ERROR) << "Failed to extract zip entry " << name << " : " << ErrorCodeString(status); + return false; + } + return true; +} + +bool TargetFile::Open() { + if (!extracted_input_) { + if (auto ret = OpenArchive(path_.c_str(), &handle_); ret != 0) { + LOG(ERROR) << "failed to open source target file " << path_ << ": " << ErrorCodeString(ret); + return false; + } + } + + // Parse the misc info. + std::string misc_info_content; + if (!ReadEntryToString("META/misc_info.txt", &misc_info_content)) { + return false; + } + if (!ParsePropertyFile(misc_info_content, &misc_info_)) { + return false; + } + + return true; +} + +bool TargetFile::GetBuildProps(std::map>* props_map) const { + props_map->clear(); + // Parse the source zip to mock the system props and block devices. We try all the possible + // locations for build props. + constexpr std::string_view kPropLocations[] = { + "SYSTEM/build.prop", + "VENDOR/build.prop", + "PRODUCT/build.prop", + "PRODUCT_SERVICES/build.prop", + "SYSTEM/vendor/build.prop", + "SYSTEM/product/build.prop", + "SYSTEM/product_services/build.prop", + "ODM/build.prop", // legacy + "ODM/etc/build.prop", + "VENDOR/odm/build.prop", // legacy + "VENDOR/odm/etc/build.prop", + }; + for (const auto& name : kPropLocations) { + std::string build_prop_content; + if (!ReadEntryToString(name, &build_prop_content)) { + continue; + } + std::map> props; + if (!ParsePropertyFile(build_prop_content, &props)) { + LOG(ERROR) << "Failed to parse build prop in " << name; + return false; + } + for (const auto& [key, value] : props) { + if (auto it = props_map->find(key); it != props_map->end() && it->second != value) { + LOG(WARNING) << "Property " << key << " has different values in property files, we got " + << it->second << " and " << value; + } + props_map->emplace(key, value); + } + } + + return true; +} + +bool TargetFile::ExtractImage(const std::string_view entry_name, const FstabInfo& fstab_info, + const std::string_view work_dir, TemporaryFile* image_file) const { + if (!EntryExists(entry_name)) { + return false; + } + + // We don't need extra work for 'emmc'; use the image file as the block device. + if (fstab_info.fs_type == "emmc" || misc_info_.find("extfs_sparse_flag") == misc_info_.end()) { + if (!ExtractEntryToTempFile(entry_name, image_file)) { + return false; + } + } else { // treated as ext4 sparse image + TemporaryFile sparse_image{ std::string(work_dir) }; + if (!ExtractEntryToTempFile(entry_name, &sparse_image)) { + return false; + } + + // Convert the sparse image to raw. + if (!SimgToImg(sparse_image.fd, image_file->fd)) { + LOG(ERROR) << "Failed to convert " << fstab_info.mount_point << " to raw."; + return false; + } + } + + return true; +} + +bool TargetFile::ParseFstabInfo(std::vector* fstab_info_list) const { + // Parse the fstab file and extract the image files. The location of the fstab actually depends + // on some flags e.g. "no_recovery", "recovery_as_boot". Here we just try all possibilities. + constexpr std::string_view kRecoveryFstabLocations[] = { + "RECOVERY/RAMDISK/system/etc/recovery.fstab", + "RECOVERY/RAMDISK/etc/recovery.fstab", + "BOOT/RAMDISK/system/etc/recovery.fstab", + "BOOT/RAMDISK/etc/recovery.fstab", + }; + std::string fstab_content; + for (const auto& name : kRecoveryFstabLocations) { + if (std::string content; ReadEntryToString(name, &content)) { + fstab_content = std::move(content); + break; + } + } + if (fstab_content.empty()) { + LOG(ERROR) << "Failed to parse the recovery fstab file"; + return false; + } + + // Extract the images and convert them to raw. + if (!ParseFstab(fstab_content, fstab_info_list)) { + LOG(ERROR) << "Failed to mount the block devices for source build."; + return false; + } + + return true; } diff --git a/updater/update_simulator_main.cpp b/updater/update_simulator_main.cpp index d10453c2..94924e78 100644 --- a/updater/update_simulator_main.cpp +++ b/updater/update_simulator_main.cpp @@ -22,9 +22,10 @@ #include "otautil/error_code.h" #include "otautil/paths.h" #include "updater/blockimg.h" +#include "updater/build_info.h" +#include "updater/dynamic_partitions.h" #include "updater/install.h" #include "updater/simulator_runtime.h" -#include "updater/target_files.h" #include "updater/updater.h" int main(int argc, char** argv) { @@ -34,7 +35,7 @@ int main(int argc, char** argv) { if (argc != 3 && argc != 4) { LOG(ERROR) << "unexpected number of arguments: " << argc << std::endl << "Usage: " << argv[0] << " "; - return 1; + return EXIT_FAILURE; } // TODO(xunchang) implement a commandline parser, e.g. it can take an oem property so that the @@ -57,17 +58,21 @@ int main(int argc, char** argv) { Paths::Get().set_stash_directory_base(temp_stash_base.path); TemporaryFile cmd_pipe; - TemporaryDir source_temp_dir; - TargetFiles source(source_target_file, source_temp_dir.path); - Updater updater(std::make_unique(&source)); + BuildInfo source_build_info(source_temp_dir.path); + if (!source_build_info.ParseTargetFile(source_target_file, false)) { + LOG(ERROR) << "Failed to parse the target file " << source_target_file; + return EXIT_FAILURE; + } + + Updater updater(std::make_unique(&source_build_info)); if (!updater.Init(cmd_pipe.release(), package_name, false)) { - return 1; + return EXIT_FAILURE; } if (!updater.RunUpdate()) { - return 1; + return EXIT_FAILURE; } LOG(INFO) << "\nscript succeeded, result: " << updater.GetResult(); From eb586b7baf900b76f954fb4a2adbb831d0ccdf41 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Mon, 3 Jun 2019 16:14:35 +0100 Subject: [PATCH 046/171] Use icu4j instead of its deprecated legacy alias icu4j-host Bug: 134379140 Test: m droid Change-Id: I71c2ac392361b3c6dfa693194c05116422335e55 --- tools/image_generator/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/image_generator/Android.bp b/tools/image_generator/Android.bp index 2afdd5a8..83000407 100644 --- a/tools/image_generator/Android.bp +++ b/tools/image_generator/Android.bp @@ -19,7 +19,7 @@ java_library_host { static_libs: [ "commons-cli-1.2", - "icu4j-host", + "icu4j", ], srcs: [ From 3b9ef341be618885407bc302ffc235585bc01c5d Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 3 Jun 2019 12:10:54 -0700 Subject: [PATCH 047/171] minadbd: More allowed properties. Most of these properties are already part of the fingerprint. This CL allows querying them directly, instead of encouraging users to decode from fingerprints. Bug: 134027350 Test: Boot into rescue mode on taimen. Run `adb rescue getprop` with new props. Change-Id: Id4667fcaf0e908c391085b22e22c957acd01d9c4 --- minadbd/minadbd_services.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 8f2b71aa..5eda73e4 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -158,8 +158,15 @@ static void RescueInstallHostService(unique_fd sfd, const std::string& args) { static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { static const std::unordered_set kGetpropAllowedProps = { - "ro.build.fingerprint", "ro.build.date.utc", + "ro.build.fingerprint", + "ro.build.flavor", + "ro.build.id", + "ro.build.product", + "ro.build.tags", + "ro.build.version.incremental", + "ro.product.device", + "ro.product.vendor.device", }; auto allowed = kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end(); if (!allowed) { From d8db81a01464c53c9bd6dbff32194faed85b6ccc Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 4 Jun 2019 11:20:00 -0700 Subject: [PATCH 048/171] minadbd: Support `adb rescue getprop`. It dumps all the allowed properties, similar to `adb shell getprop`. Bug: 134027350 Test: Run the command under rescue mode. Change-Id: Ic0864ca0fb51505ec1e4f38af2464591aa576201 --- minadbd/minadbd_services.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 5eda73e4..d2b824cf 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -25,10 +25,10 @@ #include #include +#include #include #include #include -#include #include #include @@ -156,8 +156,11 @@ static void RescueInstallHostService(unique_fd sfd, const std::string& args) { } } +// Answers the query on a given property. The result will be written to the given sfd. If given an +// empty string, dumps all the supported properties (similar to `adb shell getprop`) in lines, e.g. +// "[prop]: [value]". static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { - static const std::unordered_set kGetpropAllowedProps = { + static const std::set kGetpropAllowedProps = { "ro.build.date.utc", "ro.build.fingerprint", "ro.build.flavor", @@ -168,12 +171,22 @@ static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { "ro.product.device", "ro.product.vendor.device", }; - auto allowed = kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end(); - if (!allowed) { + if (!prop.empty() && kGetpropAllowedProps.find(prop) == kGetpropAllowedProps.end()) { return; } - auto result = android::base::GetProperty(prop, ""); + std::string result; + if (prop.empty()) { + for (const auto& key : kGetpropAllowedProps) { + auto value = android::base::GetProperty(key, ""); + if (value.empty()) { + continue; + } + result += "[" + key + "]: [" + value + "]\n"; + } + } else { + result = android::base::GetProperty(prop, ""); + } if (result.empty()) { return; } From 57a27890cec050d36a0d2d6e7db008cd22eb0560 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 10 Jun 2019 12:41:44 -0700 Subject: [PATCH 049/171] minadbd: `adb rescue getprop` returns newline-terminated result. This change addresses the comment in [1], which makes the results of `adb shell getprop foo` and `adb rescue getprop foo` more consistent. That is, both will return newline-terminated results now. [1] https://r.android.com/c/platform/bootable/recovery/+/976340/3/minadbd/minadbd_services.cpp#188 Test: Run the following commands on taimen (under rescue mode): `adb rescue getprop ro.build.fingerprint` `adb rescue getprop ro.nonexistent` `adb rescue getprop` Change-Id: I5af47f8ea4d569b8507e259daef87749c0945f47 --- minadbd/minadbd_services.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index d2b824cf..1e6eb316 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -156,9 +156,10 @@ static void RescueInstallHostService(unique_fd sfd, const std::string& args) { } } -// Answers the query on a given property. The result will be written to the given sfd. If given an -// empty string, dumps all the supported properties (similar to `adb shell getprop`) in lines, e.g. -// "[prop]: [value]". +// Answers the query on a given property |prop|, by writing the result to the given |sfd|. The +// result will be newline-terminated, so nonexistent or nonallowed query will be answered with "\n". +// If given an empty string, dumps all the supported properties (analogous to `adb shell getprop`) +// in lines, e.g. "[prop]: [value]". static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { static const std::set kGetpropAllowedProps = { "ro.build.date.utc", @@ -171,10 +172,6 @@ static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { "ro.product.device", "ro.product.vendor.device", }; - if (!prop.empty() && kGetpropAllowedProps.find(prop) == kGetpropAllowedProps.end()) { - return; - } - std::string result; if (prop.empty()) { for (const auto& key : kGetpropAllowedProps) { @@ -184,11 +181,11 @@ static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { } result += "[" + key + "]: [" + value + "]\n"; } - } else { - result = android::base::GetProperty(prop, ""); + } else if (kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end()) { + result = android::base::GetProperty(prop, "") + "\n"; } if (result.empty()) { - return; + result = "\n"; } if (!android::base::WriteFully(sfd, result.data(), result.size())) { exit(kMinadbdHostSocketIOError); From 852d9fec6d5febc60641dfac60ea4b8f145f5b2f Mon Sep 17 00:00:00 2001 From: "Zhang, GaofengX" Date: Tue, 11 Jun 2019 11:16:30 +0800 Subject: [PATCH 050/171] Avoid key_queue_mutex deadlock in waitkey() Waitkey() is designed to obtain lock "key_queue_mutex" in the very beginning of function. int RecoveryUI::WaitKey() { std::unique_lock lk(key_queue_mutex); ... } However, there's case "key_queue_mutex" being applied again in waitkey(), thus cause deadlock. There are two reproduce scenario: 1.Executing "fastboot reboot recovery" in userspace fastboot 2.Executing "adb reboot fastboot" in recovery os When entering userspace fastboot/recovery, waitkey() will wait there for user action. fastboot/adb commands will trigger ui->interruptkey() to notify the thread waitkey() in. In the next, waitkey() will move on and call SetScreenSaveState(), which do LOG(ERROR) in fail case of brightness set. LOG(ERROR) is designed to print log on UI. Unfortunately, UI->print() applies lock "key_queue_mutex" too, so deadlock happen. Note: Here is details how lock "key_queue_mutex" applied in UI->print(): Function Print() call Function PrintV() call Function update_screen_locked() call Function draw_screen_locked() call Function draw_menu_and_test_buffer_locked() call Function IsLongPress() bool RecoveryUI::IsLongPress() { std::lock_guard lg(key_queue_mutex); bool result = key_long_press; return result; } Bug: 135078366 Test: no errors when running "fastboot reboot recovery" in userspace fastboot & "adb reboot fastboot" in recovery os Change-Id: Ida6b3c4ba9896a70021373f02a94954f0a60cf31 Signed-off-by: Zhang, GaofengX Signed-off-by: Xihua Chen --- recovery_ui/ui.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index b7107ff2..f1a79dcf 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -419,7 +419,7 @@ void RecoveryUI::SetScreensaverState(ScreensaverState state) { LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_ << "%)"; } else { - LOG(ERROR) << "Unable to set brightness to normal"; + LOG(WARNING) << "Unable to set brightness to normal"; } break; case ScreensaverState::DIMMED: @@ -429,7 +429,7 @@ void RecoveryUI::SetScreensaverState(ScreensaverState state) { << "%)"; screensaver_state_ = ScreensaverState::DIMMED; } else { - LOG(ERROR) << "Unable to set brightness to dim"; + LOG(WARNING) << "Unable to set brightness to dim"; } break; case ScreensaverState::OFF: @@ -437,7 +437,7 @@ void RecoveryUI::SetScreensaverState(ScreensaverState state) { LOG(INFO) << "Brightness: 0 (off)"; screensaver_state_ = ScreensaverState::OFF; } else { - LOG(ERROR) << "Unable to set brightness to off"; + LOG(WARNING) << "Unable to set brightness to off"; } break; default: From 39ac1c013c9c127c0ff8ad73b9cc2fbcc7f06ecb Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 12 Jun 2019 12:20:37 -0700 Subject: [PATCH 051/171] Use the new ziparchive Next std::string_view overload. Bug: http://b/129068177 Test: treehugger Change-Id: Ieec83126e36b330da33092a172e365376cd04dfe --- install/install.cpp | 2 +- install/verifier.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/install.cpp b/install/install.cpp index 8d46641d..445f3ab3 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -539,7 +539,7 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) { std::vector compatibility_info; ZipEntry info_entry; - std::string info_name; + std::string_view info_name; while (Next(cookie, &info_entry, &info_name) == 0) { std::string content(info_entry.uncompressed_length, '\0'); int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast(&content[0]), diff --git a/install/verifier.cpp b/install/verifier.cpp index 02759cdc..ab750442 100644 --- a/install/verifier.cpp +++ b/install/verifier.cpp @@ -320,7 +320,7 @@ static std::vector IterateZipEntriesAndSearchForKeys(const ZipArchi std::vector result; - std::string name; + std::string_view name; ZipEntry entry; while ((iter_status = Next(cookie, &entry, &name)) == 0) { std::vector pem_content(entry.uncompressed_length); From 980f92ec008738b4d54208ed0fe9bd9cede4d7e1 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 11 Jun 2019 15:43:43 -0700 Subject: [PATCH 052/171] InstallPackage now takes a package as parameter Therefore InstallPackage() doesn't need to worry about the details of a given Package. Bug: 127071893 Test: run update from /bin/recovery --update_package=@path, sideload a package Change-Id: I0caa36785b43924f884ee398e7ea640d7472a92e --- install/adb_install.cpp | 6 ++- install/fuse_sdcard_install.cpp | 7 ++- install/include/install/install.h | 11 ++--- install/include/install/package.h | 9 ++++ install/install.cpp | 75 +++++++++++++------------------ install/package.cpp | 18 +++++++- recovery.cpp | 10 ++++- 7 files changed, 83 insertions(+), 53 deletions(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 2de1075d..37280a34 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -110,7 +110,11 @@ static auto AdbInstallPackageHandler(RecoveryUI* ui, InstallResult* result) { break; } } - *result = InstallPackage(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui); + + auto package = + Package::CreateFilePackage(FUSE_SIDELOAD_HOST_PATHNAME, + std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); + *result = InstallPackage(package.get(), FUSE_SIDELOAD_HOST_PATHNAME, false, 0, ui); break; } diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_sdcard_install.cpp index a5caa6e7..9fdb2f34 100644 --- a/install/fuse_sdcard_install.cpp +++ b/install/fuse_sdcard_install.cpp @@ -182,8 +182,11 @@ InstallResult ApplyFromSdcard(Device* device, RecoveryUI* ui) { break; } } - - result = InstallPackage(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /* retry_count */, ui); + auto package = + Package::CreateFilePackage(FUSE_SIDELOAD_HOST_PATHNAME, + std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); + result = + InstallPackage(package.get(), FUSE_SIDELOAD_HOST_PATHNAME, false, 0 /* retry_count */, ui); break; } diff --git a/install/include/install/install.h b/install/include/install/install.h index 44a5cde9..b4b3a914 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -44,11 +44,12 @@ enum class OtaType { BRICK, }; -// Installs the given update package. This function should also wipe the cache partition after a -// successful installation if |should_wipe_cache| is true or an updater command asks to wipe the -// cache. -InstallResult InstallPackage(const std::string& package, bool should_wipe_cache, bool needs_mount, - int retry_count, RecoveryUI* ui); +// Installs the given update package. The package_id is a string provided by the caller (e.g. the +// package path) to identify the package and log to last_install. This function should also wipe the +// cache partition after a successful installation if |should_wipe_cache| is true or an updater +// command asks to wipe the cache. +InstallResult InstallPackage(Package* package, const std::string_view package_id, + bool should_wipe_cache, int retry_count, RecoveryUI* ui); // Verifies the package by ota keys. Returns true if the package is verified successfully, // otherwise returns false. diff --git a/install/include/install/package.h b/install/include/install/package.h index cd44d10b..0b423323 100644 --- a/install/include/install/package.h +++ b/install/include/install/package.h @@ -28,6 +28,11 @@ #include "verifier.h" +enum class PackageType { + kMemory, + kFile, +}; + // This class serves as a wrapper for an OTA update package. It aims to provide the common // interface for both packages loaded in memory and packages read from fd. class Package : public VerifierInterface { @@ -41,6 +46,10 @@ class Package : public VerifierInterface { virtual ~Package() = default; + virtual PackageType GetType() const = 0; + + virtual std::string GetPath() const = 0; + // Opens the package as a zip file and returns the ZipArchiveHandle. virtual ZipArchiveHandle GetZipArchiveHandle() = 0; diff --git a/install/install.cpp b/install/install.cpp index 8d46641d..b0162f09 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -320,16 +320,21 @@ static void log_max_temperature(int* max_temperature, const std::atomic& l } // If the package contains an update binary, extract it and run it. -static InstallResult TryUpdateBinary(const std::string& package, ZipArchiveHandle zip, - bool* wipe_cache, std::vector* log_buffer, - int retry_count, int* max_temperature, RecoveryUI* ui) { +static InstallResult TryUpdateBinary(Package* package, bool* wipe_cache, + std::vector* log_buffer, int retry_count, + int* max_temperature, RecoveryUI* ui) { std::map metadata; + auto zip = package->GetZipArchiveHandle(); if (!ReadMetadataFromPackage(zip, &metadata)) { LOG(ERROR) << "Failed to parse metadata in the zip file"; return INSTALL_CORRUPT; } bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false); + if (is_ab) { + CHECK(package->GetType() == PackageType::kFile); + } + // Verify against the metadata in the package first. if (is_ab && !CheckPackageMetadata(metadata, OtaType::AB)) { log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); @@ -379,10 +384,12 @@ static InstallResult TryUpdateBinary(const std::string& package, ZipArchiveHandl // updater requests logging the string (e.g. cause of the failure). // + std::string package_path = package->GetPath(); + std::vector args; if (auto setup_result = - is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args) - : SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args); + is_ab ? SetUpAbUpdateCommands(package_path, zip, pipe_write.get(), &args) + : SetUpNonAbUpdateCommands(package_path, zip, retry_count, pipe_write.get(), &args); !setup_result) { log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); return INSTALL_CORRUPT; @@ -484,11 +491,11 @@ static InstallResult TryUpdateBinary(const std::string& package, ZipArchiveHandl } if (WIFEXITED(status)) { if (WEXITSTATUS(status) != EXIT_SUCCESS) { - LOG(ERROR) << "Error in " << package << " (status " << WEXITSTATUS(status) << ")"; + LOG(ERROR) << "Error in " << package_path << " (status " << WEXITSTATUS(status) << ")"; return INSTALL_ERROR; } } else if (WIFSIGNALED(status)) { - LOG(ERROR) << "Error in " << package << " (killed by signal " << WTERMSIG(status) << ")"; + LOG(ERROR) << "Error in " << package_path << " (killed by signal " << WTERMSIG(status) << ")"; return INSTALL_ERROR; } else { LOG(FATAL) << "Invalid status code " << status; @@ -497,7 +504,7 @@ static InstallResult TryUpdateBinary(const std::string& package, ZipArchiveHandl return INSTALL_SUCCESS; } -// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the +// Verifies the compatibility info in a Treble-compatible package. Returns true directly if the // entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA // package. bool verify_package_compatibility(ZipArchiveHandle package_zip) { @@ -564,37 +571,16 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) { return false; } -static InstallResult VerifyAndInstallPackage(const std::string& path, bool* wipe_cache, - bool needs_mount, std::vector* log_buffer, - int retry_count, int* max_temperature, - RecoveryUI* ui) { +static InstallResult VerifyAndInstallPackage(Package* package, bool* wipe_cache, + std::vector* log_buffer, int retry_count, + int* max_temperature, RecoveryUI* ui) { ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); - ui->Print("Finding update package...\n"); // Give verification half the progress bar... ui->SetProgressType(RecoveryUI::DETERMINATE); ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); - LOG(INFO) << "Update location: " << path; - - // Map the update package into memory. - ui->Print("Opening update package...\n"); - - if (needs_mount) { - if (path[0] == '@') { - ensure_path_mounted(path.substr(1)); - } else { - ensure_path_mounted(path); - } - } - - auto package = Package::CreateMemoryPackage( - path, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); - if (!package) { - log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure)); - return INSTALL_CORRUPT; - } // Verify package. - if (!verify_package(package.get(), ui)) { + if (!verify_package(package, ui)) { log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure)); return INSTALL_CORRUPT; } @@ -618,18 +604,15 @@ static InstallResult VerifyAndInstallPackage(const std::string& path, bool* wipe ui->Print("Retry attempt: %d\n", retry_count); } ui->SetEnableReboot(false); - auto result = - TryUpdateBinary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui); + auto result = TryUpdateBinary(package, wipe_cache, log_buffer, retry_count, max_temperature, ui); ui->SetEnableReboot(true); ui->Print("\n"); return result; } -InstallResult InstallPackage(const std::string& path, bool should_wipe_cache, bool needs_mount, - int retry_count, RecoveryUI* ui) { - CHECK(!path.empty()); - +InstallResult InstallPackage(Package* package, const std::string_view package_id, + bool should_wipe_cache, int retry_count, RecoveryUI* ui) { auto start = std::chrono::system_clock::now(); int start_temperature = GetMaxValueFromThermalZone(); @@ -637,13 +620,19 @@ InstallResult InstallPackage(const std::string& path, bool should_wipe_cache, bo InstallResult result; std::vector log_buffer; - if (setup_install_mounts() != 0) { + + ui->Print("Finding update package...\n"); + LOG(INFO) << "Update package id: " << package_id; + if (!package) { + log_buffer.push_back(android::base::StringPrintf("error: %d", kMapFileFailure)); + result = INSTALL_CORRUPT; + } else if (setup_install_mounts() != 0) { LOG(ERROR) << "failed to set up expected mounts for install; aborting"; result = INSTALL_ERROR; } else { bool updater_wipe_cache = false; - result = VerifyAndInstallPackage(path, &updater_wipe_cache, needs_mount, &log_buffer, - retry_count, &max_temperature, ui); + result = VerifyAndInstallPackage(package, &updater_wipe_cache, &log_buffer, retry_count, + &max_temperature, ui); should_wipe_cache = should_wipe_cache || updater_wipe_cache; } @@ -671,7 +660,7 @@ InstallResult InstallPackage(const std::string& path, bool should_wipe_cache, bo // The first two lines need to be the package name and install result. std::vector log_header = { - path, + std::string(package_id), result == INSTALL_SUCCESS ? "1" : "0", "time_total: " + std::to_string(time_total), "retry: " + std::to_string(retry_count), diff --git a/install/package.cpp b/install/package.cpp index 4402f485..86fc0647 100644 --- a/install/package.cpp +++ b/install/package.cpp @@ -40,12 +40,20 @@ class MemoryPackage : public Package { ~MemoryPackage() override; + PackageType GetType() const override { + return PackageType::kMemory; + } + // Memory maps the package file if necessary. Initializes the start address and size of the // package. uint64_t GetPackageSize() const override { return package_size_; } + std::string GetPath() const override { + return path_; + } + bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override; ZipArchiveHandle GetZipArchiveHandle() override; @@ -82,10 +90,18 @@ class FilePackage : public Package { ~FilePackage() override; + PackageType GetType() const override { + return PackageType::kFile; + } + uint64_t GetPackageSize() const override { return package_size_; } + std::string GetPath() const override { + return path_; + } + bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override; ZipArchiveHandle GetZipArchiveHandle() override; @@ -253,7 +269,7 @@ ZipArchiveHandle FilePackage::GetZipArchiveHandle() { return zip_handle_; } - if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_); err != 0) { + if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_, false); err != 0) { LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err); return nullptr; } diff --git a/recovery.cpp b/recovery.cpp index eb0c2b21..db66ea7e 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -732,7 +732,15 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Installation aborted.\n"); From 9c207203b2c8a06603b23b6525621d22feb9ef1d Mon Sep 17 00:00:00 2001 From: Zhijun He Date: Wed, 19 Jun 2019 04:44:04 +0000 Subject: [PATCH 053/171] Revert "libprocessgroup users use libcutils" This reverts commit 46ec20b69298ff267fcc9f42a742015fb541b4a4. Reason for revert: breaks all camera use cases Bug: 135568875 Change-Id: I86747c0df5489f80d1966dd07669637597fb2b00 (cherry picked from commit cdbd84de26bb213fee6d8560976d9b99eea75f77) --- updater/Android.bp | 2 -- 1 file changed, 2 deletions(-) diff --git a/updater/Android.bp b/updater/Android.bp index 4e87db2d..b80cdb3a 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -45,8 +45,6 @@ cc_defaults { "libcrypto", "libcrypto_utils", "libcutils", - "libcgrouprc", - "libcgrouprc_format", "libutils", "libtune2fs", From f6158eb918fb86f94e418244a9351379ad568563 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 11 Jun 2019 16:09:07 -0700 Subject: [PATCH 054/171] Support starting fuse from a block map Factor out a new function from ApplyFromSdcard that installs a package from a local path. Inside this function, we start the fuse and choose the type of data provider depending on the path string. And similar to the existing logic, we treat the package as a block map if the path starts with a '@'. This is part of the effort to install larger than 2GiB packages on ILP32 devices. Bug: 127071893 Test: Build a 32 bit sailfish and create a 3GiB OTA package. Sideload the package, uncrypt and install the package from sdcard. Change-Id: I328ea34fa530731acbce7554bfc3059313ad6ece --- fuse_sideload/fuse_provider.cpp | 7 +- fuse_sideload/include/fuse_provider.h | 17 ++++- install/Android.bp | 2 +- ...se_sdcard_install.cpp => fuse_install.cpp} | 71 ++++++++++++------- .../{fuse_sdcard_install.h => fuse_install.h} | 9 ++- minadbd/fuse_adb_provider.h | 4 ++ recovery.cpp | 4 +- tests/unit/fuse_provider_test.cpp | 3 +- tests/unit/fuse_sideload_test.cpp | 4 ++ 9 files changed, 88 insertions(+), 33 deletions(-) rename install/{fuse_sdcard_install.cpp => fuse_install.cpp} (81%) rename install/include/install/{fuse_sdcard_install.h => fuse_install.h} (62%) diff --git a/fuse_sideload/fuse_provider.cpp b/fuse_sideload/fuse_provider.cpp index 5ee6e247..8fa1b5c2 100644 --- a/fuse_sideload/fuse_provider.cpp +++ b/fuse_sideload/fuse_provider.cpp @@ -49,6 +49,11 @@ FuseFileDataProvider::FuseFileDataProvider(const std::string& path, uint32_t blo fuse_block_size_ = block_size; } +std::unique_ptr FuseFileDataProvider::CreateFromFile(const std::string& path, + uint32_t block_size) { + return std::make_unique(path, block_size); +} + bool FuseFileDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, uint32_t start_block) const { uint64_t offset = static_cast(start_block) * fuse_block_size_; @@ -127,7 +132,7 @@ bool FuseBlockDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch return true; } -std::unique_ptr FuseBlockDataProvider::CreateFromBlockMap( +std::unique_ptr FuseBlockDataProvider::CreateFromBlockMap( const std::string& block_map_path, uint32_t fuse_block_size) { auto block_map = BlockMapData::ParseBlockMapFile(block_map_path); if (!block_map) { diff --git a/fuse_sideload/include/fuse_provider.h b/fuse_sideload/include/fuse_provider.h index 8d4ea407..3cdaef33 100644 --- a/fuse_sideload/include/fuse_provider.h +++ b/fuse_sideload/include/fuse_provider.h @@ -44,6 +44,8 @@ class FuseDataProvider { virtual bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, uint32_t start_block) const = 0; + virtual bool Valid() const = 0; + virtual void Close() {} protected: @@ -60,10 +62,13 @@ class FuseFileDataProvider : public FuseDataProvider { public: FuseFileDataProvider(const std::string& path, uint32_t block_size); + static std::unique_ptr CreateFromFile(const std::string& path, + uint32_t block_size); + bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, uint32_t start_block) const override; - bool Valid() const { + bool Valid() const override { return fd_ != -1; } @@ -78,14 +83,20 @@ class FuseFileDataProvider : public FuseDataProvider { class FuseBlockDataProvider : public FuseDataProvider { public: // Constructs the fuse provider from the block map. - static std::unique_ptr CreateFromBlockMap( - const std::string& block_map_path, uint32_t fuse_block_size); + static std::unique_ptr CreateFromBlockMap(const std::string& block_map_path, + uint32_t fuse_block_size); RangeSet ranges() const { return ranges_; } + bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, uint32_t start_block) const override; + + bool Valid() const override { + return fd_ != -1; + } + void Close() override; private: diff --git a/install/Android.bp b/install/Android.bp index 4696e501..89cc3f23 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -61,7 +61,7 @@ cc_library_static { srcs: [ "adb_install.cpp", "asn1_decoder.cpp", - "fuse_sdcard_install.cpp", + "fuse_install.cpp", "install.cpp", "package.cpp", "verifier.cpp", diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_install.cpp similarity index 81% rename from install/fuse_sdcard_install.cpp rename to install/fuse_install.cpp index 9fdb2f34..ffde4a34 100644 --- a/install/fuse_sdcard_install.cpp +++ b/install/fuse_install.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "install/fuse_sdcard_install.h" +#include "install/fuse_install.h" #include #include @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -74,7 +75,8 @@ static std::string BrowseDirectory(const std::string& path, Device* device, Reco // Skip "." and ".." entries. if (name == "." || name == "..") continue; dirs.push_back(name + "/"); - } else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) { + } else if (de->d_type == DT_REG && (android::base::EndsWithIgnoreCase(name, ".zip") || + android::base::EndsWithIgnoreCase(name, ".map"))) { entries.push_back(name); } } @@ -119,42 +121,37 @@ static std::string BrowseDirectory(const std::string& path, Device* device, Reco // Unreachable. } -static bool StartSdcardFuse(const std::string& path) { - auto file_data_reader = std::make_unique(path, 65536); +static bool StartInstallPackageFuse(std::string_view path) { + if (path.empty()) { + return false; + } + + constexpr auto FUSE_BLOCK_SIZE = 65536; + bool is_block_map = android::base::ConsumePrefix(&path, "@"); + auto file_data_reader = + is_block_map ? FuseBlockDataProvider::CreateFromBlockMap(std::string(path), FUSE_BLOCK_SIZE) + : FuseFileDataProvider::CreateFromFile(std::string(path), FUSE_BLOCK_SIZE); 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); + if (android::base::StartsWith(path, SDCARD_ROOT)) { + // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so + // that our open file continues to work but new references see it as unmounted. + umount2(SDCARD_ROOT, MNT_DETACH); + } return run_fuse_sideload(std::move(file_data_reader)) == 0; } -InstallResult ApplyFromSdcard(Device* device, RecoveryUI* ui) { - if (ensure_path_mounted(SDCARD_ROOT) != 0) { - LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n"; - return INSTALL_ERROR; - } - - 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(); - +InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui) { // We used to use fuse in a thread as opposed to a process. Since accessing // through fuse involves going from kernel to userspace to kernel, it leads // to deadlock when a page fault occurs. (Bug: 26313124) pid_t child; if ((child = fork()) == 0) { - bool status = StartSdcardFuse(path); + bool status = StartInstallPackageFuse(path); _exit(status ? EXIT_SUCCESS : EXIT_FAILURE); } @@ -203,6 +200,32 @@ InstallResult ApplyFromSdcard(Device* device, RecoveryUI* ui) { LOG(ERROR) << "Error exit from the fuse process: " << WEXITSTATUS(status); } + return result; +} + +InstallResult ApplyFromSdcard(Device* device) { + auto ui = device->GetUI(); + if (ensure_path_mounted(SDCARD_ROOT) != 0) { + LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n"; + return INSTALL_ERROR; + } + + std::string path = BrowseDirectory(SDCARD_ROOT, device, ui); + if (path.empty()) { + LOG(ERROR) << "\n-- No package file selected.\n"; + ensure_path_unmounted(SDCARD_ROOT); + return INSTALL_ERROR; + } + + // Hint the install function to read from a block map file. + if (android::base::EndsWithIgnoreCase(path, ".map")) { + path = "@" + path; + } + + ui->Print("\n-- Install %s ...\n", path.c_str()); + SetSdcardUpdateBootloaderMessage(); + + auto result = InstallWithFuseFromPath(path, ui); ensure_path_unmounted(SDCARD_ROOT); return result; } diff --git a/install/include/install/fuse_sdcard_install.h b/install/include/install/fuse_install.h similarity index 62% rename from install/include/install/fuse_sdcard_install.h rename to install/include/install/fuse_install.h index e5bb01f0..63b116ae 100644 --- a/install/include/install/fuse_sdcard_install.h +++ b/install/include/install/fuse_install.h @@ -16,8 +16,15 @@ #pragma once +#include + #include "install/install.h" #include "recovery_ui/device.h" #include "recovery_ui/ui.h" -InstallResult ApplyFromSdcard(Device* device, RecoveryUI* ui); +// Starts FUSE with the package from |path| as the data source. And installs the package from +// |FUSE_SIDELOAD_HOST_PATHNAME|. The |path| can point to the location of a package zip file or a +// block map file with the prefix '@'; e.g. /sdcard/package.zip, @/cache/recovery/block.map. +InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui); + +InstallResult ApplyFromSdcard(Device* device); diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h index c5561e57..43c07d28 100644 --- a/minadbd/fuse_adb_provider.h +++ b/minadbd/fuse_adb_provider.h @@ -29,6 +29,10 @@ class FuseAdbDataProvider : public FuseDataProvider { bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, uint32_t start_block) const override; + bool Valid() const override { + return fd_ != -1; + } + private: // The underlying source to read data from (i.e. the one that talks to the host). int fd_; diff --git a/recovery.cpp b/recovery.cpp index db66ea7e..b18a8e74 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -49,7 +49,7 @@ #include "common.h" #include "fsck_unshare_blocks.h" #include "install/adb_install.h" -#include "install/fuse_sdcard_install.h" +#include "install/fuse_install.h" #include "install/install.h" #include "install/package.h" #include "install/wipe_data.h" @@ -408,7 +408,7 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) status = ApplyFromAdb(device, false /* rescue_mode */, &reboot_action); } else { adb = false; - status = ApplyFromSdcard(device, ui); + status = ApplyFromSdcard(device); } ui->Print("\nInstall from %s completed with status %d.\n", adb ? "ADB" : "SD card", status); diff --git a/tests/unit/fuse_provider_test.cpp b/tests/unit/fuse_provider_test.cpp index c5995dd7..37f99f92 100644 --- a/tests/unit/fuse_provider_test.cpp +++ b/tests/unit/fuse_provider_test.cpp @@ -44,7 +44,8 @@ TEST(FuseBlockMapTest, CreateFromBlockMap_smoke) { ASSERT_TRUE(block_map_data); ASSERT_EQ(10000, block_map_data->file_size()); ASSERT_EQ(4096, block_map_data->fuse_block_size()); - ASSERT_EQ(RangeSet({ { 10, 11 }, { 20, 21 }, { 22, 23 } }), block_map_data->ranges()); + ASSERT_EQ(RangeSet({ { 10, 11 }, { 20, 21 }, { 22, 23 } }), + static_cast(block_map_data.get())->ranges()); } TEST(FuseBlockMapTest, ReadBlockAlignedData_smoke) { diff --git a/tests/unit/fuse_sideload_test.cpp b/tests/unit/fuse_sideload_test.cpp index 6add99f4..ea895038 100644 --- a/tests/unit/fuse_sideload_test.cpp +++ b/tests/unit/fuse_sideload_test.cpp @@ -40,6 +40,10 @@ class FuseTestDataProvider : public FuseDataProvider { bool ReadBlockAlignedData(uint8_t*, uint32_t, uint32_t) const override { return true; } + + bool Valid() const override { + return true; + } }; TEST(SideloadTest, run_fuse_sideload_wrong_parameters) { From 061267eee9ccf8e17ce1ab81e4f0845a5a49ae45 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 21 Jun 2019 11:16:45 -0700 Subject: [PATCH 055/171] updater_sample: Build SystemUpdaterSample as non-privileged app by default. Prior to the change, SystemUpdaterSample was built as privileged app. It must be installed along with the matching privapp-permissions whitelist change (as noted in updater_sample/README.md), otherwise would block device booting if installed unintentionally (e.g. with `mm` or `mmma`). This CL avoids putting the device in a bad state due to a sample app. Fixes: 135703777 Test: `lunch aosp_taimen-userdebug && m -j installclean && mmma -j bootable/recovery && m -j` Flash on device and boot successfully. Test: Run the tests per instructions in updater_sample/README.md. Change-Id: Ib8b587c77570f05f7db748fad84744fa45016aab --- CleanSpec.mk | 3 +++ updater_sample/Android.bp | 1 - updater_sample/README.md | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CleanSpec.mk b/CleanSpec.mk index 6bd1eb17..8405d20e 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -55,6 +55,9 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest/recovery_component_ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest64/recovery_component_test) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/testcases/recovery_component_test) +$(call add-clean-step, find $(OUT_DIR) -type f -name "SystemUpdaterSample*" -print0 | xargs -0 rm -f) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUpdaterSample) + # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/updater_sample/Android.bp b/updater_sample/Android.bp index 845e07b7..a014248b 100644 --- a/updater_sample/Android.bp +++ b/updater_sample/Android.bp @@ -15,7 +15,6 @@ android_app { name: "SystemUpdaterSample", sdk_version: "system_current", - privileged: true, srcs: ["src/**/*.java"], diff --git a/updater_sample/README.md b/updater_sample/README.md index 2070ebc2..2e12a2fb 100644 --- a/updater_sample/README.md +++ b/updater_sample/README.md @@ -191,6 +191,8 @@ privileged system app, so it's granted the required permissions to access ``` to `frameworks/base/data/etc/privapp-permissions-platform.xml` +4. Add `privileged: true` to SystemUpdaterSample + [building rule](https://android.googlesource.com/platform/bootable/recovery/+/refs/heads/master/updater_sample/Android.bp). 5. Build sample app `make -j SystemUpdaterSample`. 6. Build Android `make -j` 7. [Flash the device](https://source.android.com/setup/build/running) @@ -229,9 +231,9 @@ The commands are expected to be run from `$ANDROID_BUILD_TOP`. 1. Build `make -j SystemUpdaterSample` and `make -j SystemUpdaterSampleTests`. 2. Install app - `adb install $OUT/system/priv-app/SystemUpdaterSample/SystemUpdaterSample.apk` + `adb install $OUT/system/app/SystemUpdaterSample/SystemUpdaterSample.apk` 3. Install tests - `adb install $OUT/testcases/SystemUpdaterSampleTests/SystemUpdaterSampleTests.apk` + `adb install $OUT/testcases/SystemUpdaterSampleTests/arm64/SystemUpdaterSampleTests.apk` 4. Run tests `adb shell am instrument -w com.example.android.systemupdatersample.tests/android.support.test.runner.AndroidJUnitRunner` 5. Run a test file From d118833f3e55f94d1dad416c6facddb3e2d48297 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 24 May 2019 16:08:45 -0700 Subject: [PATCH 056/171] Implement updater runtime for dynamic partitions The simulator skips executing the operations for dynamic partitions, and will use the logical images under target_files/IMAGES directly. (Similar to the targets without DAP enabled) Bug: 131911365 Test: run update on cuttlefish, run simulator Change-Id: Id318d97ece4560df9f20dc5cabeb8b2e261bdf9c --- .../include/edify/updater_runtime_interface.h | 7 +- updater/Android.bp | 2 + updater/dynamic_partitions.cpp | 313 +--------------- updater/include/updater/simulator_runtime.h | 4 + updater/include/updater/updater_runtime.h | 5 + updater/simulator_runtime.cpp | 31 ++ updater/update_simulator_main.cpp | 1 + .../updater_runtime_dynamic_partitions.cpp | 336 ++++++++++++++++++ 8 files changed, 394 insertions(+), 305 deletions(-) create mode 100644 updater/updater_runtime_dynamic_partitions.cpp diff --git a/edify/include/edify/updater_runtime_interface.h b/edify/include/edify/updater_runtime_interface.h index 15ccd832..d3d26da6 100644 --- a/edify/include/edify/updater_runtime_interface.h +++ b/edify/include/edify/updater_runtime_interface.h @@ -66,4 +66,9 @@ class UpdaterRuntimeInterface { // Runs tune2fs with arguments |args|. virtual int Tune2Fs(const std::vector& args) const = 0; -}; \ No newline at end of file + + // Dynamic partition related functions. + virtual bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) = 0; + virtual bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) = 0; + virtual bool UpdateDynamicPartitions(const std::string_view op_list_value) = 0; +}; diff --git a/updater/Android.bp b/updater/Android.bp index 93eeece5..063366e5 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -104,6 +104,7 @@ cc_library_static { srcs: [ "dynamic_partitions.cpp", "updater_runtime.cpp", + "updater_runtime_dynamic_partitions.cpp", ], static_libs: [ @@ -129,6 +130,7 @@ cc_library_host_static { srcs: [ "build_info.cpp", + "dynamic_partitions.cpp", "simulator_runtime.cpp", "target_files.cpp", ], diff --git a/updater/dynamic_partitions.cpp b/updater/dynamic_partitions.cpp index b50dd75f..a340116f 100644 --- a/updater/dynamic_partitions.cpp +++ b/updater/dynamic_partitions.cpp @@ -19,46 +19,20 @@ #include #include -#include -#include -#include #include -#include #include -#include #include #include #include -#include #include -#include -#include -#include -#include #include "edify/expr.h" +#include "edify/updater_runtime_interface.h" #include "otautil/error_code.h" #include "otautil/paths.h" #include "private/utils.h" -using android::base::ParseUint; -using android::dm::DeviceMapper; -using android::dm::DmDeviceState; -using android::fs_mgr::CreateLogicalPartition; -using android::fs_mgr::DestroyLogicalPartition; -using android::fs_mgr::LpMetadata; -using android::fs_mgr::MetadataBuilder; -using android::fs_mgr::Partition; -using android::fs_mgr::PartitionOpener; - -static constexpr std::chrono::milliseconds kMapTimeout{ 1000 }; -static constexpr char kMetadataUpdatedMarker[] = "/dynamic_partition_metadata.UPDATED"; - -static std::string GetSuperDevice() { - return "/dev/block/by-name/" + fs_mgr_get_super_partition_name(); -} - static std::vector ReadStringArgs(const char* name, State* state, const std::vector>& argv, const std::vector& arg_names) { @@ -89,40 +63,14 @@ static std::vector ReadStringArgs(const char* name, State* state, return ret; } -static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) { - auto state = DeviceMapper::Instance().GetState(partition_name); - if (state == DmDeviceState::INVALID) { - return true; - } - if (state == DmDeviceState::ACTIVE) { - return DestroyLogicalPartition(partition_name, kMapTimeout); - } - LOG(ERROR) << "Unknown device mapper state: " - << static_cast>(state); - return false; -} - -static bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) { - auto state = DeviceMapper::Instance().GetState(partition_name); - if (state == DmDeviceState::INVALID) { - return CreateLogicalPartition(GetSuperDevice(), 0 /* metadata slot */, partition_name, - true /* force writable */, kMapTimeout, path); - } - - if (state == DmDeviceState::ACTIVE) { - return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path); - } - LOG(ERROR) << "Unknown device mapper state: " - << static_cast>(state); - return false; -} - Value* UnmapPartitionFn(const char* name, State* state, const std::vector>& argv) { auto args = ReadStringArgs(name, state, argv, { "name" }); if (args.empty()) return StringValue(""); - return UnmapPartitionOnDeviceMapper(args[0]) ? StringValue("t") : StringValue(""); + auto updater_runtime = state->updater->GetRuntime(); + return updater_runtime->UnmapPartitionOnDeviceMapper(args[0]) ? StringValue("t") + : StringValue(""); } Value* MapPartitionFn(const char* name, State* state, @@ -131,207 +79,12 @@ Value* MapPartitionFn(const char* name, State* state, if (args.empty()) return StringValue(""); std::string path; - bool result = MapPartitionOnDeviceMapper(args[0], &path); + auto updater_runtime = state->updater->GetRuntime(); + bool result = updater_runtime->MapPartitionOnDeviceMapper(args[0], &path); return result ? StringValue(path) : StringValue(""); } -namespace { // Ops - -struct OpParameters { - std::vector tokens; - MetadataBuilder* builder; - - bool ExpectArgSize(size_t size) const { - CHECK(!tokens.empty()); - auto actual = tokens.size() - 1; - if (actual != size) { - LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual; - return false; - } - return true; - } - const std::string& op() const { - CHECK(!tokens.empty()); - return tokens[0]; - } - const std::string& arg(size_t pos) const { - CHECK_LE(pos + 1, tokens.size()); - return tokens[pos + 1]; - } - std::optional uint_arg(size_t pos, const std::string& name) const { - auto str = arg(pos); - uint64_t ret; - if (!ParseUint(str, &ret)) { - LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str; - return std::nullopt; - } - return ret; - } -}; - -using OpFunction = std::function; -using OpMap = std::map; - -bool PerformOpResize(const OpParameters& params) { - if (!params.ExpectArgSize(2)) return false; - const auto& partition_name = params.arg(0); - auto size = params.uint_arg(1, "size"); - if (!size.has_value()) return false; - - auto partition = params.builder->FindPartition(partition_name); - if (partition == nullptr) { - LOG(ERROR) << "Failed to find partition " << partition_name - << " in dynamic partition metadata."; - return false; - } - if (!UnmapPartitionOnDeviceMapper(partition_name)) { - LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing."; - return false; - } - if (!params.builder->ResizePartition(partition, size.value())) { - LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << "."; - return false; - } - return true; -} - -bool PerformOpRemove(const OpParameters& params) { - if (!params.ExpectArgSize(1)) return false; - const auto& partition_name = params.arg(0); - - if (!UnmapPartitionOnDeviceMapper(partition_name)) { - LOG(ERROR) << "Cannot unmap " << partition_name << " before removing."; - return false; - } - params.builder->RemovePartition(partition_name); - return true; -} - -bool PerformOpAdd(const OpParameters& params) { - if (!params.ExpectArgSize(2)) return false; - const auto& partition_name = params.arg(0); - const auto& group_name = params.arg(1); - - if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) == - nullptr) { - LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << "."; - return false; - } - return true; -} - -bool PerformOpMove(const OpParameters& params) { - if (!params.ExpectArgSize(2)) return false; - const auto& partition_name = params.arg(0); - const auto& new_group = params.arg(1); - - auto partition = params.builder->FindPartition(partition_name); - if (partition == nullptr) { - LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group - << " because it is not found."; - return false; - } - - auto old_group = partition->group_name(); - if (old_group != new_group) { - if (!params.builder->ChangePartitionGroup(partition, new_group)) { - LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group - << " to group " << new_group << "."; - return false; - } - } - return true; -} - -bool PerformOpAddGroup(const OpParameters& params) { - if (!params.ExpectArgSize(2)) return false; - const auto& group_name = params.arg(0); - auto maximum_size = params.uint_arg(1, "maximum_size"); - if (!maximum_size.has_value()) return false; - - auto group = params.builder->FindGroup(group_name); - if (group != nullptr) { - LOG(ERROR) << "Cannot add group " << group_name << " because it already exists."; - return false; - } - - if (maximum_size.value() == 0) { - LOG(WARNING) << "Adding group " << group_name << " with no size limits."; - } - - if (!params.builder->AddGroup(group_name, maximum_size.value())) { - LOG(ERROR) << "Failed to add group " << group_name << " with maximum size " - << maximum_size.value() << "."; - return false; - } - return true; -} - -bool PerformOpResizeGroup(const OpParameters& params) { - if (!params.ExpectArgSize(2)) return false; - const auto& group_name = params.arg(0); - auto new_size = params.uint_arg(1, "maximum_size"); - if (!new_size.has_value()) return false; - - auto group = params.builder->FindGroup(group_name); - if (group == nullptr) { - LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found."; - return false; - } - - auto old_size = group->maximum_size(); - if (old_size != new_size.value()) { - if (!params.builder->ChangeGroupSize(group_name, new_size.value())) { - LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to " - << new_size.value() << "."; - return false; - } - } - return true; -} - -std::vector ListPartitionNamesInGroup(MetadataBuilder* builder, - const std::string& group_name) { - auto partitions = builder->ListPartitionsInGroup(group_name); - std::vector partition_names; - std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names), - [](Partition* partition) { return partition->name(); }); - return partition_names; -} - -bool PerformOpRemoveGroup(const OpParameters& params) { - if (!params.ExpectArgSize(1)) return false; - const auto& group_name = params.arg(0); - - auto partition_names = ListPartitionNamesInGroup(params.builder, group_name); - if (!partition_names.empty()) { - LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions [" - << android::base::Join(partition_names, ", ") << "]"; - return false; - } - params.builder->RemoveGroupAndPartitions(group_name); - return true; -} - -bool PerformOpRemoveAllGroups(const OpParameters& params) { - if (!params.ExpectArgSize(0)) return false; - - auto group_names = params.builder->ListGroups(); - for (const auto& group_name : group_names) { - auto partition_names = ListPartitionNamesInGroup(params.builder, group_name); - for (const auto& partition_name : partition_names) { - if (!UnmapPartitionOnDeviceMapper(partition_name)) { - LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name - << "."; - return false; - } - } - params.builder->RemoveGroupAndPartitions(group_name); - } - return true; -} - -} // namespace +static constexpr char kMetadataUpdatedMarker[] = "/dynamic_partition_metadata.UPDATED"; Value* UpdateDynamicPartitionsFn(const char* name, State* state, const std::vector>& argv) { @@ -367,56 +120,8 @@ Value* UpdateDynamicPartitionsFn(const char* name, State* state, } } - auto super_device = GetSuperDevice(); - auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0); - if (builder == nullptr) { - LOG(ERROR) << "Failed to load dynamic partition metadata."; - return StringValue(""); - } - - static const OpMap op_map{ - // clang-format off - {"resize", PerformOpResize}, - {"remove", PerformOpRemove}, - {"add", PerformOpAdd}, - {"move", PerformOpMove}, - {"add_group", PerformOpAddGroup}, - {"resize_group", PerformOpResizeGroup}, - {"remove_group", PerformOpRemoveGroup}, - {"remove_all_groups", PerformOpRemoveAllGroups}, - // clang-format on - }; - - std::vector lines = android::base::Split(op_list_value->data, "\n"); - for (const auto& line : lines) { - auto comment_idx = line.find('#'); - auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx); - op_and_args = android::base::Trim(op_and_args); - if (op_and_args.empty()) continue; - - auto tokens = android::base::Split(op_and_args, " "); - const auto& op = tokens[0]; - auto it = op_map.find(op); - if (it == op_map.end()) { - LOG(ERROR) << "Unknown operation in op_list: " << op; - return StringValue(""); - } - OpParameters params; - params.tokens = tokens; - params.builder = builder.get(); - if (!it->second(params)) { - return StringValue(""); - } - } - - auto metadata = builder->Export(); - if (metadata == nullptr) { - LOG(ERROR) << "Failed to export metadata."; - return StringValue(""); - } - - if (!UpdatePartitionTable(super_device, *metadata, 0)) { - LOG(ERROR) << "Failed to write metadata."; + auto updater_runtime = state->updater->GetRuntime(); + if (!updater_runtime->UpdateDynamicPartitions(op_list_value->data)) { return StringValue(""); } diff --git a/updater/include/updater/simulator_runtime.h b/updater/include/updater/simulator_runtime.h index 62909588..9f7847b4 100644 --- a/updater/include/updater/simulator_runtime.h +++ b/updater/include/updater/simulator_runtime.h @@ -50,6 +50,10 @@ class SimulatorRuntime : public UpdaterRuntimeInterface { int RunProgram(const std::vector& args, bool is_vfork) const override; int Tune2Fs(const std::vector& args) const override; + bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override; + bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override; + bool UpdateDynamicPartitions(const std::string_view op_list_value) override; + private: std::string FindBlockDeviceName(const std::string_view name) const override; diff --git a/updater/include/updater/updater_runtime.h b/updater/include/updater/updater_runtime.h index e97eb49b..8fc066f6 100644 --- a/updater/include/updater/updater_runtime.h +++ b/updater/include/updater/updater_runtime.h @@ -53,5 +53,10 @@ class UpdaterRuntime : public UpdaterRuntimeInterface { int RunProgram(const std::vector& args, bool is_vfork) const override; int Tune2Fs(const std::vector& args) const override; + bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override; + bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override; + bool UpdateDynamicPartitions(const std::string_view op_list_value) override; + + private: struct selabel_handle* sehandle_{ nullptr }; }; diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp index c3b3d951..c8718c71 100644 --- a/updater/simulator_runtime.cpp +++ b/updater/simulator_runtime.cpp @@ -22,6 +22,8 @@ #include #include +#include + #include #include #include @@ -95,3 +97,32 @@ bool SimulatorRuntime::WriteStringToFile(const std::string_view content, LOG(INFO) << "SKip writing " << content.size() << " bytes to file " << filename; return true; } + +bool SimulatorRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name, + std::string* path) { + *path = partition_name; + return true; +} + +bool SimulatorRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) { + LOG(INFO) << "Skip unmapping " << partition_name; + return true; +} + +bool SimulatorRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) { + const std::unordered_set commands{ + "resize", "remove", "add", "move", + "add_group", "resize_group", "remove_group", "remove_all_groups", + }; + + std::vector lines = android::base::Split(std::string(op_list_value), "\n"); + for (const auto& line : lines) { + if (line.empty() || line[0] == '#') continue; + auto tokens = android::base::Split(line, " "); + if (commands.find(tokens[0]) == commands.end()) { + LOG(ERROR) << "Unknown operation in op_list: " << line; + return false; + } + } + return true; +} diff --git a/updater/update_simulator_main.cpp b/updater/update_simulator_main.cpp index 94924e78..019c404e 100644 --- a/updater/update_simulator_main.cpp +++ b/updater/update_simulator_main.cpp @@ -48,6 +48,7 @@ int main(int argc, char** argv) { RegisterBuiltins(); RegisterInstallFunctions(); RegisterBlockImageFunctions(); + RegisterDynamicPartitionsFunctions(); TemporaryFile temp_saved_source; TemporaryFile temp_last_command; diff --git a/updater/updater_runtime_dynamic_partitions.cpp b/updater/updater_runtime_dynamic_partitions.cpp new file mode 100644 index 00000000..e9f4c97c --- /dev/null +++ b/updater/updater_runtime_dynamic_partitions.cpp @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "updater/updater_runtime.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using android::dm::DeviceMapper; +using android::dm::DmDeviceState; +using android::fs_mgr::CreateLogicalPartition; +using android::fs_mgr::DestroyLogicalPartition; +using android::fs_mgr::LpMetadata; +using android::fs_mgr::MetadataBuilder; +using android::fs_mgr::Partition; +using android::fs_mgr::PartitionOpener; + +static constexpr std::chrono::milliseconds kMapTimeout{ 1000 }; + +static std::string GetSuperDevice() { + return "/dev/block/by-name/" + fs_mgr_get_super_partition_name(); +} + +static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) { + auto state = DeviceMapper::Instance().GetState(partition_name); + if (state == DmDeviceState::INVALID) { + return true; + } + if (state == DmDeviceState::ACTIVE) { + return DestroyLogicalPartition(partition_name, kMapTimeout); + } + LOG(ERROR) << "Unknown device mapper state: " + << static_cast>(state); + return false; +} + +bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name, + std::string* path) { + auto state = DeviceMapper::Instance().GetState(partition_name); + if (state == DmDeviceState::INVALID) { + return CreateLogicalPartition(GetSuperDevice(), 0 /* metadata slot */, partition_name, + true /* force writable */, kMapTimeout, path); + } + + if (state == DmDeviceState::ACTIVE) { + return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path); + } + LOG(ERROR) << "Unknown device mapper state: " + << static_cast>(state); + return false; +} + +bool UpdaterRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) { + return ::UnmapPartitionOnDeviceMapper(partition_name); +} + +namespace { // Ops + +struct OpParameters { + std::vector tokens; + MetadataBuilder* builder; + + bool ExpectArgSize(size_t size) const { + CHECK(!tokens.empty()); + auto actual = tokens.size() - 1; + if (actual != size) { + LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual; + return false; + } + return true; + } + const std::string& op() const { + CHECK(!tokens.empty()); + return tokens[0]; + } + const std::string& arg(size_t pos) const { + CHECK_LE(pos + 1, tokens.size()); + return tokens[pos + 1]; + } + std::optional uint_arg(size_t pos, const std::string& name) const { + auto str = arg(pos); + uint64_t ret; + if (!android::base::ParseUint(str, &ret)) { + LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str; + return std::nullopt; + } + return ret; + } +}; + +using OpFunction = std::function; +using OpMap = std::map; + +bool PerformOpResize(const OpParameters& params) { + if (!params.ExpectArgSize(2)) return false; + const auto& partition_name = params.arg(0); + auto size = params.uint_arg(1, "size"); + if (!size.has_value()) return false; + + auto partition = params.builder->FindPartition(partition_name); + if (partition == nullptr) { + LOG(ERROR) << "Failed to find partition " << partition_name + << " in dynamic partition metadata."; + return false; + } + if (!UnmapPartitionOnDeviceMapper(partition_name)) { + LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing."; + return false; + } + if (!params.builder->ResizePartition(partition, size.value())) { + LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << "."; + return false; + } + return true; +} + +bool PerformOpRemove(const OpParameters& params) { + if (!params.ExpectArgSize(1)) return false; + const auto& partition_name = params.arg(0); + + if (!UnmapPartitionOnDeviceMapper(partition_name)) { + LOG(ERROR) << "Cannot unmap " << partition_name << " before removing."; + return false; + } + params.builder->RemovePartition(partition_name); + return true; +} + +bool PerformOpAdd(const OpParameters& params) { + if (!params.ExpectArgSize(2)) return false; + const auto& partition_name = params.arg(0); + const auto& group_name = params.arg(1); + + if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) == + nullptr) { + LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << "."; + return false; + } + return true; +} + +bool PerformOpMove(const OpParameters& params) { + if (!params.ExpectArgSize(2)) return false; + const auto& partition_name = params.arg(0); + const auto& new_group = params.arg(1); + + auto partition = params.builder->FindPartition(partition_name); + if (partition == nullptr) { + LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group + << " because it is not found."; + return false; + } + + auto old_group = partition->group_name(); + if (old_group != new_group) { + if (!params.builder->ChangePartitionGroup(partition, new_group)) { + LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group + << " to group " << new_group << "."; + return false; + } + } + return true; +} + +bool PerformOpAddGroup(const OpParameters& params) { + if (!params.ExpectArgSize(2)) return false; + const auto& group_name = params.arg(0); + auto maximum_size = params.uint_arg(1, "maximum_size"); + if (!maximum_size.has_value()) return false; + + auto group = params.builder->FindGroup(group_name); + if (group != nullptr) { + LOG(ERROR) << "Cannot add group " << group_name << " because it already exists."; + return false; + } + + if (maximum_size.value() == 0) { + LOG(WARNING) << "Adding group " << group_name << " with no size limits."; + } + + if (!params.builder->AddGroup(group_name, maximum_size.value())) { + LOG(ERROR) << "Failed to add group " << group_name << " with maximum size " + << maximum_size.value() << "."; + return false; + } + return true; +} + +bool PerformOpResizeGroup(const OpParameters& params) { + if (!params.ExpectArgSize(2)) return false; + const auto& group_name = params.arg(0); + auto new_size = params.uint_arg(1, "maximum_size"); + if (!new_size.has_value()) return false; + + auto group = params.builder->FindGroup(group_name); + if (group == nullptr) { + LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found."; + return false; + } + + auto old_size = group->maximum_size(); + if (old_size != new_size.value()) { + if (!params.builder->ChangeGroupSize(group_name, new_size.value())) { + LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to " + << new_size.value() << "."; + return false; + } + } + return true; +} + +std::vector ListPartitionNamesInGroup(MetadataBuilder* builder, + const std::string& group_name) { + auto partitions = builder->ListPartitionsInGroup(group_name); + std::vector partition_names; + std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names), + [](Partition* partition) { return partition->name(); }); + return partition_names; +} + +bool PerformOpRemoveGroup(const OpParameters& params) { + if (!params.ExpectArgSize(1)) return false; + const auto& group_name = params.arg(0); + + auto partition_names = ListPartitionNamesInGroup(params.builder, group_name); + if (!partition_names.empty()) { + LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions [" + << android::base::Join(partition_names, ", ") << "]"; + return false; + } + params.builder->RemoveGroupAndPartitions(group_name); + return true; +} + +bool PerformOpRemoveAllGroups(const OpParameters& params) { + if (!params.ExpectArgSize(0)) return false; + + auto group_names = params.builder->ListGroups(); + for (const auto& group_name : group_names) { + auto partition_names = ListPartitionNamesInGroup(params.builder, group_name); + for (const auto& partition_name : partition_names) { + if (!UnmapPartitionOnDeviceMapper(partition_name)) { + LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name + << "."; + return false; + } + } + params.builder->RemoveGroupAndPartitions(group_name); + } + return true; +} + +} // namespace + +bool UpdaterRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) { + auto super_device = GetSuperDevice(); + auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0); + if (builder == nullptr) { + LOG(ERROR) << "Failed to load dynamic partition metadata."; + return false; + } + + static const OpMap op_map{ + // clang-format off + {"resize", PerformOpResize}, + {"remove", PerformOpRemove}, + {"add", PerformOpAdd}, + {"move", PerformOpMove}, + {"add_group", PerformOpAddGroup}, + {"resize_group", PerformOpResizeGroup}, + {"remove_group", PerformOpRemoveGroup}, + {"remove_all_groups", PerformOpRemoveAllGroups}, + // clang-format on + }; + + std::vector lines = android::base::Split(std::string(op_list_value), "\n"); + for (const auto& line : lines) { + auto comment_idx = line.find('#'); + auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx); + op_and_args = android::base::Trim(op_and_args); + if (op_and_args.empty()) continue; + + auto tokens = android::base::Split(op_and_args, " "); + const auto& op = tokens[0]; + auto it = op_map.find(op); + if (it == op_map.end()) { + LOG(ERROR) << "Unknown operation in op_list: " << op; + return false; + } + OpParameters params; + params.tokens = tokens; + params.builder = builder.get(); + if (!it->second(params)) { + return false; + } + } + + auto metadata = builder->Export(); + if (metadata == nullptr) { + LOG(ERROR) << "Failed to export metadata."; + return false; + } + + if (!UpdatePartitionTable(super_device, *metadata, 0)) { + LOG(ERROR) << "Failed to write metadata."; + return false; + } + + return true; +} From 32148d9a0afe0e75416fff73cb2bce1f641d6742 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 24 Jun 2019 13:51:43 -0700 Subject: [PATCH 057/171] Fix build for API change to DestroyLogicalPartition(). This method no longer contains a timeout parameter; it has been removed in favor of changes to libdm. Bug: 135771280 Test: builds Change-Id: Id8c0f17c2787412bc4588af5a1bc49cc8e6edd51 --- updater/updater_runtime_dynamic_partitions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updater/updater_runtime_dynamic_partitions.cpp b/updater/updater_runtime_dynamic_partitions.cpp index e9f4c97c..b084f659 100644 --- a/updater/updater_runtime_dynamic_partitions.cpp +++ b/updater/updater_runtime_dynamic_partitions.cpp @@ -53,7 +53,7 @@ static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) { return true; } if (state == DmDeviceState::ACTIVE) { - return DestroyLogicalPartition(partition_name, kMapTimeout); + return DestroyLogicalPartition(partition_name); } LOG(ERROR) << "Unknown device mapper state: " << static_cast>(state); From fa8e02af7c51ee0f112e6d1b057208433566c6e4 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 27 Jun 2019 09:07:04 -0700 Subject: [PATCH 058/171] recovery_ui: Remove redundant menu creation. Test: Boot into recovery and "Run graphics test". Change-Id: Icbf1c230b4af57cf32b3c7a4b30ddbabc21d5618 --- recovery_ui/screen_ui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp index 82300452..f2af66c4 100644 --- a/recovery_ui/screen_ui.cpp +++ b/recovery_ui/screen_ui.cpp @@ -1263,7 +1263,7 @@ size_t ScreenRecoveryUI::ShowMenu(const std::vector& headers, return initial_selection; } - return ShowMenu(CreateMenu(headers, items, initial_selection), menu_only, key_handler); + return ShowMenu(std::move(menu), menu_only, key_handler); } size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector& backup_headers, From 09a8f76ac23c358642a256308cd68a6d4bc23b72 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 27 Jun 2019 15:01:55 -0700 Subject: [PATCH 059/171] Implement ShowMenu for StubRecoveryUI The ShowMenu in StubRecoveryUI used to return initial selection immediately. This leads to an immediate reboot if we enter recovery with a stub UI and wait for user's input. This cl changes the behavior of stub UI's ShowMenu so that it waits keys in loop, and only returns in the case of interruption or time out. Bug: 130535924 Test: start recovery with a stub UI, wait for the timeout Change-Id: I96ad8aad8930756b09101bb98ba5fbe7b53bcb37 --- recovery_ui/Android.bp | 1 + recovery_ui/include/recovery_ui/stub_ui.h | 6 ++-- recovery_ui/stub_ui.cpp | 36 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 recovery_ui/stub_ui.cpp diff --git a/recovery_ui/Android.bp b/recovery_ui/Android.bp index ee3149d5..149ef8ac 100644 --- a/recovery_ui/Android.bp +++ b/recovery_ui/Android.bp @@ -23,6 +23,7 @@ cc_library { srcs: [ "device.cpp", "screen_ui.cpp", + "stub_ui.cpp", "ui.cpp", "vr_ui.cpp", "wear_ui.cpp", diff --git a/recovery_ui/include/recovery_ui/stub_ui.h b/recovery_ui/include/recovery_ui/stub_ui.h index fb1d8c7a..511b1314 100644 --- a/recovery_ui/include/recovery_ui/stub_ui.h +++ b/recovery_ui/include/recovery_ui/stub_ui.h @@ -62,11 +62,9 @@ class StubRecoveryUI : public RecoveryUI { // menu display size_t ShowMenu(const std::vector& /* headers */, - const std::vector& /* items */, size_t initial_selection, + const std::vector& /* items */, size_t /* initial_selection */, bool /* menu_only */, - const std::function& /* key_handler */) override { - return initial_selection; - } + const std::function& /* key_handler */) override; size_t ShowPromptWipeDataMenu(const std::vector& /* backup_headers */, const std::vector& /* backup_items */, diff --git a/recovery_ui/stub_ui.cpp b/recovery_ui/stub_ui.cpp new file mode 100644 index 00000000..a56b3f72 --- /dev/null +++ b/recovery_ui/stub_ui.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "recovery_ui/stub_ui.h" + +#include + +#include "recovery_ui/device.h" + +size_t StubRecoveryUI::ShowMenu(const std::vector& /* headers */, + const std::vector& /* items */, + size_t /* initial_selection */, bool /* menu_only */, + const std::function& /*key_handler*/) { + while (true) { + int key = WaitKey(); + // Exit the loop in the case of interruption or time out. + if (key == static_cast(KeyError::INTERRUPTED) || + key == static_cast(KeyError::TIMED_OUT)) { + return static_cast(key); + } + } + LOG(FATAL) << "Unreachable key selected in ShowMenu of stub UI"; +} From c3a161e2b8232a887efe73abe51ea9071052555d Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 20 Jun 2019 18:16:49 -0700 Subject: [PATCH 060/171] Add unit tests for simulator Make sure the simulator succeeds executing common non-A/B update functions. Bug: 131911365 Test: run unit tests Change-Id: I520ce6a8827539b88a9e36f9e67eec30d8b586d4 --- tests/Android.bp | 16 +- tests/unit/{ => host}/imgdiff_test.cpp | 0 tests/unit/host/update_simulator_test.cpp | 403 ++++++++++++++++++++++ updater/target_files.cpp | 5 + 4 files changed, 414 insertions(+), 10 deletions(-) rename tests/unit/{ => host}/imgdiff_test.cpp (100%) create mode 100644 tests/unit/host/update_simulator_test.cpp diff --git a/tests/Android.bp b/tests/Android.bp index 4969c087..fec2f07d 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -157,26 +157,22 @@ cc_test_host { defaults: [ "recovery_test_defaults", + "libupdater_defaults", ], srcs: [ - "unit/imgdiff_test.cpp", + "unit/host/*", ], static_libs: [ + "libupdater_host", + "libupdater_core", "libimgdiff", - "libimgpatch", - "libotautil", "libbsdiff", - "libbspatch", - "libziparchive", - "libutils", - "libcrypto", - "libbrotli", - "libbz", "libdivsufsort64", "libdivsufsort", - "libz", + "libfstab", + "libc++fs", ], test_suites: ["general-tests"], diff --git a/tests/unit/imgdiff_test.cpp b/tests/unit/host/imgdiff_test.cpp similarity index 100% rename from tests/unit/imgdiff_test.cpp rename to tests/unit/host/imgdiff_test.cpp diff --git a/tests/unit/host/update_simulator_test.cpp b/tests/unit/host/update_simulator_test.cpp new file mode 100644 index 00000000..bf89b785 --- /dev/null +++ b/tests/unit/host/update_simulator_test.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "otautil/paths.h" +#include "otautil/print_sha1.h" +#include "updater/blockimg.h" +#include "updater/build_info.h" +#include "updater/install.h" +#include "updater/simulator_runtime.h" +#include "updater/target_files.h" +#include "updater/updater.h" + +using std::string; + +// echo -n "system.img" > system.img && img2simg system.img sparse_system_string_.img 4096 && +// hexdump -v -e '" " 12/1 "0x%02x, " "\n"' sparse_system_string_.img +// The total size of the result sparse image is 4136 bytes; and we can append 0s in the end to get +// the full image. +constexpr uint8_t SPARSE_SYSTEM_HEADER[] = { + 0x3a, 0xff, 0x26, 0xed, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x0c, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xca, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x10, 0x00, 0x00, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x2e, 0x69, 0x6d, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static void AddZipEntries(int fd, const std::map& entries) { + FILE* zip_file = fdopen(fd, "w"); + ZipWriter writer(zip_file); + for (const auto& pair : entries) { + ASSERT_EQ(0, writer.StartEntry(pair.first.c_str(), 0)); + ASSERT_EQ(0, writer.WriteBytes(pair.second.data(), pair.second.size())); + ASSERT_EQ(0, writer.FinishEntry()); + } + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); +} + +static string CalculateSha1(const string& data) { + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast(data.c_str()), data.size(), digest); + return print_sha1(digest); +} + +static void CreateBsdiffPatch(const string& src, const string& tgt, string* patch) { + TemporaryFile patch_file; + ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast(src.data()), src.size(), + reinterpret_cast(tgt.data()), tgt.size(), + patch_file.path, nullptr)); + ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, patch)); +} + +static void RunSimulation(std::string_view src_tf, std::string_view ota_package, bool expected) { + TemporaryFile cmd_pipe; + TemporaryFile temp_saved_source; + TemporaryFile temp_last_command; + TemporaryDir temp_stash_base; + + Paths::Get().set_cache_temp_source(temp_saved_source.path); + Paths::Get().set_last_command_file(temp_last_command.path); + Paths::Get().set_stash_directory_base(temp_stash_base.path); + + // Configure edify's functions. + RegisterBuiltins(); + RegisterInstallFunctions(); + RegisterBlockImageFunctions(); + + // Run the update simulation and check the result. + TemporaryDir work_dir; + BuildInfo build_info(work_dir.path); + ASSERT_TRUE(build_info.ParseTargetFile(src_tf, false)); + Updater updater(std::make_unique(&build_info)); + ASSERT_TRUE(updater.Init(cmd_pipe.release(), ota_package, false)); + ASSERT_EQ(expected, updater.RunUpdate()); + // TODO(xunchang) check the recovery&system has the expected contents. +} + +class UpdateSimulatorTest : public ::testing::Test { + protected: + void SetUp() override { + std::vector props = { + "import /oem/oem.prop oem*", + "# begin build properties", + "# autogenerated by buildinfo.sh", + "ro.build.id=OPR1.170510.001", + "ro.build.display.id=OPR1.170510.001 dev-keys", + "ro.build.version.incremental=3993052", + "ro.build.version.release=O", + "ro.build.date=Wed May 10 11:10:29 UTC 2017", + "ro.build.date.utc=1494414629", + "ro.build.type=user", + "ro.build.tags=dev-keys", + "ro.build.flavor=angler-user", + "ro.product.system.brand=google", + "ro.product.system.name=angler", + "ro.product.system.device=angler", + }; + build_prop_string_ = android::base::Join(props, "\n"); + + fstab_content_ = R"( +# +# More comments..... + +/dev/block/by-name/system /system ext4 ro,barrier=1 wait +/dev/block/by-name/vendor /vendor ext4 ro wait,verify=/dev/metadata +/dev/block/by-name/cache /cache ext4 noatime,errors=panic wait,check +/dev/block/by-name/modem /firmware vfat ro,uid=1000,gid=1000, wait +/dev/block/by-name/boot /boot emmc defaults defaults +/dev/block/by-name/recovery /recovery emmc defaults defaults +/dev/block/by-name/misc /misc emmc defaults +/dev/block/by-name/modem /modem emmc defaults defaults)"; + + raw_system_string_ = "system.img" + string(4086, '\0'); // raw image is 4096 bytes in total + sparse_system_string_ = string(SPARSE_SYSTEM_HEADER, std::end(SPARSE_SYSTEM_HEADER)) + + string(4136 - sizeof(SPARSE_SYSTEM_HEADER), '\0'); + } + + string build_prop_string_; + string fstab_content_; + string raw_system_string_; + string sparse_system_string_; +}; + +TEST_F(UpdateSimulatorTest, TargetFile_ExtractImage) { + TemporaryFile zip_file; + AddZipEntries(zip_file.release(), { { "META/misc_info.txt", "extfs_sparse_flag=-s" }, + { "IMAGES/system.img", sparse_system_string_ } }); + TargetFile target_file(zip_file.path, false); + ASSERT_TRUE(target_file.Open()); + + TemporaryDir temp_dir; + TemporaryFile raw_image; + ASSERT_TRUE(target_file.ExtractImage( + "IMAGES/system.img", FstabInfo("/dev/system", "system", "ext4"), temp_dir.path, &raw_image)); + + // Check the raw image has expected contents. + string content; + ASSERT_TRUE(android::base::ReadFileToString(raw_image.path, &content)); + string expected_content = "system.img" + string(4086, '\0'); + ASSERT_EQ(expected_content, content); +} + +TEST_F(UpdateSimulatorTest, TargetFile_ParseFstabInfo) { + TemporaryFile zip_file; + AddZipEntries(zip_file.release(), + { { "META/misc_info.txt", "" }, + { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ } }); + TargetFile target_file(zip_file.path, false); + ASSERT_TRUE(target_file.Open()); + + std::vector fstab_info; + EXPECT_TRUE(target_file.ParseFstabInfo(&fstab_info)); + + std::vector> transformed; + std::transform(fstab_info.begin(), fstab_info.end(), std::back_inserter(transformed), + [](const FstabInfo& info) { + return std::vector{ info.blockdev_name, info.mount_point, info.fs_type }; + }); + + std::vector> expected = { + { "/dev/block/by-name/system", "/system", "ext4" }, + { "/dev/block/by-name/vendor", "/vendor", "ext4" }, + { "/dev/block/by-name/cache", "/cache", "ext4" }, + { "/dev/block/by-name/boot", "/boot", "emmc" }, + { "/dev/block/by-name/recovery", "/recovery", "emmc" }, + { "/dev/block/by-name/misc", "/misc", "emmc" }, + { "/dev/block/by-name/modem", "/modem", "emmc" }, + }; + EXPECT_EQ(expected, transformed); +} + +TEST_F(UpdateSimulatorTest, BuildInfo_ParseTargetFile) { + std::map entries = { + { "META/misc_info.txt", "" }, + { "SYSTEM/build.prop", build_prop_string_ }, + { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ }, + { "IMAGES/recovery.img", "" }, + { "IMAGES/boot.img", "" }, + { "IMAGES/misc.img", "" }, + { "IMAGES/system.map", "" }, + { "IMAGES/system.img", sparse_system_string_ }, + }; + + TemporaryFile zip_file; + AddZipEntries(zip_file.release(), entries); + + TemporaryDir temp_dir; + BuildInfo build_info(temp_dir.path); + ASSERT_TRUE(build_info.ParseTargetFile(zip_file.path, false)); + + std::map expected_result = { + { "ro.build.id", "OPR1.170510.001" }, + { "ro.build.display.id", "OPR1.170510.001 dev-keys" }, + { "ro.build.version.incremental", "3993052" }, + { "ro.build.version.release", "O" }, + { "ro.build.date", "Wed May 10 11:10:29 UTC 2017" }, + { "ro.build.date.utc", "1494414629" }, + { "ro.build.type", "user" }, + { "ro.build.tags", "dev-keys" }, + { "ro.build.flavor", "angler-user" }, + { "ro.product.brand", "google" }, + { "ro.product.name", "angler" }, + { "ro.product.device", "angler" }, + }; + + for (const auto& [key, value] : expected_result) { + ASSERT_EQ(value, build_info.GetProperty(key, "")); + } + + // Check that the temp files for each block device are created successfully. + for (auto name : { "/dev/block/by-name/system", "/dev/block/by-name/recovery", + "/dev/block/by-name/boot", "/dev/block/by-name/misc" }) { + ASSERT_EQ(0, access(build_info.FindBlockDeviceName(name).c_str(), R_OK)); + } +} + +TEST_F(UpdateSimulatorTest, RunUpdateSmoke) { + string recovery_img_string = "recovery.img"; + string boot_img_string = "boot.img"; + + std::map src_entries{ + { "META/misc_info.txt", "extfs_sparse_flag=-s" }, + { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ }, + { "SYSTEM/build.prop", build_prop_string_ }, + { "IMAGES/recovery.img", "" }, + { "IMAGES/boot.img", boot_img_string }, + { "IMAGES/system.img", sparse_system_string_ }, + }; + + // Construct the source target-files. + TemporaryFile src_tf; + AddZipEntries(src_tf.release(), src_entries); + + string recovery_from_boot; + CreateBsdiffPatch(boot_img_string, recovery_img_string, &recovery_from_boot); + + // Set up the apply patch commands to patch the recovery image. + string recovery_sha1 = CalculateSha1(recovery_img_string); + string boot_sha1 = CalculateSha1(boot_img_string); + string apply_patch_source_string = android::base::StringPrintf( + "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str()); + string apply_patch_target_string = android::base::StringPrintf( + "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str()); + string check_command = android::base::StringPrintf( + R"(patch_partition_check("%s", "%s") || abort("check failed");)", + apply_patch_target_string.c_str(), apply_patch_source_string.c_str()); + string patch_command = android::base::StringPrintf( + R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("patch failed");)", + apply_patch_target_string.c_str(), apply_patch_source_string.c_str()); + + // Add the commands to update the system image. Test common commands: + // * getprop + // * ui_print + // * patch_partition + // * package_extract_file (single argument) + // * block_image_verify, block_image_update + string tgt_system_string = string(4096, 'a'); + string system_patch; + CreateBsdiffPatch(raw_system_string_, tgt_system_string, &system_patch); + + string tgt_system_hash = CalculateSha1(tgt_system_string); + string src_system_hash = CalculateSha1(raw_system_string_); + + std::vector transfer_list = { + "4", + "1", + "0", + "0", + android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,1 1 2,0,1", system_patch.size(), + src_system_hash.c_str(), tgt_system_hash.c_str()), + }; + + // Construct the updater_script. + std::vector updater_commands = { + R"(getprop("ro.product.device") == "angler" || abort("This package is for \"angler\"");)", + R"(ui_print("Source: angler/OPR1.170510.001");)", + check_command, + patch_command, + R"(block_image_verify("/dev/block/by-name/system", )" + R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )" + R"(abort("Failed to verify system.");)", + R"(block_image_update("/dev/block/by-name/system", )" + R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )" + R"(abort("Failed to verify system.");)", + }; + string updater_script = android::base::Join(updater_commands, '\n'); + + // Construct the ota update package. + std::map ota_entries{ + { "system.new.dat", "" }, + { "system.patch.dat", system_patch }, + { "system.transfer.list", android::base::Join(transfer_list, '\n') }, + { "META-INF/com/google/android/updater-script", updater_script }, + { "patch.p", recovery_from_boot }, + }; + + TemporaryFile ota_package; + AddZipEntries(ota_package.release(), ota_entries); + + RunSimulation(src_tf.path, ota_package.path, true); +} + +TEST_F(UpdateSimulatorTest, RunUpdateUnrecognizedFunction) { + std::map src_entries{ + { "META/misc_info.txt", "extfs_sparse_flag=-s" }, + { "IMAGES/system.img", sparse_system_string_ }, + { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ }, + { "SYSTEM/build.prop", build_prop_string_ }, + }; + + TemporaryFile src_tf; + AddZipEntries(src_tf.release(), src_entries); + + std::map ota_entries{ + { "system.new.dat", "" }, + { "system.patch.dat", "" }, + { "system.transfer.list", "" }, + { "META-INF/com/google/android/updater-script", R"(bad_function("");)" }, + }; + + TemporaryFile ota_package; + AddZipEntries(ota_package.release(), ota_entries); + + RunSimulation(src_tf.path, ota_package.path, false); +} + +TEST_F(UpdateSimulatorTest, RunUpdateApplyPatchFailed) { + string recovery_img_string = "recovery.img"; + string boot_img_string = "boot.img"; + + std::map src_entries{ + { "META/misc_info.txt", "extfs_sparse_flag=-s" }, + { "IMAGES/recovery.img", "" }, + { "IMAGES/boot.img", boot_img_string }, + { "IMAGES/system.img", sparse_system_string_ }, + { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ }, + { "SYSTEM/build.prop", build_prop_string_ }, + }; + + TemporaryFile src_tf; + AddZipEntries(src_tf.release(), src_entries); + + string recovery_sha1 = CalculateSha1(recovery_img_string); + string boot_sha1 = CalculateSha1(boot_img_string); + string apply_patch_source_string = android::base::StringPrintf( + "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str()); + string apply_patch_target_string = android::base::StringPrintf( + "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str()); + string check_command = android::base::StringPrintf( + R"(patch_partition_check("%s", "%s") || abort("check failed");)", + apply_patch_target_string.c_str(), apply_patch_source_string.c_str()); + string patch_command = android::base::StringPrintf( + R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("failed");)", + apply_patch_target_string.c_str(), apply_patch_source_string.c_str()); + + // Give an invalid recovery patch and expect the apply patch to fail. + // TODO(xunchang) check the cause code. + std::vector updater_commands = { + R"(ui_print("Source: angler/OPR1.170510.001");)", + check_command, + patch_command, + }; + + string updater_script = android::base::Join(updater_commands, '\n'); + std::map ota_entries{ + { "system.new.dat", "" }, + { "system.patch.dat", "" }, + { "system.transfer.list", "" }, + { "META-INF/com/google/android/updater-script", updater_script }, + { "patch.p", "random string" }, + }; + + TemporaryFile ota_package; + AddZipEntries(ota_package.release(), ota_entries); + + RunSimulation(src_tf.path, ota_package.path, false); +} diff --git a/updater/target_files.cpp b/updater/target_files.cpp index 93540b2e..092edb9a 100644 --- a/updater/target_files.cpp +++ b/updater/target_files.cpp @@ -132,6 +132,11 @@ bool TargetFile::ReadEntryToString(const std::string_view name, std::string* con return false; } + if (entry.uncompressed_length == 0) { + content->clear(); + return true; + } + content->resize(entry.uncompressed_length); if (auto extract_err = ExtractToMemory( handle_, &entry, reinterpret_cast(&content->at(0)), entry.uncompressed_length); From 7ba8f18590559cde3632a24dcfdaf6c2f5dfb31e Mon Sep 17 00:00:00 2001 From: Justin Yun Date: Fri, 28 Jun 2019 16:17:26 +0900 Subject: [PATCH 061/171] Rename product_services to system_ext Bug: 134359158 Test: build and boot Change-Id: I2ea21a0e528bdbab7d2479b48c73e7f2274ecca5 --- updater/build_info.cpp | 2 +- updater/target_files.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/updater/build_info.cpp b/updater/build_info.cpp index 8e87bd3e..3072aab5 100644 --- a/updater/build_info.cpp +++ b/updater/build_info.cpp @@ -79,7 +79,7 @@ std::string BuildInfo::GetProperty(const std::string_view key, "ro.product.name" }; const std::vector source_order = { - "product", "product_services", "odm", "vendor", "system", + "product", "odm", "vendor", "system_ext", "system", }; if (ro_product_props.find(key) != ro_product_props.end()) { std::string_view key_suffix(key); diff --git a/updater/target_files.cpp b/updater/target_files.cpp index 93540b2e..1581b25a 100644 --- a/updater/target_files.cpp +++ b/updater/target_files.cpp @@ -193,10 +193,10 @@ bool TargetFile::GetBuildProps(std::map>* "SYSTEM/build.prop", "VENDOR/build.prop", "PRODUCT/build.prop", - "PRODUCT_SERVICES/build.prop", + "SYSTEM_EXT/build.prop", "SYSTEM/vendor/build.prop", "SYSTEM/product/build.prop", - "SYSTEM/product_services/build.prop", + "SYSTEM/ext/build.prop", "ODM/build.prop", // legacy "ODM/etc/build.prop", "VENDOR/odm/build.prop", // legacy From 42d7779caf217660113c308d845cd9e11c191dd5 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 28 Jun 2019 11:04:07 -0700 Subject: [PATCH 062/171] Build libimgdiff as a host only library Stop building libimgdiff on device because we are only running patching there. Test: unit tests pass Change-Id: I4225c6b52a536617301a64c405e325799a303b40 --- applypatch/Android.bp | 4 +--- tests/Android.bp | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/applypatch/Android.bp b/applypatch/Android.bp index 620ca6cc..64dd38d2 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -114,11 +114,9 @@ cc_binary { ], } -cc_library_static { +cc_library_host_static { name: "libimgdiff", - host_supported: true, - defaults: [ "applypatch_defaults", ], diff --git a/tests/Android.bp b/tests/Android.bp index fec2f07d..1801f3b5 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -50,12 +50,11 @@ cc_defaults { }, } -// libapplypatch, libapplypatch_modes, libimgdiff, libimgpatch +// libapplypatch, libapplypatch_modes, libimgpatch libapplypatch_static_libs = [ "libapplypatch_modes", "libapplypatch", "libedify", - "libimgdiff", "libimgpatch", "libotautil", "libbsdiff", From 1cf8eb7559d161b8a11d83e2518316b25331fbb6 Mon Sep 17 00:00:00 2001 From: Robin Lee Date: Fri, 5 Jul 2019 22:56:20 +0200 Subject: [PATCH 063/171] Add a GetMappedPackageLength to Updater Corresponds to GetMappedPackageAddress. There is at least one custom device recovery with an extension to copy the package somewhere else as a backup. Ability to do this was removed in change ag/955273. Test: lunch cf_x86_tv-eng; make Test: atest recovery_unit_test recovery_component_test Change-Id: I0e61d8a8839c47721874526504ea03b9ca7bafa9 --- edify/include/edify/updater_interface.h | 1 + updater/include/updater/updater.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/edify/include/edify/updater_interface.h b/edify/include/edify/updater_interface.h index a4d581ee..aa977e3c 100644 --- a/edify/include/edify/updater_interface.h +++ b/edify/include/edify/updater_interface.h @@ -44,4 +44,5 @@ class UpdaterInterface { virtual ZipArchiveHandle GetPackageHandle() const = 0; virtual std::string GetResult() const = 0; virtual uint8_t* GetMappedPackageAddress() const = 0; + virtual size_t GetMappedPackageLength() const = 0; }; diff --git a/updater/include/updater/updater.h b/updater/include/updater/updater.h index 08816bf3..8676b603 100644 --- a/updater/include/updater/updater.h +++ b/updater/include/updater/updater.h @@ -62,10 +62,12 @@ class Updater : public UpdaterInterface { std::string GetResult() const override { return result_; } - uint8_t* GetMappedPackageAddress() const override { return mapped_package_.addr; } + size_t GetMappedPackageLength() const override { + return mapped_package_.length; + } private: friend class UpdaterTestBase; From 290322b0608538ed4afb713ba5ca3a8fad5b7b2b Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Mon, 8 Jul 2019 15:54:33 -0700 Subject: [PATCH 064/171] minadbd: delete unnecessary fdevent.h include. Test: treehugger Change-Id: I09e9850dabbd71bffbe568f240f9fde8b3743e5e --- minadbd/minadbd_services.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 1e6eb316..5792c7a1 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -41,7 +41,6 @@ #include "adb.h" #include "adb_unique_fd.h" #include "adb_utils.h" -#include "fdevent.h" #include "fuse_adb_provider.h" #include "fuse_sideload.h" #include "minadbd_types.h" From 2223e6a9f8bf24b023e8ae3103b50c37def3147e Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 8 Jul 2019 18:07:22 -0700 Subject: [PATCH 065/171] minadbd sends heartbeat to rescue service for getprop command. We start minadbd and rescue services in two processes. In particular, minadbd handles the requests from host, then communicates with rescue service to do install/wipe works. When resuce service doesn't see any request in a pre-defined timeout (currently 300s), rescue service will exit to avoid endless waiting. This CL changes minadbd to additionally send a no-op command to rescue service as a heartbeat signal, so that host side can finish time-consuming operations (e.g. downloading over network) while keeping rescue service alive. Bug: 136457446 Test: Enter resuce mode on blueline. Send `adb rescue getprop ro.build.fingerprint` and check that rescue service doesn't exit. Test: Stop sending the getprop command. Check that rescue service exits after 300s. Change-Id: Ib9d5ed710cfa94ecfe6cf393a71a0b67b2539531 --- install/adb_install.cpp | 4 +++- minadbd/minadbd_services.cpp | 8 ++++++++ minadbd/minadbd_types.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 37280a34..ed664429 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -367,11 +367,13 @@ InstallResult ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinActi "\n\nNow send the package you want to apply\n" "to the device with \"adb sideload \"...\n"); } else { - ui->Print("\n\nWaiting for rescue commands...\n"); command_map.emplace(MinadbdCommand::kWipeData, [&device]() { bool result = WipeData(device, false); return std::make_pair(result, true); }); + command_map.emplace(MinadbdCommand::kNoOp, []() { return std::make_pair(true, true); }); + + ui->Print("\n\nWaiting for rescue commands...\n"); } CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode); diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 1e6eb316..0becfa43 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -190,6 +190,14 @@ static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { if (!android::base::WriteFully(sfd, result.data(), result.size())) { exit(kMinadbdHostSocketIOError); } + + // Send heartbeat signal to keep the rescue service alive. + if (!WriteCommandToFd(MinadbdCommand::kNoOp, minadbd_socket)) { + exit(kMinadbdSocketIOError); + } + if (MinadbdCommandStatus status; !WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } } // Reboots into the given target. We don't reboot directly from minadbd, but going through recovery diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h index 99fd45e8..002523f1 100644 --- a/minadbd/minadbd_types.h +++ b/minadbd/minadbd_types.h @@ -53,6 +53,7 @@ enum class MinadbdCommand : uint32_t { kRebootRescue = 6, kWipeCache = 7, kWipeData = 8, + kNoOp = 9, // Last but invalid command. kError, From 1df3ce7ea8b8fe2146ffd946d0c867c66e32c36b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 9 Jul 2019 11:16:51 -0700 Subject: [PATCH 066/171] recovery_ui: Remove RecoveryUI::last_key. It's a private member, and the last user has been removed in [1] in 2015. [1] commit ec28340cf3af1029a00db1c83d78d14e8798e245, https://android-review.googlesource.com/c/platform/bootable/recovery/+/146330 Test: mmma -j bootable/recovery Change-Id: I65a2370cb20a7b296888425a44a42c8b90abc766 --- recovery_ui/include/recovery_ui/ui.h | 2 +- recovery_ui/ui.cpp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h index 797e2f0d..d2ebad25 100644 --- a/recovery_ui/include/recovery_ui/ui.h +++ b/recovery_ui/include/recovery_ui/ui.h @@ -230,6 +230,7 @@ class RecoveryUI { bool InitScreensaver(); void SetScreensaverState(ScreensaverState state); + // Key event input queue std::mutex key_queue_mutex; std::condition_variable key_queue_cond; @@ -243,7 +244,6 @@ class RecoveryUI { int rel_sum; int consecutive_power_keys; - int last_key; bool has_power_key; bool has_up_key; diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index cf0d3b56..1f1cd18b 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -70,7 +70,6 @@ RecoveryUI::RecoveryUI() key_down_count(0), enable_reboot(true), consecutive_power_keys(0), - last_key(-1), has_power_key(false), has_up_key(false), has_down_key(false), @@ -585,7 +584,6 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { consecutive_power_keys = 0; } - last_key = key; return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE; } From e5218615084c3bb4f12c91d0ebed823927f1bc68 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 25 Jun 2019 13:59:39 -0700 Subject: [PATCH 067/171] Create a fallback to install from fuse if mmap fails We may fail to memory map the package on 32 bit builds for packages with 2GiB+ size. This cl tries to install the package with fuse when memory map fails in such cases. Bug: 127071893 Test: build 32 bit version sailfish, push package and block.map, reboot into recovery with the corresponding update_package argument. Change-Id: I5dae4f3e27ccaf8d64ff3657d36f0e75db2330b0 --- install/fuse_install.cpp | 7 ++++--- otautil/sysutil.cpp | 2 +- recovery.cpp | 28 ++++++++++++++++++++++++---- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/install/fuse_install.cpp b/install/fuse_install.cpp index ffde4a34..8a7a278e 100644 --- a/install/fuse_install.cpp +++ b/install/fuse_install.cpp @@ -128,11 +128,12 @@ static bool StartInstallPackageFuse(std::string_view path) { constexpr auto FUSE_BLOCK_SIZE = 65536; bool is_block_map = android::base::ConsumePrefix(&path, "@"); - auto file_data_reader = + auto fuse_data_provider = is_block_map ? FuseBlockDataProvider::CreateFromBlockMap(std::string(path), FUSE_BLOCK_SIZE) : FuseFileDataProvider::CreateFromFile(std::string(path), FUSE_BLOCK_SIZE); - if (!file_data_reader->Valid()) { + if (!fuse_data_provider || !fuse_data_provider->Valid()) { + LOG(ERROR) << "Failed to create fuse data provider."; return false; } @@ -142,7 +143,7 @@ static bool StartInstallPackageFuse(std::string_view path) { umount2(SDCARD_ROOT, MNT_DETACH); } - return run_fuse_sideload(std::move(file_data_reader)) == 0; + return run_fuse_sideload(std::move(fuse_data_provider)) == 0; } InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui) { diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp index a8829858..6cd46c6a 100644 --- a/otautil/sysutil.cpp +++ b/otautil/sysutil.cpp @@ -38,7 +38,7 @@ BlockMapData BlockMapData::ParseBlockMapFile(const std::string& block_map_path) { std::string content; if (!android::base::ReadFileToString(block_map_path, &content)) { - LOG(ERROR) << "Failed to read " << block_map_path; + PLOG(ERROR) << "Failed to read " << block_map_path; return {}; } diff --git a/recovery.cpp b/recovery.cpp index b18a8e74..97ca0a50 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -84,6 +84,8 @@ const char* reason = nullptr; * * The arguments which may be supplied in the recovery.command file: * --update_package=path - verify install an OTA package file + * --install_with_fuse - install the update package with FUSE. This allows installation of large + * packages on LP32 builds. Since the mmap will otherwise fail due to out of memory. * --wipe_data - erase user data (and cache), then reboot * --prompt_and_wipe_data - prompt the user that data is corrupt, with their consent erase user * data (and cache), then reboot @@ -577,6 +579,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Installation aborted.\n"); From ea3c4a49489c80edd9ef745ad438868a5689939a Mon Sep 17 00:00:00 2001 From: Justin Yun Date: Tue, 2 Jul 2019 08:53:25 +0900 Subject: [PATCH 068/171] Change the symlink to system_ext As system_ext partition will be linked to system/system_ext, update the path to system/system_ext. Bug: 134359158 Test: build Change-Id: I5a49adfe7e045e24bf6dfbf6990a965b8a5e1ce1 --- updater/target_files.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updater/target_files.cpp b/updater/target_files.cpp index 2789683b..919ec4e0 100644 --- a/updater/target_files.cpp +++ b/updater/target_files.cpp @@ -201,7 +201,7 @@ bool TargetFile::GetBuildProps(std::map>* "SYSTEM_EXT/build.prop", "SYSTEM/vendor/build.prop", "SYSTEM/product/build.prop", - "SYSTEM/ext/build.prop", + "SYSTEM/system_ext/build.prop", "ODM/build.prop", // legacy "ODM/etc/build.prop", "VENDOR/odm/build.prop", // legacy From b8a959b00fe57fc431034f9bea3c7b3380d4b6a5 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 14 Jun 2019 15:35:31 -0700 Subject: [PATCH 069/171] Add a new key_pressed_mutex The following variables in recovery ui were protected by key_queue_mutex. But the purpose of key_queue_mutex is to protect the key_queue, which will be changed after we already have a key code. So getting the key pressed should be orthogonal to the key queue. And adding a mutex will help to avoid deadlocks in b/135078366. Variables include: char key_pressed[KEY_MAX + 1]; int key_last_down; bool key_long_press; int key_down_count; bool enable_reboot; Bug: 135078366 Test: boot into recovery and press keys Change-Id: Ie2cfcf1f2fec49b53f8fac97aa9a2c60f15b84f9 --- recovery_ui/include/recovery_ui/ui.h | 16 ++++++++++------ recovery_ui/ui.cpp | 12 ++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h index 797e2f0d..0b29c29f 100644 --- a/recovery_ui/include/recovery_ui/ui.h +++ b/recovery_ui/include/recovery_ui/ui.h @@ -230,18 +230,22 @@ class RecoveryUI { bool InitScreensaver(); void SetScreensaverState(ScreensaverState state); + // Key event input queue std::mutex key_queue_mutex; std::condition_variable key_queue_cond; bool key_interrupted_; int key_queue[256], key_queue_len; - char key_pressed[KEY_MAX + 1]; // under key_queue_mutex - int key_last_down; // under key_queue_mutex - bool key_long_press; // under key_queue_mutex - int key_down_count; // under key_queue_mutex - bool enable_reboot; // under key_queue_mutex - int rel_sum; + // key press events + std::mutex key_press_mutex; + char key_pressed[KEY_MAX + 1]; + int key_last_down; + bool key_long_press; + int key_down_count; + bool enable_reboot; + + int rel_sum; int consecutive_power_keys; int last_key; diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index cf0d3b56..51ad169c 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -346,7 +346,7 @@ void RecoveryUI::ProcessKey(int key_code, int updown) { bool long_press = false; { - std::lock_guard lg(key_queue_mutex); + std::lock_guard lg(key_press_mutex); key_pressed[key_code] = updown; if (updown) { ++key_down_count; @@ -393,7 +393,7 @@ void RecoveryUI::TimeKey(int key_code, int count) { std::this_thread::sleep_for(750ms); // 750 ms == "long" bool long_press = false; { - std::lock_guard lg(key_queue_mutex); + std::lock_guard lg(key_press_mutex); if (key_last_down == key_code && key_down_count == count) { long_press = key_long_press = true; } @@ -518,13 +518,13 @@ bool RecoveryUI::IsUsbConnected() { } bool RecoveryUI::IsKeyPressed(int key) { - std::lock_guard lg(key_queue_mutex); + std::lock_guard lg(key_press_mutex); int pressed = key_pressed[key]; return pressed; } bool RecoveryUI::IsLongPress() { - std::lock_guard lg(key_queue_mutex); + std::lock_guard lg(key_press_mutex); bool result = key_long_press; return result; } @@ -548,7 +548,7 @@ void RecoveryUI::FlushKeys() { RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { { - std::lock_guard lg(key_queue_mutex); + std::lock_guard lg(key_press_mutex); key_long_press = false; } @@ -592,6 +592,6 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { void RecoveryUI::KeyLongPress(int) {} void RecoveryUI::SetEnableReboot(bool enabled) { - std::lock_guard lg(key_queue_mutex); + std::lock_guard lg(key_press_mutex); enable_reboot = enabled; } From 45c40ec87679570aa35be5d8a70e09dd8eeaf5ef Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 2 Jul 2019 15:09:24 -0700 Subject: [PATCH 070/171] Remove libimgpatch Stop building libimgpatch as it's merely a subset of libapplypatch. Test: unit tests pass Change-Id: I0735ec053344404434a50e53a36e3f55964c2e4f --- applypatch/Android.bp | 32 -------------------------------- tests/Android.bp | 5 ++--- 2 files changed, 2 insertions(+), 35 deletions(-) diff --git a/applypatch/Android.bp b/applypatch/Android.bp index 64dd38d2..42aa5295 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -168,35 +168,3 @@ cc_binary_host { "libz", ], } - -cc_library_static { - name: "libimgpatch", - - // The host module is for recovery_host_test (Linux only). - host_supported: true, - - defaults: [ - "applypatch_defaults", - ], - - srcs: [ - "bspatch.cpp", - "imgpatch.cpp", - ], - - static_libs: [ - "libbase", - "libbspatch", - "libbz", - "libcrypto", - "libedify", - "libotautil", - "libz", - ], - - target: { - darwin: { - enabled: false, - }, - }, -} diff --git a/tests/Android.bp b/tests/Android.bp index 1801f3b5..a8670401 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -50,12 +50,11 @@ cc_defaults { }, } -// libapplypatch, libapplypatch_modes, libimgpatch +// libapplypatch, libapplypatch_modes libapplypatch_static_libs = [ "libapplypatch_modes", "libapplypatch", "libedify", - "libimgpatch", "libotautil", "libbsdiff", "libbspatch", @@ -180,7 +179,7 @@ cc_test_host { target: { darwin: { - // libimgdiff is not available on the Mac. + // libapplypatch in "libupdater_defaults" is not available on the Mac. enabled: false, }, }, From b42281a423356b55a3e0138316497999f04a706b Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 2 Jul 2019 15:39:59 -0700 Subject: [PATCH 071/171] Drop the device specific support for update host simulator The device specific libs prevent us from building an universal simulator to add to the otatools. Drop the support since there is currently no active users of the simulator extension; plus we will unlikely to implement the device specific simulator runtime. As a alternation, we will add the commandline arguments to skip certain unsupported functions, and move the simulator build to the bp file. Bug: 131911365 Test: mma Change-Id: I3ff0f45dbebe3ed72d5f4670a869b40e6cfd5a7c --- updater/Android.mk | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/updater/Android.mk b/updater/Android.mk index 63fd7bdf..93525c12 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -112,8 +112,8 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true include $(BUILD_EXECUTABLE) - -# update_host_simulator (static executable) +# TODO(xunchang) move to bp file +# update_host_simulator (host executable) # =============================== include $(CLEAR_VARS) @@ -133,21 +133,10 @@ LOCAL_CFLAGS := \ LOCAL_STATIC_LIBRARIES := \ libupdater_host \ libupdater_core \ - $(TARGET_RECOVERY_UPDATER_HOST_LIBS) \ - $(TARGET_RECOVERY_UPDATER_HOST_EXTRA_LIBS) \ $(updater_common_static_libraries) \ libfstab \ libc++fs LOCAL_MODULE_CLASS := EXECUTABLES -inc := $(call local-generated-sources-dir)/register.inc - -$(inc) : libs := $(TARGET_RECOVERY_UPDATER_HOST_LIBS) -$(inc) : - $(call generate-register-inc,$@,$(libs)) - -LOCAL_GENERATED_SOURCES := $(inc) - -inc := include $(BUILD_HOST_EXECUTABLE) From 7efd23338a0d18d596aa1058896e72d1ee3cb501 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 15 Jul 2019 16:13:49 -0700 Subject: [PATCH 072/171] Add command line parser for simulator Add a command line parser. Also add the support to parse the oem property file and skip certain functions. Bug: 131911365 Test: run simulator for wear builds Change-Id: Ide306b53d3f42b29c02279969aeb18bec4045d6f --- updater/include/updater/build_info.h | 9 +++ updater/simulator_runtime.cpp | 6 +- updater/update_simulator_main.cpp | 92 +++++++++++++++++++++++++--- 3 files changed, 97 insertions(+), 10 deletions(-) diff --git a/updater/include/updater/build_info.h b/updater/include/updater/build_info.h index a1355e89..22299579 100644 --- a/updater/include/updater/build_info.h +++ b/updater/include/updater/build_info.h @@ -51,9 +51,18 @@ class BuildInfo { // Parses the given target-file, initializes the build properties and extracts the images. bool ParseTargetFile(const std::string_view target_file_path, bool extracted_input); + std::string GetOemSettings() const { + return oem_settings_; + } + void SetOemSettings(const std::string_view oem_settings) { + oem_settings_ = oem_settings; + } + private: // A map to store the system properties during simulation. std::map> build_props_; + // A file that contains the oem properties. + std::string oem_settings_; // A map from the blockdev_name to the FakeBlockDevice object, which contains the path to the // temporary file. std::map> blockdev_map_; diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp index c8718c71..d2074d69 100644 --- a/updater/simulator_runtime.cpp +++ b/updater/simulator_runtime.cpp @@ -87,7 +87,11 @@ int SimulatorRuntime::WipeBlockDevice(const std::string_view filename, size_t /* } bool SimulatorRuntime::ReadFileToString(const std::string_view filename, - std::string* /* content */) const { + std::string* content) const { + if (android::base::EndsWith(filename, "oem.prop")) { + return android::base::ReadFileToString(source_->GetOemSettings(), content); + } + LOG(INFO) << "SKip reading filename " << filename; return true; } diff --git a/updater/update_simulator_main.cpp b/updater/update_simulator_main.cpp index 019c404e..278a4405 100644 --- a/updater/update_simulator_main.cpp +++ b/updater/update_simulator_main.cpp @@ -14,11 +14,18 @@ * limitations under the License. */ +#include +#include +#include + #include +#include #include #include +#include +#include "edify/expr.h" #include "otautil/error_code.h" #include "otautil/paths.h" #include "updater/blockimg.h" @@ -28,21 +35,67 @@ #include "updater/simulator_runtime.h" #include "updater/updater.h" +using namespace std::string_literals; + +void Usage(std::string_view name) { + LOG(INFO) << "Usage: " << name << "[--oem_settings ]" + << "[--skip_functions ]" + << " --source " + << " --ota_package "; +} + +Value* SimulatorPlaceHolderFn(const char* name, State* /* state */, + const std::vector>& /* argv */) { + LOG(INFO) << "Skip function " << name << " in host simulation"; + return StringValue("t"); +} + int main(int argc, char** argv) { // Write the logs to stdout. android::base::InitLogging(argv, &android::base::StderrLogger); - if (argc != 3 && argc != 4) { - LOG(ERROR) << "unexpected number of arguments: " << argc << std::endl - << "Usage: " << argv[0] << " "; - return EXIT_FAILURE; + std::string oem_settings; + std::string skip_function_file; + std::string source_target_file; + std::string package_name; + + constexpr struct option OPTIONS[] = { + { "oem_settings", required_argument, nullptr, 0 }, + { "ota_package", required_argument, nullptr, 0 }, + { "skip_functions", required_argument, nullptr, 0 }, + { "source", required_argument, nullptr, 0 }, + { nullptr, 0, nullptr, 0 }, + }; + + int arg; + int option_index; + while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) { + if (arg != 0) { + LOG(ERROR) << "Invalid command argument"; + Usage(argv[0]); + return EXIT_FAILURE; + } + auto option_name = OPTIONS[option_index].name; + // The same oem property file used during OTA generation. It's needed for file_getprop() to + // return the correct value for the source build. + if (option_name == "oem_settings"s) { + oem_settings = optarg; + } else if (option_name == "skip_functions"s) { + skip_function_file = optarg; + } else if (option_name == "source"s) { + source_target_file = optarg; + } else if (option_name == "ota_package"s) { + package_name = optarg; + } else { + Usage(argv[0]); + return EXIT_FAILURE; + } } - // TODO(xunchang) implement a commandline parser, e.g. it can take an oem property so that the - // file_getprop() will return correct value. - - std::string source_target_file = argv[1]; - std::string package_name = argv[2]; + if (source_target_file.empty() || package_name.empty()) { + Usage(argv[0]); + return EXIT_FAILURE; + } // Configure edify's functions. RegisterBuiltins(); @@ -50,6 +103,22 @@ int main(int argc, char** argv) { RegisterBlockImageFunctions(); RegisterDynamicPartitionsFunctions(); + if (!skip_function_file.empty()) { + std::string content; + if (!android::base::ReadFileToString(skip_function_file, &content)) { + PLOG(ERROR) << "Failed to read " << skip_function_file; + return EXIT_FAILURE; + } + + auto lines = android::base::Split(content, "\n"); + for (const auto& line : lines) { + if (line.empty() || android::base::StartsWith(line, "#")) { + continue; + } + RegisterFunction(line, SimulatorPlaceHolderFn); + } + } + TemporaryFile temp_saved_source; TemporaryFile temp_last_command; TemporaryDir temp_stash_base; @@ -67,6 +136,11 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } + if (!oem_settings.empty()) { + CHECK_EQ(0, access(oem_settings.c_str(), R_OK)); + source_build_info.SetOemSettings(oem_settings); + } + Updater updater(std::make_unique(&source_build_info)); if (!updater.Init(cmd_pipe.release(), package_name, false)) { return EXIT_FAILURE; From 793e8943eb1f1c968923de51b18403e717a05c79 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 8 Jul 2019 18:07:22 -0700 Subject: [PATCH 073/171] minadbd sends heartbeat to rescue service for getprop command. We start minadbd and rescue services in two processes. In particular, minadbd handles the requests from host, then communicates with rescue service to do install/wipe works. When resuce service doesn't see any request in a pre-defined timeout (currently 300s), rescue service will exit to avoid endless waiting. This CL changes minadbd to additionally send a no-op command to rescue service as a heartbeat signal, so that host side can finish time-consuming operations (e.g. downloading over network) while keeping rescue service alive. Bug: 136457446 Test: Enter resuce mode on blueline. Send `adb rescue getprop ro.build.fingerprint` and check that rescue service doesn't exit. Test: Stop sending the getprop command. Check that rescue service exits after 300s. Change-Id: Ib9d5ed710cfa94ecfe6cf393a71a0b67b2539531 Merged-In: Ib9d5ed710cfa94ecfe6cf393a71a0b67b2539531 (cherry picked from commit 2223e6a9f8bf24b023e8ae3103b50c37def3147e) (cherry picked from commit 0bbb2ed53eb4dc4ae8d447062482f9eda5ef9a91) (cherry picked from commit dd0158ac6016f2853d1af336e345980e06144abd) --- install/adb_install.cpp | 4 +++- minadbd/minadbd_services.cpp | 8 ++++++++ minadbd/minadbd_types.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 4dd1f1b0..9497df50 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -363,11 +363,13 @@ int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot "\n\nNow send the package you want to apply\n" "to the device with \"adb sideload \"...\n"); } else { - ui->Print("\n\nWaiting for rescue commands...\n"); command_map.emplace(MinadbdCommand::kWipeData, [&device]() { bool result = WipeData(device, false); return std::make_pair(result, true); }); + command_map.emplace(MinadbdCommand::kNoOp, []() { return std::make_pair(true, true); }); + + ui->Print("\n\nWaiting for rescue commands...\n"); } CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode); diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 1c4c0f49..6c10274d 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -173,6 +173,14 @@ static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { if (!android::base::WriteFully(sfd, result.data(), result.size())) { exit(kMinadbdHostSocketIOError); } + + // Send heartbeat signal to keep the rescue service alive. + if (!WriteCommandToFd(MinadbdCommand::kNoOp, minadbd_socket)) { + exit(kMinadbdSocketIOError); + } + if (MinadbdCommandStatus status; !WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } } // Reboots into the given target. We don't reboot directly from minadbd, but going through recovery diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h index 99fd45e8..002523f1 100644 --- a/minadbd/minadbd_types.h +++ b/minadbd/minadbd_types.h @@ -53,6 +53,7 @@ enum class MinadbdCommand : uint32_t { kRebootRescue = 6, kWipeCache = 7, kWipeData = 8, + kNoOp = 9, // Last but invalid command. kError, From 164c60a4f3de934bab7035114546e9500cd8f232 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 15 May 2019 13:59:39 -0700 Subject: [PATCH 074/171] Clean up some global variables in common.h Some global variables are only used for recovery.cpp and recovery_main.cpp, remove them from common.h and handle their usage accordingly. Variables include: static constexpr int kRecoveryApiVersion; extern struct selabel_handle* sehandle; extern RecoveryUI* ui; extern bool has_cache; bool is_ro_debuggable(); Test: unit tests pass, boot into recovery mode and run graphic tests Change-Id: If83a005786c9b38412731da97aaf85af69a3b917 --- common.h | 13 -------- install/install.cpp | 5 ++- otautil/include/otautil/logging.h | 2 +- otautil/include/otautil/roots.h | 3 ++ otautil/logging.cpp | 17 +++++----- otautil/roots.cpp | 8 +++++ recovery.cpp | 53 +++++++++++++++---------------- recovery_main.cpp | 24 +++++++------- 8 files changed, 60 insertions(+), 65 deletions(-) diff --git a/common.h b/common.h index a524a418..128a69d9 100644 --- a/common.h +++ b/common.h @@ -18,21 +18,8 @@ #include -// Not using the command-line defined macro here because this header could be included by -// device-specific recovery libraries. We static assert the value consistency in recovery.cpp. -static constexpr int kRecoveryApiVersion = 3; - -class RecoveryUI; -struct selabel_handle; - -extern struct selabel_handle* sehandle; -extern RecoveryUI* ui; -extern bool has_cache; - // The current stage, e.g. "1/2". extern std::string stage; // The reason argument provided in "--reason=". extern const char* reason; - -bool is_ro_debuggable(); diff --git a/install/install.cpp b/install/install.cpp index 09b88392..9d67b010 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -60,7 +60,8 @@ using namespace std::chrono_literals; static constexpr int kRecoveryApiVersion = 3; -// Assert the version defined in code and in Android.mk are consistent. +// We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed +// into target_files.zip. Assert the version defined in code and in Android.mk are consistent. static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions."); // Default allocation of progress bar segments to operations @@ -621,6 +622,8 @@ InstallResult InstallPackage(Package* package, const std::string_view package_id InstallResult result; std::vector log_buffer; + ui->Print("Supported API: %d\n", kRecoveryApiVersion); + ui->Print("Finding update package...\n"); LOG(INFO) << "Update package id: " << package_id; if (!package) { diff --git a/otautil/include/otautil/logging.h b/otautil/include/otautil/logging.h index 60834978..4462eca6 100644 --- a/otautil/include/otautil/logging.h +++ b/otautil/include/otautil/logging.h @@ -53,7 +53,7 @@ void rotate_logs(const char* last_log_file, const char* last_kmsg_file); void check_and_fclose(FILE* fp, const std::string& name); void copy_log_file_to_pmsg(const std::string& source, const std::string& destination); -void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle); +void copy_logs(bool save_current_log); void reset_tmplog_offset(); void save_kernel_log(const char* destination); diff --git a/otautil/include/otautil/roots.h b/otautil/include/otautil/roots.h index 2ab3f454..92ee756f 100644 --- a/otautil/include/otautil/roots.h +++ b/otautil/include/otautil/roots.h @@ -53,3 +53,6 @@ int format_volume(const std::string& volume, const std::string& directory); // Ensure that all and only the volumes that packages expect to find // mounted (/tmp and /cache) are mounted. Returns 0 on success. int setup_install_mounts(); + +// Returns true if there is /cache in the volumes. +bool HasCache(); diff --git a/otautil/logging.cpp b/otautil/logging.cpp index 484f1150..3db0e8ac 100644 --- a/otautil/logging.cpp +++ b/otautil/logging.cpp @@ -178,9 +178,8 @@ void reset_tmplog_offset() { tmplog_offset = 0; } -static void copy_log_file(const std::string& source, const std::string& destination, bool append, - const selabel_handle* sehandle) { - FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", sehandle); +static void copy_log_file(const std::string& source, const std::string& destination, bool append) { + FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", logging_sehandle); if (dest_fp == nullptr) { PLOG(ERROR) << "Can't open " << destination; } else { @@ -203,7 +202,7 @@ static void copy_log_file(const std::string& source, const std::string& destinat } } -void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle) { +void copy_logs(bool save_current_log) { // We only rotate and record the log of the current session if explicitly requested. This usually // happens after wipes, installation from BCB or menu selections. This is to avoid unnecessary // rotation (and possible deletion) of log files, if it does not do anything loggable. @@ -216,7 +215,7 @@ void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* seha copy_log_file_to_pmsg(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE); // We can do nothing for now if there's no /cache partition. - if (!has_cache) { + if (!HasCache()) { return; } @@ -225,9 +224,9 @@ void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* seha rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE); // Copy logs to cache so the system can find out what happened. - copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true, sehandle); - copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false, sehandle); - copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false, sehandle); + copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true); + copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false); + copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false); save_kernel_log(LAST_KMSG_FILE); chmod(LOG_FILE, 0600); chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM); @@ -319,7 +318,7 @@ bool RestoreLogFilesAfterFormat(const std::vector& log_files) { // Reset the pointer so we copy from the beginning of the temp // log. reset_tmplog_offset(); - copy_logs(true /* save_current_log */, true /* has_cache */, logging_sehandle); + copy_logs(true /* save_current_log */); return true; } diff --git a/otautil/roots.cpp b/otautil/roots.cpp index a778e05f..43155178 100644 --- a/otautil/roots.cpp +++ b/otautil/roots.cpp @@ -51,6 +51,8 @@ using android::fs_mgr::ReadDefaultFstab; static Fstab fstab; +constexpr const char* CACHE_ROOT = "/cache"; + void load_volume_table() { if (!ReadDefaultFstab(&fstab)) { LOG(ERROR) << "Failed to read default fstab"; @@ -275,3 +277,9 @@ int setup_install_mounts() { } return 0; } + +bool HasCache() { + CHECK(!fstab.empty()); + static bool has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr; + return has_cache; +} diff --git a/recovery.cpp b/recovery.cpp index 97ca0a50..b989b246 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -69,10 +69,6 @@ static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale"; static constexpr const char* CACHE_ROOT = "/cache"; -// We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed -// into target_files.zip. Assert the version defined in code and in Android.mk are consistent. -static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions."); - static bool save_current_log = false; std::string stage; const char* reason = nullptr; @@ -106,7 +102,7 @@ const char* reason = nullptr; * -- after this, rebooting will restart the erase -- * 5. erase_volume() reformats /data * 6. erase_volume() reformats /cache - * 7. finish_recovery() erases BCB + * 7. FinishRecovery() erases BCB * -- after this, rebooting will restart the main system -- * 8. main() calls reboot() to boot main system * @@ -118,25 +114,25 @@ const char* reason = nullptr; * -- after this, rebooting will attempt to reinstall the update -- * 5. InstallPackage() attempts to install the update * NOTE: the package install must itself be restartable from any point - * 6. finish_recovery() erases BCB + * 6. FinishRecovery() erases BCB * -- after this, rebooting will (try to) restart the main system -- * 7. ** if install failed ** * 7a. PromptAndWait() shows an error icon and waits for the user * 7b. the user reboots (pulling the battery, etc) into the main system */ -bool is_ro_debuggable() { - return android::base::GetBoolProperty("ro.debuggable", false); +static bool IsRoDebuggable() { + return android::base::GetBoolProperty("ro.debuggable", false); } // Clear the recovery command and prepare to boot a (hopefully working) system, // copy our log file to cache as well (for the system to read). This function is // idempotent: call it as many times as you like. -static void finish_recovery() { +static void FinishRecovery(RecoveryUI* ui) { std::string locale = ui->GetLocale(); // Save the locale to cache, so if recovery is next started up without a '--locale' argument // (e.g., directly from the bootloader) it will use the last-known locale. - if (!locale.empty() && has_cache) { + if (!locale.empty() && HasCache()) { LOG(INFO) << "Saving locale \"" << locale << "\""; if (ensure_path_mounted(LOCALE_FILE) != 0) { LOG(ERROR) << "Failed to mount " << LOCALE_FILE; @@ -145,7 +141,7 @@ static void finish_recovery() { } } - copy_logs(save_current_log, has_cache, sehandle); + copy_logs(save_current_log); // Reset to normal system boot so recovery won't cycle indefinitely. std::string err; @@ -154,7 +150,7 @@ static void finish_recovery() { } // Remove the command file, so recovery won't repeat indefinitely. - if (has_cache) { + if (HasCache()) { if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) { LOG(WARNING) << "Can't unlink " << COMMAND_FILE; } @@ -168,7 +164,7 @@ static bool yes_no(Device* device, const char* question1, const char* question2) std::vector headers{ question1, question2 }; std::vector items{ " No", " Yes" }; - size_t chosen_item = ui->ShowMenu( + size_t chosen_item = device->GetUI()->ShowMenu( headers, items, 0, true, std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); return (chosen_item == 1); @@ -178,7 +174,7 @@ static bool ask_to_wipe_data(Device* device) { std::vector headers{ "Wipe all user data?", " THIS CAN NOT BE UNDONE!" }; std::vector items{ " Cancel", " Factory data reset" }; - size_t chosen_item = ui->ShowPromptWipeDataConfirmationMenu( + size_t chosen_item = device->GetUI()->ShowPromptWipeDataConfirmationMenu( headers, items, std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); @@ -200,7 +196,7 @@ static InstallResult prompt_and_wipe_data(Device* device) { }; // clang-format on for (;;) { - size_t chosen_item = ui->ShowPromptWipeDataMenu( + size_t chosen_item = device->GetUI()->ShowPromptWipeDataMenu( wipe_data_menu_headers, wipe_data_menu_items, std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); // If ShowMenu() returned RecoveryUI::KeyError::INTERRUPTED, WaitKey() was interrupted. @@ -224,7 +220,7 @@ static InstallResult prompt_and_wipe_data(Device* device) { static void choose_recovery_file(Device* device) { std::vector entries; - if (has_cache) { + if (HasCache()) { for (int i = 0; i < KEEP_LOG_COUNT; i++) { auto add_to_entries = [&](const char* filename) { std::string log_file(filename); @@ -258,7 +254,7 @@ static void choose_recovery_file(Device* device) { size_t chosen_item = 0; while (true) { - chosen_item = ui->ShowMenu( + chosen_item = device->GetUI()->ShowMenu( headers, entries, chosen_item, true, std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); @@ -268,11 +264,11 @@ static void choose_recovery_file(Device* device) { } if (entries[chosen_item] == "Back") break; - ui->ShowFile(entries[chosen_item]); + device->GetUI()->ShowFile(entries[chosen_item]); } } -static void run_graphics_test() { +static void run_graphics_test(RecoveryUI* ui) { // Switch to graphics screen. ui->ShowText(false); @@ -319,8 +315,9 @@ static void run_graphics_test() { // as REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default, which // is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery. static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) { + auto ui = device->GetUI(); for (;;) { - finish_recovery(); + FinishRecovery(ui); switch (status) { case INSTALL_SUCCESS: case INSTALL_NONE: @@ -421,7 +418,7 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); - copy_logs(save_current_log, has_cache, sehandle); + copy_logs(save_current_log); } else if (!ui->IsTextVisible()) { return Device::NO_ACTION; // reboot if logs aren't visible } @@ -433,7 +430,7 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) break; case Device::RUN_GRAPHICS_TEST: - run_graphics_test(); + run_graphics_test(ui); break; case Device::RUN_LOCALE_TEST: { @@ -680,6 +677,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorGetUI(); + // Set background string to "installing security update" for security update, // otherwise set it to "installing system update". ui->SetSystemUpdateText(security_update); @@ -706,8 +705,6 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Supported API: %d\n", kRecoveryApiVersion); - InstallResult status = INSTALL_SUCCESS; // next_action indicates the next target to reboot into upon finishing the install. It could be // overridden to a different reboot target per user request. @@ -768,7 +765,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); } } @@ -843,7 +840,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); } status = INSTALL_NONE; // No command specified @@ -876,7 +873,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector get_args(const int argc, char** const argv) { } // --- if that doesn't work, try the command file (if we have /cache). - if (args.size() == 1 && has_cache) { + if (args.size() == 1 && HasCache()) { std::string content; if (ensure_path_mounted(COMMAND_FILE) == 0 && android::base::ReadFileToString(COMMAND_FILE, &content)) { @@ -148,7 +147,7 @@ static std::vector get_args(const int argc, char** const argv) { // Write the arguments (excluding the filename in args[0]) back into the // bootloader control block. So the device will always boot into recovery to - // finish the pending work, until finish_recovery() is called. + // finish the pending work, until FinishRecovery() is called. std::vector options(args.cbegin() + 1, args.cend()); if (!update_bootloader_message(options, &err)) { LOG(ERROR) << "Failed to set BCB message: " << err; @@ -331,7 +330,6 @@ int main(int argc, char** argv) { redirect_stdio(Paths::Get().temporary_log_file().c_str()); load_volume_table(); - has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr; std::vector args = get_args(argc, argv); auto args_to_parse = StringVectorToNullTerminatedArray(args); @@ -370,7 +368,7 @@ int main(int argc, char** argv) { optind = 1; if (locale.empty()) { - if (has_cache) { + if (HasCache()) { locale = load_locale_from_cache(); } @@ -416,7 +414,7 @@ int main(int argc, char** argv) { } ui = device->GetUI(); - if (!has_cache) { + if (!HasCache()) { device->RemoveMenuItemForAction(Device::WIPE_CACHE); } @@ -424,7 +422,7 @@ int main(int argc, char** argv) { device->RemoveMenuItemForAction(Device::ENTER_FASTBOOT); } - if (!is_ro_debuggable()) { + if (!IsRoDebuggable()) { device->RemoveMenuItemForAction(Device::ENTER_RESCUE); } @@ -434,7 +432,7 @@ int main(int argc, char** argv) { LOG(INFO) << "Starting recovery (pid " << getpid() << ") on " << ctime(&start); LOG(INFO) << "locale is [" << locale << "]"; - sehandle = selinux_android_file_context_handle(); + auto sehandle = selinux_android_file_context_handle(); selinux_android_set_sehandle(sehandle); if (!sehandle) { ui->Print("Warning: No file_contexts\n"); @@ -447,7 +445,7 @@ int main(int argc, char** argv) { listener_thread.detach(); while (true) { - std::string usb_config = fastboot ? "fastboot" : is_ro_debuggable() ? "adb" : "none"; + std::string usb_config = fastboot ? "fastboot" : IsRoDebuggable() ? "adb" : "none"; std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); if (usb_config != usb_state) { if (!SetUsbConfig("none")) { From e5032219fe909864233e8225ee4fabd10c49ed5b Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 23 Jul 2019 13:23:29 -0700 Subject: [PATCH 075/171] Create a new function to return the help message for menu Then we can override this function in the device specific recovery ui; and allow customizing the help message. Bug: 137965958 Test: Check the menu on sailfish Change-Id: I09f23166f4205c5edf6c62eb42c8ada0fa710b26 --- recovery_ui/include/recovery_ui/screen_ui.h | 3 +++ recovery_ui/include/recovery_ui/ui.h | 2 +- recovery_ui/screen_ui.cpp | 24 ++++++++++++--------- recovery_ui/ui.cpp | 2 +- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/recovery_ui/include/recovery_ui/screen_ui.h b/recovery_ui/include/recovery_ui/screen_ui.h index 5cda2a2e..92b3c254 100644 --- a/recovery_ui/include/recovery_ui/screen_ui.h +++ b/recovery_ui/include/recovery_ui/screen_ui.h @@ -286,6 +286,9 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface { // selected. virtual int SelectMenu(int sel); + // Returns the help message displayed on top of the menu. + virtual std::vector GetMenuHelpMessage() const; + virtual void draw_background_locked(); virtual void draw_foreground_locked(); virtual void draw_screen_locked(); diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h index a95f935e..08ec1d76 100644 --- a/recovery_ui/include/recovery_ui/ui.h +++ b/recovery_ui/include/recovery_ui/ui.h @@ -118,7 +118,7 @@ class RecoveryUI { // Returns true if you have the volume up/down and power trio typical of phones and tablets, false // otherwise. - virtual bool HasThreeButtons(); + virtual bool HasThreeButtons() const; // Returns true if it has a power key. virtual bool HasPowerKey() const; diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp index f2af66c4..087fc0e8 100644 --- a/recovery_ui/screen_ui.cpp +++ b/recovery_ui/screen_ui.cpp @@ -673,6 +673,19 @@ void ScreenRecoveryUI::SetTitle(const std::vector& lines) { title_lines_ = lines; } +std::vector ScreenRecoveryUI::GetMenuHelpMessage() const { + // clang-format off + static std::vector REGULAR_HELP{ + "Use volume up/down and power.", + }; + static std::vector LONG_PRESS_HELP{ + "Any button cycles highlight.", + "Long-press activates.", + }; + // clang-format on + return HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP; +} + // Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex // locked. void ScreenRecoveryUI::draw_screen_locked() { @@ -685,16 +698,7 @@ void ScreenRecoveryUI::draw_screen_locked() { gr_color(0, 0, 0, 255); gr_clear(); - // clang-format off - static std::vector REGULAR_HELP{ - "Use volume up/down and power.", - }; - static std::vector LONG_PRESS_HELP{ - "Any button cycles highlight.", - "Long-press activates.", - }; - // clang-format on - draw_menu_and_text_buffer_locked(HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); + draw_menu_and_text_buffer_locked(GetMenuHelpMessage()); } // Draws the menu and text buffer on the screen. Should only be called with updateMutex locked. diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index 98c654dd..6f5cbbca 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -528,7 +528,7 @@ bool RecoveryUI::IsLongPress() { return result; } -bool RecoveryUI::HasThreeButtons() { +bool RecoveryUI::HasThreeButtons() const { return has_power_key && has_up_key && has_down_key; } From 60b242cfd58a234727f5d339e2f6fa4e91ffe1fa Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 30 Jul 2019 16:48:52 -0700 Subject: [PATCH 076/171] Simulator: add the argument to keep the updated images Add the command line option to select the work directory and save the updated image files. Because some people might have interested in getting updated images from an ota file. Also, fix a minor issue that the destination of package_extract_file needs to be updated if it's a block device. Otherwise, an unintended file may be extracted in the callers' directory. Test: run simulation, run unit tests Change-Id: Ic6a7db0580bc1748d6e080102e4654da4e41fd8c --- tests/unit/host/update_simulator_test.cpp | 4 ++-- updater/build_info.cpp | 17 +++++++++++++++-- updater/include/updater/build_info.h | 4 +++- updater/install.cpp | 9 ++++++++- updater/update_simulator_main.cpp | 13 ++++++++++++- 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/tests/unit/host/update_simulator_test.cpp b/tests/unit/host/update_simulator_test.cpp index bf89b785..fb121787 100644 --- a/tests/unit/host/update_simulator_test.cpp +++ b/tests/unit/host/update_simulator_test.cpp @@ -93,7 +93,7 @@ static void RunSimulation(std::string_view src_tf, std::string_view ota_package, // Run the update simulation and check the result. TemporaryDir work_dir; - BuildInfo build_info(work_dir.path); + BuildInfo build_info(work_dir.path, false); ASSERT_TRUE(build_info.ParseTargetFile(src_tf, false)); Updater updater(std::make_unique(&build_info)); ASSERT_TRUE(updater.Init(cmd_pipe.release(), ota_package, false)); @@ -211,7 +211,7 @@ TEST_F(UpdateSimulatorTest, BuildInfo_ParseTargetFile) { AddZipEntries(zip_file.release(), entries); TemporaryDir temp_dir; - BuildInfo build_info(temp_dir.path); + BuildInfo build_info(temp_dir.path, false); ASSERT_TRUE(build_info.ParseTargetFile(zip_file.path, false)); std::map expected_result = { diff --git a/updater/build_info.cpp b/updater/build_info.cpp index 3072aab5..f168008e 100644 --- a/updater/build_info.cpp +++ b/updater/build_info.cpp @@ -16,6 +16,8 @@ #include "updater/build_info.h" +#include + #include #include @@ -55,12 +57,23 @@ bool BuildInfo::ParseTargetFile(const std::string_view target_file_path, bool ex return false; } + std::string mapped_path = image_file.path; + // Rename the images to more readable ones if we want to keep the image. + if (keep_images_) { + mapped_path = work_dir_ + fstab_info.mount_point + ".img"; + image_file.release(); + if (rename(image_file.path, mapped_path.c_str()) != 0) { + PLOG(ERROR) << "Failed to rename " << image_file.path << " to " << mapped_path; + return false; + } + } + LOG(INFO) << "Mounted " << fstab_info.mount_point << "\nMapping: " << fstab_info.blockdev_name - << " to " << image_file.path; + << " to " << mapped_path; blockdev_map_.emplace( fstab_info.blockdev_name, - FakeBlockDevice(fstab_info.blockdev_name, fstab_info.mount_point, image_file.path)); + FakeBlockDevice(fstab_info.blockdev_name, fstab_info.mount_point, mapped_path)); break; } } diff --git a/updater/include/updater/build_info.h b/updater/include/updater/build_info.h index 22299579..0073bfa4 100644 --- a/updater/include/updater/build_info.h +++ b/updater/include/updater/build_info.h @@ -43,7 +43,8 @@ class FakeBlockDevice { // query the information and run the update on host. class BuildInfo { public: - explicit BuildInfo(const std::string_view work_dir) : work_dir_(work_dir) {} + BuildInfo(const std::string_view work_dir, bool keep_images) + : work_dir_(work_dir), keep_images_(keep_images) {} // Returns the value of the build properties. std::string GetProperty(const std::string_view key, const std::string_view default_value) const; // Returns the path to the mock block device. @@ -69,4 +70,5 @@ class BuildInfo { std::list temp_files_; std::string work_dir_; // A temporary directory to store the extracted image files + bool keep_images_; }; diff --git a/updater/install.cpp b/updater/install.cpp index c82351ec..be0ceb06 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -113,7 +113,7 @@ Value* PackageExtractFileFn(const char* name, State* state, argv.size()); } const std::string& zip_path = args[0]; - const std::string& dest_path = args[1]; + std::string dest_path = args[1]; ZipArchiveHandle za = state->updater->GetPackageHandle(); ZipEntry entry; @@ -122,6 +122,13 @@ Value* PackageExtractFileFn(const char* name, State* state, return StringValue(""); } + // Update the destination of package_extract_file if it's a block device. During simulation the + // destination will map to a fake file. + if (std::string block_device_name = state->updater->FindBlockDeviceName(dest_path); + !block_device_name.empty()) { + dest_path = block_device_name; + } + android::base::unique_fd fd(TEMP_FAILURE_RETRY( open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR))); if (fd == -1) { diff --git a/updater/update_simulator_main.cpp b/updater/update_simulator_main.cpp index 278a4405..6c6989ba 100644 --- a/updater/update_simulator_main.cpp +++ b/updater/update_simulator_main.cpp @@ -58,12 +58,16 @@ int main(int argc, char** argv) { std::string skip_function_file; std::string source_target_file; std::string package_name; + std::string work_dir; + bool keep_images = false; constexpr struct option OPTIONS[] = { + { "keep_images", no_argument, nullptr, 0 }, { "oem_settings", required_argument, nullptr, 0 }, { "ota_package", required_argument, nullptr, 0 }, { "skip_functions", required_argument, nullptr, 0 }, { "source", required_argument, nullptr, 0 }, + { "work_dir", required_argument, nullptr, 0 }, { nullptr, 0, nullptr, 0 }, }; @@ -86,6 +90,10 @@ int main(int argc, char** argv) { source_target_file = optarg; } else if (option_name == "ota_package"s) { package_name = optarg; + } else if (option_name == "keep_images"s) { + keep_images = true; + } else if (option_name == "work_dir"s) { + work_dir = optarg; } else { Usage(argv[0]); return EXIT_FAILURE; @@ -129,8 +137,11 @@ int main(int argc, char** argv) { TemporaryFile cmd_pipe; TemporaryDir source_temp_dir; + if (work_dir.empty()) { + work_dir = source_temp_dir.path; + } - BuildInfo source_build_info(source_temp_dir.path); + BuildInfo source_build_info(work_dir, keep_images); if (!source_build_info.ParseTargetFile(source_target_file, false)) { LOG(ERROR) << "Failed to parse the target file " << source_target_file; return EXIT_FAILURE; From e94b64ae864e5109d612ad31e78d4d9b2ac29435 Mon Sep 17 00:00:00 2001 From: Vic Yang Date: Tue, 6 Aug 2019 14:18:33 -0700 Subject: [PATCH 077/171] bootloader_message: Remove global std::string A global std::string, even if not used, pollutes the bss section unnecessarily. Since this object is only there for testing, make it std::optional, which is constexpr constructible. Bug: 138856262 Test: Along with a fix in fs_mgr, see that the bss section for libbootloader_message.so is now clean on cuttlefish for several processes. Change-Id: I6df837dded88d979ffe14d5b2770b120bcf87341 --- bootloader_message/bootloader_message.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index e684abba..b15a9b9f 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,7 @@ using android::fs_mgr::Fstab; using android::fs_mgr::ReadDefaultFstab; -static std::string g_misc_device_for_test; +static std::optional g_misc_device_for_test; // Exposed for test purpose. void SetMiscBlockDeviceForTest(std::string_view misc_device) { @@ -45,8 +46,8 @@ void SetMiscBlockDeviceForTest(std::string_view misc_device) { } static std::string get_misc_blk_device(std::string* err) { - if (!g_misc_device_for_test.empty()) { - return g_misc_device_for_test; + if (g_misc_device_for_test.has_value() && !g_misc_device_for_test->empty()) { + return *g_misc_device_for_test; } Fstab fstab; if (!ReadDefaultFstab(&fstab)) { From 3cbd7ae6877e6ba8fdba8f66577e68034b78511a Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 14 Aug 2019 12:31:58 -0700 Subject: [PATCH 078/171] updater: Fix build for new CreateLogicalPartition signature. Bug: 135752105 Test: mm libupdater_device Change-Id: I9dc6dc1e9112177ee5c0a96b8969dc3dc02d95f2 --- updater/updater_runtime_dynamic_partitions.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/updater/updater_runtime_dynamic_partitions.cpp b/updater/updater_runtime_dynamic_partitions.cpp index b084f659..be9250a8 100644 --- a/updater/updater_runtime_dynamic_partitions.cpp +++ b/updater/updater_runtime_dynamic_partitions.cpp @@ -35,6 +35,7 @@ using android::dm::DeviceMapper; using android::dm::DmDeviceState; using android::fs_mgr::CreateLogicalPartition; +using android::fs_mgr::CreateLogicalPartitionParams; using android::fs_mgr::DestroyLogicalPartition; using android::fs_mgr::LpMetadata; using android::fs_mgr::MetadataBuilder; @@ -64,8 +65,14 @@ bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_nam std::string* path) { auto state = DeviceMapper::Instance().GetState(partition_name); if (state == DmDeviceState::INVALID) { - return CreateLogicalPartition(GetSuperDevice(), 0 /* metadata slot */, partition_name, - true /* force writable */, kMapTimeout, path); + CreateLogicalPartitionParams params = { + .block_device = GetSuperDevice(), + .metadata_slot = 0, + .partition_name = partition_name, + .force_writable = true, + .timeout_ms = kMapTimeout, + }; + return CreateLogicalPartition(params, path); } if (state == DmDeviceState::ACTIVE) { From a7952ac14158f130e6381011ca7d308dc47c7660 Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Fri, 12 Jan 2018 13:42:18 +0800 Subject: [PATCH 079/171] minui: Support input device hotplug in recovery mode. In the old code, the recovery only enumerated the input devices at the startup, and read the input events from these devices. So if a USB input device is probed after the recovery startup, then the recovery can't read the events from this device. This patch use inotify to monitor /dev/input for new added input device, then support input device hotplug in recovery mode. Bug: 111847510 Test: can use USB keyboard hotplugged in recovery mode Change-Id: I7e7dcbd619d3c66a2f40a43418f5dac6a50c859e Signed-off-by: Liu Shuo A Signed-off-by: Ming Tan --- minui/events.cpp | 124 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 104 insertions(+), 20 deletions(-) diff --git a/minui/events.cpp b/minui/events.cpp index 7d0250e9..f331ed68 100644 --- a/minui/events.cpp +++ b/minui/events.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,8 @@ #include "minui/minui.h" +constexpr const char* INPUT_DEV_DIR = "/dev/input"; + constexpr size_t MAX_DEVICES = 16; constexpr size_t MAX_MISC_FDS = 16; @@ -46,6 +49,8 @@ struct FdInfo { ev_callback cb; }; +static bool g_allow_touch_inputs = true; +static ev_callback g_saved_input_cb; static android::base::unique_fd g_epoll_fd; static epoll_event g_polled_events[MAX_DEVICES + MAX_MISC_FDS]; static int g_polled_events_count; @@ -60,6 +65,78 @@ static bool test_bit(size_t bit, unsigned long* array) { // NOLINT return (array[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0; } +static bool should_add_input_device(int fd, bool allow_touch_inputs) { + // Use unsigned long to match ioctl's parameter type. + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT + + // Read the evbits of the input device. + if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + return false; + } + + // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also + // allowed if allow_touch_inputs is set. + if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) { + if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) { + return false; + } + } + + return true; +} + +static int inotify_cb(int fd, __unused uint32_t epevents) { + if (g_saved_input_cb == nullptr) return -1; + + // The inotify will put one or several complete events. + // Should not read part of one event. + size_t event_len; + int ret = ioctl(fd, FIONREAD, &event_len); + if (ret != 0) return -1; + + std::unique_ptr dir(opendir(INPUT_DEV_DIR), closedir); + if (!dir) { + return -1; + } + + std::vector buf(event_len); + + ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf.data(), event_len)); + if (r != event_len) { + return -1; + } + + size_t offset = 0; + while (offset < event_len) { + struct inotify_event* pevent = reinterpret_cast(buf.data() + offset); + if (offset + sizeof(inotify_event) + pevent->len > event_len) { + // The pevent->len is too large and buffer will over flow. + // In general, should not happen, just make more stable. + return -1; + } + offset += sizeof(inotify_event) + pevent->len; + + pevent->name[pevent->len] = '\0'; + if (strncmp(pevent->name, "event", 5)) { + continue; + } + + android::base::unique_fd dfd(openat(dirfd(dir.get()), pevent->name, O_RDONLY)); + if (dfd == -1) { + break; + } + + if (!should_add_input_device(dfd, g_allow_touch_inputs)) { + continue; + } + + // Only add, we assume the user will not plug out and plug in USB device again and again :) + ev_add_fd(std::move(dfd), g_saved_input_cb); + } + + return 0; +} + int ev_init(ev_callback input_cb, bool allow_touch_inputs) { g_epoll_fd.reset(); @@ -68,7 +145,16 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) { return -1; } - std::unique_ptr dir(opendir("/dev/input"), closedir); + android::base::unique_fd inotify_fd(inotify_init1(IN_CLOEXEC)); + if (inotify_fd.get() == -1) { + return -1; + } + + if (inotify_add_watch(inotify_fd, INPUT_DEV_DIR, IN_CREATE) < 0) { + return -1; + } + + std::unique_ptr dir(opendir(INPUT_DEV_DIR), closedir); if (!dir) { return -1; } @@ -80,22 +166,10 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) { android::base::unique_fd fd(openat(dirfd(dir.get()), de->d_name, O_RDONLY | O_CLOEXEC)); if (fd == -1) continue; - // Use unsigned long to match ioctl's parameter type. - unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT - - // Read the evbits of the input device. - if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + if (!should_add_input_device(fd, allow_touch_inputs)) { continue; } - // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also - // allowed if allow_touch_inputs is set. - if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) { - if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) { - continue; - } - } - epoll_event ev; ev.events = EPOLLIN | EPOLLWAKEUP; ev.data.ptr = &ev_fdinfo[g_ev_count]; @@ -116,6 +190,11 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) { } g_epoll_fd.reset(epoll_fd.release()); + + g_saved_input_cb = input_cb; + g_allow_touch_inputs = allow_touch_inputs; + ev_add_fd(std::move(inotify_fd), inotify_cb); + return 0; } @@ -148,6 +227,7 @@ void ev_exit(void) { } g_ev_misc_count = 0; g_ev_dev_count = 0; + g_saved_input_cb = nullptr; g_epoll_fd.reset(); } @@ -170,13 +250,17 @@ void ev_dispatch(void) { } int ev_get_input(int fd, uint32_t epevents, input_event* ev) { - if (epevents & EPOLLIN) { - ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev))); - if (r == sizeof(*ev)) { - return 0; - } + if (epevents & EPOLLIN) { + ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev))); + if (r == sizeof(*ev)) { + return 0; } - return -1; + } + if (epevents & EPOLLHUP) { + // Delete this watch + epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, fd, nullptr); + } + return -1; } int ev_sync_key_state(const ev_set_key_callback& set_key_cb) { From b63a2215b5e3fc9c7254aa783c7857ede79e6f4e Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 29 Jul 2019 14:21:49 -0700 Subject: [PATCH 080/171] Remove common.h Additionally kill the global variable: reason, stage; move them to a separate BootState class instead. Vendor specific recovery code will need to call getters from Device() class to access these variables. Bug: 137705917 Test: unit tests pass, boot sailfish into recovery, code search and no code includes common.h in vendor specific recovery. Change-Id: Ia50a5ea951212c25548562f29cc9cf78505b5e34 --- .../include/otautil/boot_state.h | 21 ++++++++++++++----- recovery.cpp | 21 +++++++++---------- recovery_main.cpp | 19 +++++++++++++---- recovery_ui/device.cpp | 13 ++++++++++++ recovery_ui/include/recovery_ui/device.h | 10 +++++++++ 5 files changed, 64 insertions(+), 20 deletions(-) rename common.h => otautil/include/otautil/boot_state.h (57%) diff --git a/common.h b/otautil/include/otautil/boot_state.h similarity index 57% rename from common.h rename to otautil/include/otautil/boot_state.h index 128a69d9..6c877bae 100644 --- a/common.h +++ b/otautil/include/otautil/boot_state.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 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. @@ -17,9 +17,20 @@ #pragma once #include +#include -// The current stage, e.g. "1/2". -extern std::string stage; +class BootState { + public: + BootState(std::string_view reason, std::string_view stage) : reason_(reason), stage_(stage) {} -// The reason argument provided in "--reason=". -extern const char* reason; + std::string reason() const { + return reason_; + } + std::string stage() const { + return stage_; + } + + private: + std::string reason_; // The reason argument provided in "--reason=". + std::string stage_; // The current stage, e.g. "1/2". +}; diff --git a/recovery.cpp b/recovery.cpp index b989b246..4862dfcc 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -46,7 +46,6 @@ #include #include "bootloader_message/bootloader_message.h" -#include "common.h" #include "fsck_unshare_blocks.h" #include "install/adb_install.h" #include "install/fuse_install.h" @@ -54,6 +53,7 @@ #include "install/package.h" #include "install/wipe_data.h" #include "install/wipe_device.h" +#include "otautil/boot_state.h" #include "otautil/error_code.h" #include "otautil/logging.h" #include "otautil/paths.h" @@ -70,8 +70,6 @@ static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale"; static constexpr const char* CACHE_ROOT = "/cache"; static bool save_current_log = false; -std::string stage; -const char* reason = nullptr; /* * The recovery tool communicates with the main system through /cache files. @@ -208,7 +206,8 @@ static InstallResult prompt_and_wipe_data(Device* device) { } if (ask_to_wipe_data(device)) { - bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0; + CHECK(device->GetReason().has_value()); + bool convert_fbe = device->GetReason().value() == "convert_fbe"; if (WipeData(device, convert_fbe)) { return INSTALL_SUCCESS; } else { @@ -635,12 +634,10 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorGetStage().value_or("").c_str()); + printf("reason is [%s]\n", device->GetReason().value_or("").c_str()); auto ui = device->GetUI(); @@ -684,7 +681,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorSetSystemUpdateText(security_update); int st_cur, st_max; - if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) { + if (!device->GetStage().has_value() && + sscanf(device->GetStage().value().c_str(), "%d/%d", &st_cur, &st_max) == 2) { ui->SetStage(st_cur, st_max); } @@ -790,7 +788,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorGetReason().has_value()); + bool convert_fbe = device->GetReason().value() == "convert_fbe"; if (!WipeData(device, convert_fbe)) { status = INSTALL_ERROR; } diff --git a/recovery_main.cpp b/recovery_main.cpp index f0d75ee1..28197bf4 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -48,9 +48,9 @@ #include #include -#include "common.h" #include "fastboot/fastboot.h" #include "install/wipe_data.h" +#include "otautil/boot_state.h" #include "otautil/logging.h" #include "otautil/paths.h" #include "otautil/roots.h" @@ -80,11 +80,12 @@ static void UiLogger(android::base::LogId /* id */, android::base::LogSeverity s } } +// Parses the command line argument from various sources; and reads the stage field from BCB. // command line args come from, in decreasing precedence: // - the actual command line // - the bootloader control block (one per line, after "recovery") // - the contents of COMMAND_FILE (one per line) -static std::vector get_args(const int argc, char** const argv) { +static std::vector get_args(const int argc, char** const argv, std::string* stage) { CHECK_GT(argc, 0); bootloader_message boot = {}; @@ -94,7 +95,9 @@ static std::vector get_args(const int argc, char** const argv) { // If fails, leave a zeroed bootloader_message. boot = {}; } - stage = std::string(boot.stage); + if (stage) { + *stage = std::string(boot.stage); + } std::string boot_command; if (boot.command[0] != 0) { @@ -331,12 +334,14 @@ int main(int argc, char** argv) { load_volume_table(); - std::vector args = get_args(argc, argv); + std::string stage; + std::vector args = get_args(argc, argv, &stage); auto args_to_parse = StringVectorToNullTerminatedArray(args); static constexpr struct option OPTIONS[] = { { "fastboot", no_argument, nullptr, 0 }, { "locale", required_argument, nullptr, 0 }, + { "reason", required_argument, nullptr, 0 }, { "show_text", no_argument, nullptr, 't' }, { nullptr, 0, nullptr, 0 }, }; @@ -344,6 +349,7 @@ int main(int argc, char** argv) { bool show_text = false; bool fastboot = false; std::string locale; + std::string reason; int arg; int option_index; @@ -357,6 +363,8 @@ int main(int argc, char** argv) { std::string option = OPTIONS[option_index].name; if (option == "locale") { locale = optarg; + } else if (option == "reason") { + reason = optarg; } else if (option == "fastboot" && android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) { fastboot = true; @@ -412,6 +420,9 @@ int main(int argc, char** argv) { device->ResetUI(new StubRecoveryUI()); } } + + BootState boot_state(reason, stage); // recovery_main owns the state of boot. + device->SetBootState(&boot_state); ui = device->GetUI(); if (!HasCache()) { diff --git a/recovery_ui/device.cpp b/recovery_ui/device.cpp index e7ae1a3e..d46df92d 100644 --- a/recovery_ui/device.cpp +++ b/recovery_ui/device.cpp @@ -23,6 +23,7 @@ #include +#include "otautil/boot_state.h" #include "recovery_ui/ui.h" static std::vector> g_menu_actions{ @@ -95,3 +96,15 @@ int Device::HandleMenuKey(int key, bool visible) { return ui_->HasThreeButtons() ? kNoAction : kHighlightDown; } } + +void Device::SetBootState(const BootState* state) { + boot_state_ = state; +} + +std::optional Device::GetReason() const { + return boot_state_ ? std::make_optional(boot_state_->reason()) : std::nullopt; +} + +std::optional Device::GetStage() const { + return boot_state_ ? std::make_optional(boot_state_->stage()) : std::nullopt; +} diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 9a4edf26..f4f99363 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -20,12 +20,15 @@ #include #include +#include #include #include // Forward declaration to avoid including "ui.h". class RecoveryUI; +class BootState; + class Device { public: static constexpr const int kNoAction = -1; @@ -126,9 +129,16 @@ class Device { return true; } + void SetBootState(const BootState* state); + // The getters for reason and stage may return std::nullopt until StartRecovery() is called. It's + // the caller's responsibility to perform the check and handle the exception. + std::optional GetReason() const; + std::optional GetStage() const; + private: // The RecoveryUI object that should be used to display the user interface for this device. std::unique_ptr ui_; + const BootState* boot_state_{ nullptr }; }; // Disable name mangling, as this function will be loaded via dlsym(3). From 8108e2513f8342444821c179d713982fcf931e98 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 28 Aug 2019 15:24:07 -0700 Subject: [PATCH 081/171] Refactor boot_control into a separate library. This extracts the implementation of boot_control into a new library, libboot_control. The bootctrl.default module now wraps this library. This refactoring will allow us to re-use the same implementation in HIDL. Bug: 138861550 Test: mm bootctrl.default Change-Id: Ic0558da3c2d8e6f5ebec63a497825c24b51623b7 --- boot_control/Android.bp | 32 ++- .../include/libboot_control/libboot_control.h | 58 ++++++ boot_control/legacy_boot_control.cpp | 115 +++++++++++ .../{boot_control.cpp => libboot_control.cpp} | 185 ++++++------------ 4 files changed, 256 insertions(+), 134 deletions(-) create mode 100644 boot_control/include/libboot_control/libboot_control.h create mode 100644 boot_control/legacy_boot_control.cpp rename boot_control/{boot_control.cpp => libboot_control.cpp} (60%) diff --git a/boot_control/Android.bp b/boot_control/Android.bp index 7720ead5..0ebce85e 100644 --- a/boot_control/Android.bp +++ b/boot_control/Android.bp @@ -14,13 +14,11 @@ // limitations under the License. // -cc_library_shared { - name: "bootctrl.default", +cc_defaults { + name: "libboot_control_defaults", recovery_available: true, relative_install_path: "hw", - srcs: ["boot_control.cpp"], - cflags: [ "-D_FILE_OFFSET_BITS=64", "-Werror", @@ -31,7 +29,31 @@ cc_library_shared { shared_libs: [ "libbase", "libbootloader_message", - "libfs_mgr", "liblog", ], + static_libs: [ + "libfstab", + ], +} + +cc_library_static { + name: "libboot_control", + defaults: ["libboot_control_defaults"], + export_include_dirs: ["include"], + + srcs: ["libboot_control.cpp"], +} + +cc_library_shared { + name: "bootctrl.default", + defaults: ["libboot_control_defaults"], + + srcs: ["legacy_boot_control.cpp"], + + static_libs: [ + "libboot_control", + ], + shared_libs: [ + "libhardware", + ], } diff --git a/boot_control/include/libboot_control/libboot_control.h b/boot_control/include/libboot_control/libboot_control.h new file mode 100644 index 00000000..6582d024 --- /dev/null +++ b/boot_control/include/libboot_control/libboot_control.h @@ -0,0 +1,58 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#pragma once + +#include + +namespace android { +namespace bootable { + +// Helper library to implement the IBootControl HAL using the misc partition. +class BootControl { + public: + bool Init(); + unsigned int GetNumberSlots(); + unsigned int GetCurrentSlot(); + bool MarkBootSuccessful(); + bool SetActiveBootSlot(unsigned int slot); + bool SetSlotAsUnbootable(unsigned int slot); + bool SetSlotBootable(unsigned int slot); + bool IsSlotBootable(unsigned int slot); + const char* GetSuffix(unsigned int slot); + bool IsSlotMarkedSuccessful(unsigned int slot); + + const std::string& misc_device() const { + return misc_device_; + } + + private: + // Whether this object was initialized with data from the bootloader message + // that doesn't change until next reboot. + bool initialized_ = false; + + // The path to the misc_device as reported in the fstab. + std::string misc_device_; + + // The number of slots present on the device. + unsigned int num_slots_ = 0; + + // The slot where we are running from. + unsigned int current_slot_ = 0; +}; + +} // namespace bootable +} // namespace android diff --git a/boot_control/legacy_boot_control.cpp b/boot_control/legacy_boot_control.cpp new file mode 100644 index 00000000..73d3a584 --- /dev/null +++ b/boot_control/legacy_boot_control.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +using android::bootable::BootControl; + +struct boot_control_private_t { + // The base struct needs to be first in the list. + boot_control_module_t base; + + BootControl impl; +}; + +namespace { + +void BootControl_init(boot_control_module_t* module) { + auto& impl = reinterpret_cast(module)->impl; + impl.Init(); +} + +unsigned int BootControl_getNumberSlots(boot_control_module_t* module) { + auto& impl = reinterpret_cast(module)->impl; + return impl.GetNumberSlots(); +} + +unsigned int BootControl_getCurrentSlot(boot_control_module_t* module) { + auto& impl = reinterpret_cast(module)->impl; + return impl.GetCurrentSlot(); +} + +int BootControl_markBootSuccessful(boot_control_module_t* module) { + auto& impl = reinterpret_cast(module)->impl; + return impl.MarkBootSuccessful() ? 0 : -1; +} + +int BootControl_setActiveBootSlot(boot_control_module_t* module, unsigned int slot) { + auto& impl = reinterpret_cast(module)->impl; + return impl.SetActiveBootSlot(slot) ? 0 : -1; +} + +int BootControl_setSlotAsUnbootable(struct boot_control_module* module, unsigned int slot) { + auto& impl = reinterpret_cast(module)->impl; + return impl.SetSlotAsUnbootable(slot) ? 0 : -1; +} + +int BootControl_isSlotBootable(struct boot_control_module* module, unsigned int slot) { + auto& impl = reinterpret_cast(module)->impl; + return impl.IsSlotBootable(slot) ? 0 : -1; +} + +int BootControl_isSlotMarkedSuccessful(struct boot_control_module* module, unsigned int slot) { + auto& impl = reinterpret_cast(module)->impl; + return impl.IsSlotMarkedSuccessful(slot) ? 0 : -1; +} + +const char* BootControl_getSuffix(boot_control_module_t* module, unsigned int slot) { + auto& impl = reinterpret_cast(module)->impl; + return impl.GetSuffix(slot); +} + +static int BootControl_open(const hw_module_t* module __unused, const char* id __unused, + hw_device_t** device __unused) { + /* Nothing to do currently. */ + return 0; +} + +struct hw_module_methods_t BootControl_methods = { + .open = BootControl_open, +}; + +} // namespace + +boot_control_private_t HAL_MODULE_INFO_SYM = { + .base = + { + .common = + { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = BOOT_CONTROL_HARDWARE_MODULE_ID, + .name = "AOSP reference bootctrl HAL", + .author = "The Android Open Source Project", + .methods = &BootControl_methods, + }, + .init = BootControl_init, + .getNumberSlots = BootControl_getNumberSlots, + .getCurrentSlot = BootControl_getCurrentSlot, + .markBootSuccessful = BootControl_markBootSuccessful, + .setActiveBootSlot = BootControl_setActiveBootSlot, + .setSlotAsUnbootable = BootControl_setSlotAsUnbootable, + .isSlotBootable = BootControl_isSlotBootable, + .getSuffix = BootControl_getSuffix, + .isSlotMarkedSuccessful = BootControl_isSlotMarkedSuccessful, + }, +}; diff --git a/boot_control/boot_control.cpp b/boot_control/libboot_control.cpp similarity index 60% rename from boot_control/boot_control.cpp rename to boot_control/libboot_control.cpp index ec97b6ce..8b44d5d1 100644 --- a/boot_control/boot_control.cpp +++ b/boot_control/libboot_control.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include #include #include @@ -26,30 +28,11 @@ #include #include #include -#include -#include #include -struct boot_control_private_t { - // The base struct needs to be first in the list. - boot_control_module_t base; - - // Whether this struct was initialized with data from the bootloader message - // that doesn't change until next reboot. - bool initialized; - - // The path to the misc_device as reported in the fstab. - const char* misc_device; - - // The number of slots present on the device. - unsigned int num_slots; - - // The slot where we are running from. - unsigned int current_slot; -}; - -namespace { +namespace android { +namespace bootable { // The number of boot attempts that should be made from a new slot before // rolling back to the previous slot. @@ -91,8 +74,8 @@ uint32_t BootloaderControlLECRC(const bootloader_control* boot_ctrl) { CRC32(reinterpret_cast(boot_ctrl), offsetof(bootloader_control, crc32_le))); } -bool LoadBootloaderControl(const char* misc_device, bootloader_control* buffer) { - android::base::unique_fd fd(open(misc_device, O_RDONLY)); +bool LoadBootloaderControl(const std::string& misc_device, bootloader_control* buffer) { + android::base::unique_fd fd(open(misc_device.c_str(), O_RDONLY)); if (fd.get() == -1) { PLOG(ERROR) << "failed to open " << misc_device; return false; @@ -108,9 +91,9 @@ bool LoadBootloaderControl(const char* misc_device, bootloader_control* buffer) return true; } -bool UpdateAndSaveBootloaderControl(const char* misc_device, bootloader_control* buffer) { +bool UpdateAndSaveBootloaderControl(const std::string& misc_device, bootloader_control* buffer) { buffer->crc32_le = BootloaderControlLECRC(buffer); - android::base::unique_fd fd(open(misc_device, O_WRONLY | O_SYNC)); + android::base::unique_fd fd(open(misc_device.c_str(), O_WRONLY | O_SYNC)); if (fd.get() == -1) { PLOG(ERROR) << "failed to open " << misc_device; return false; @@ -126,13 +109,12 @@ bool UpdateAndSaveBootloaderControl(const char* misc_device, bootloader_control* return true; } -void InitDefaultBootloaderControl(const boot_control_private_t* module, - bootloader_control* boot_ctrl) { +void InitDefaultBootloaderControl(BootControl* control, bootloader_control* boot_ctrl) { memset(boot_ctrl, 0, sizeof(*boot_ctrl)); - if (module->current_slot < kMaxNumSlots) { - strlcpy(boot_ctrl->slot_suffix, kSlotSuffixes[module->current_slot], - sizeof(boot_ctrl->slot_suffix)); + unsigned int current_slot = control->GetCurrentSlot(); + if (current_slot < kMaxNumSlots) { + strlcpy(boot_ctrl->slot_suffix, kSlotSuffixes[current_slot], sizeof(boot_ctrl->slot_suffix)); } boot_ctrl->magic = BOOT_CTRL_MAGIC; boot_ctrl->version = BOOT_CTRL_VERSION; @@ -140,7 +122,7 @@ void InitDefaultBootloaderControl(const boot_control_private_t* module, // Figure out the number of slots by checking if the partitions exist, // otherwise assume the maximum supported by the header. boot_ctrl->nb_slot = kMaxNumSlots; - std::string base_path = module->misc_device; + std::string base_path = control->misc_device(); size_t last_path_sep = base_path.rfind('/'); if (last_path_sep != std::string::npos) { // We test the existence of the "boot" partition on each possible slot, @@ -185,7 +167,7 @@ void InitDefaultBootloaderControl(const boot_control_private_t* module, // current slot is successful. The bootloader should repair this situation // before booting and write a valid boot_control slot, so if we reach this // stage it means that the misc partition was corrupted since boot. - if (module->current_slot == slot) { + if (current_slot == slot) { entry.successful_boot = 1; } @@ -207,14 +189,14 @@ int SlotSuffixToIndex(const char* suffix) { // Initialize the boot_control_private struct with the information from // the bootloader_message buffer stored in |boot_ctrl|. Returns whether the // initialization succeeded. -bool BootControl_lazyInitialization(boot_control_private_t* module) { - if (module->initialized) return true; +bool BootControl::Init() { + if (initialized_) return true; // Initialize the current_slot from the read-only property. If the property // was not set (from either the command line or the device tree), we can later // initialize it from the bootloader_control struct. std::string suffix_prop = android::base::GetProperty("ro.boot.slot_suffix", ""); - module->current_slot = SlotSuffixToIndex(suffix_prop.c_str()); + current_slot_ = SlotSuffixToIndex(suffix_prop.c_str()); std::string err; std::string device = get_bootloader_message_blk_device(&err); @@ -224,8 +206,8 @@ bool BootControl_lazyInitialization(boot_control_private_t* module) { if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) return false; // Note that since there isn't a module unload function this memory is leaked. - module->misc_device = strdup(device.c_str()); - module->initialized = true; + misc_device_ = strdup(device.c_str()); + initialized_ = true; // Validate the loaded data, otherwise we will destroy it and re-initialize it // with the current information. @@ -233,56 +215,47 @@ bool BootControl_lazyInitialization(boot_control_private_t* module) { if (boot_ctrl.crc32_le != computed_crc32) { LOG(WARNING) << "Invalid boot control found, expected CRC-32 0x" << std::hex << computed_crc32 << " but found 0x" << std::hex << boot_ctrl.crc32_le << ". Re-initializing."; - InitDefaultBootloaderControl(module, &boot_ctrl); + InitDefaultBootloaderControl(this, &boot_ctrl); UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl); } - module->num_slots = boot_ctrl.nb_slot; + num_slots_ = boot_ctrl.nb_slot; return true; } -void BootControl_init(boot_control_module_t* module) { - BootControl_lazyInitialization(reinterpret_cast(module)); +unsigned int BootControl::GetNumberSlots() { + return num_slots_; } -unsigned int BootControl_getNumberSlots(boot_control_module_t* module) { - return reinterpret_cast(module)->num_slots; +unsigned int BootControl::GetCurrentSlot() { + return current_slot_; } -unsigned int BootControl_getCurrentSlot(boot_control_module_t* module) { - return reinterpret_cast(module)->current_slot; -} - -int BootControl_markBootSuccessful(boot_control_module_t* module) { - boot_control_private_t* const bootctrl_module = reinterpret_cast(module); - +bool BootControl::MarkBootSuccessful() { bootloader_control bootctrl; - if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; - bootctrl.slot_info[bootctrl_module->current_slot].successful_boot = 1; + bootctrl.slot_info[current_slot_].successful_boot = 1; // tries_remaining == 0 means that the slot is not bootable anymore, make // sure we mark the current slot as bootable if it succeeds in the last // attempt. - bootctrl.slot_info[bootctrl_module->current_slot].tries_remaining = 1; - if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; - return 0; + bootctrl.slot_info[current_slot_].tries_remaining = 1; + return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl); } -int BootControl_setActiveBootSlot(boot_control_module_t* module, unsigned int slot) { - boot_control_private_t* const bootctrl_module = reinterpret_cast(module); - - if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) { +bool BootControl::SetActiveBootSlot(unsigned int slot) { + if (slot >= kMaxNumSlots || slot >= num_slots_) { // Invalid slot number. - return -1; + return false; } bootloader_control bootctrl; - if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; // Set every other slot with a lower priority than the new "active" slot. const unsigned int kActivePriority = 15; const unsigned int kActiveTries = 6; - for (unsigned int i = 0; i < bootctrl_module->num_slots; ++i) { + for (unsigned int i = 0; i < num_slots_; ++i) { if (i != slot) { if (bootctrl.slot_info[i].priority >= kActivePriority) bootctrl.slot_info[i].priority = kActivePriority - 1; @@ -299,103 +272,57 @@ int BootControl_setActiveBootSlot(boot_control_module_t* module, unsigned int sl // used to cancel the pending update. We should only reset the verity_corrpted // bit when attempting a new slot, otherwise the verity bit on the current // slot would be flip. - if (slot != bootctrl_module->current_slot) bootctrl.slot_info[slot].verity_corrupted = 0; + if (slot != current_slot_) bootctrl.slot_info[slot].verity_corrupted = 0; - if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; - return 0; + return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl); } -int BootControl_setSlotAsUnbootable(struct boot_control_module* module, unsigned int slot) { - boot_control_private_t* const bootctrl_module = reinterpret_cast(module); - - if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) { +bool BootControl::SetSlotAsUnbootable(unsigned int slot) { + if (slot >= kMaxNumSlots || slot >= num_slots_) { // Invalid slot number. - return -1; + return false; } bootloader_control bootctrl; - if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; // The only way to mark a slot as unbootable, regardless of the priority is to // set the tries_remaining to 0. bootctrl.slot_info[slot].successful_boot = 0; bootctrl.slot_info[slot].tries_remaining = 0; - if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; - return 0; + return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl); } -int BootControl_isSlotBootable(struct boot_control_module* module, unsigned int slot) { - boot_control_private_t* const bootctrl_module = reinterpret_cast(module); - - if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) { +bool BootControl::IsSlotBootable(unsigned int slot) { + if (slot >= kMaxNumSlots || slot >= num_slots_) { // Invalid slot number. - return -1; + return false; } bootloader_control bootctrl; - if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; - return bootctrl.slot_info[slot].tries_remaining; + return bootctrl.slot_info[slot].tries_remaining != 0; } -int BootControl_isSlotMarkedSuccessful(struct boot_control_module* module, unsigned int slot) { - boot_control_private_t* const bootctrl_module = reinterpret_cast(module); - - if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) { +bool BootControl::IsSlotMarkedSuccessful(unsigned int slot) { + if (slot >= kMaxNumSlots || slot >= num_slots_) { // Invalid slot number. - return -1; + return false; } bootloader_control bootctrl; - if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; return bootctrl.slot_info[slot].successful_boot && bootctrl.slot_info[slot].tries_remaining; } -const char* BootControl_getSuffix(boot_control_module_t* module, unsigned int slot) { - if (slot >= kMaxNumSlots || slot >= reinterpret_cast(module)->num_slots) { - return NULL; +const char* BootControl::GetSuffix(unsigned int slot) { + if (slot >= kMaxNumSlots || slot >= num_slots_) { + return nullptr; } return kSlotSuffixes[slot]; } -static int BootControl_open(const hw_module_t* module __unused, const char* id __unused, - hw_device_t** device __unused) { - /* Nothing to do currently. */ - return 0; -} - -struct hw_module_methods_t BootControl_methods = { - .open = BootControl_open, -}; - -} // namespace - -boot_control_private_t HAL_MODULE_INFO_SYM = { - .base = - { - .common = - { - .tag = HARDWARE_MODULE_TAG, - .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1, - .hal_api_version = HARDWARE_HAL_API_VERSION, - .id = BOOT_CONTROL_HARDWARE_MODULE_ID, - .name = "AOSP reference bootctrl HAL", - .author = "The Android Open Source Project", - .methods = &BootControl_methods, - }, - .init = BootControl_init, - .getNumberSlots = BootControl_getNumberSlots, - .getCurrentSlot = BootControl_getCurrentSlot, - .markBootSuccessful = BootControl_markBootSuccessful, - .setActiveBootSlot = BootControl_setActiveBootSlot, - .setSlotAsUnbootable = BootControl_setSlotAsUnbootable, - .isSlotBootable = BootControl_isSlotBootable, - .getSuffix = BootControl_getSuffix, - .isSlotMarkedSuccessful = BootControl_isSlotMarkedSuccessful, - }, - .initialized = false, - .misc_device = nullptr, - .num_slots = 0, - .current_slot = 0, -}; +} // namespace bootable +} // namespace android From f771484f4e2cf90c205322bcd3c260523b2c196f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 29 Aug 2019 13:47:45 -0700 Subject: [PATCH 082/171] Make libboot_control vendor available. Bug: 138861550 Test: builds Change-Id: Ib745b59931c6258073e47b00def84c9157df90a7 --- boot_control/Android.bp | 3 ++- bootloader_message/Android.bp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/boot_control/Android.bp b/boot_control/Android.bp index 0ebce85e..f6a6cedd 100644 --- a/boot_control/Android.bp +++ b/boot_control/Android.bp @@ -16,6 +16,7 @@ cc_defaults { name: "libboot_control_defaults", + vendor: true, recovery_available: true, relative_install_path: "hw", @@ -28,10 +29,10 @@ cc_defaults { shared_libs: [ "libbase", - "libbootloader_message", "liblog", ], static_libs: [ + "libbootloader_message_vendor", "libfstab", ], } diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp index 8d72a116..6443a077 100644 --- a/bootloader_message/Android.bp +++ b/bootloader_message/Android.bp @@ -56,4 +56,5 @@ cc_library_static { "libbootloader_message_defaults", ], vendor: true, + recovery_available: true, } From 643ddd99dfa3d90ebcb1f8396a2375ba7e80633c Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 30 Aug 2019 12:35:02 -0700 Subject: [PATCH 083/171] boot_control: Don't allow current_slot_ to be -1. If the HAL is run on a non-A/B device, it does not fail gracefully; it will segfault because the current_slot is -1 and methods do not protect against this. Instead, have Init() fail if we can't determine the current slot. Bug: 130078382 Test: vts tests Change-Id: I2d3094518a842b1a764427d2da9d46781a9f26da --- boot_control/libboot_control.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/boot_control/libboot_control.cpp b/boot_control/libboot_control.cpp index 8b44d5d1..fa7dc47e 100644 --- a/boot_control/libboot_control.cpp +++ b/boot_control/libboot_control.cpp @@ -196,6 +196,10 @@ bool BootControl::Init() { // was not set (from either the command line or the device tree), we can later // initialize it from the bootloader_control struct. std::string suffix_prop = android::base::GetProperty("ro.boot.slot_suffix", ""); + if (suffix_prop.empty()) { + LOG(ERROR) << "Slot suffix property is not set"; + return false; + } current_slot_ = SlotSuffixToIndex(suffix_prop.c_str()); std::string err; From b18f153b12eedad79a609be96cce69d5b137f844 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 4 Sep 2019 18:10:16 -0700 Subject: [PATCH 084/171] Improve error messaging when bootcontrol HAL fails to load. Bug: N/A Test: manual test Change-Id: I6bf849dba4ae66353c44875ece30917283557327 --- boot_control/libboot_control.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/boot_control/libboot_control.cpp b/boot_control/libboot_control.cpp index fa7dc47e..89cf8786 100644 --- a/boot_control/libboot_control.cpp +++ b/boot_control/libboot_control.cpp @@ -204,10 +204,16 @@ bool BootControl::Init() { std::string err; std::string device = get_bootloader_message_blk_device(&err); - if (device.empty()) return false; + if (device.empty()) { + LOG(ERROR) << "Could not find bootloader message block device: " << err; + return false; + } bootloader_control boot_ctrl; - if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) return false; + if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) { + LOG(ERROR) << "Failed to load bootloader control block"; + return false; + } // Note that since there isn't a module unload function this memory is leaked. misc_device_ = strdup(device.c_str()); From 46f38e461042d5f84baee32250fd15e56301a134 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 29 Aug 2019 15:09:22 -0700 Subject: [PATCH 085/171] Add IBootControl 1.1 support to libboot_control. Bug: 138861550 Test: vts Change-Id: Id4a2963d6ab657d8ab076b7013492a691462b287 --- boot_control/Android.bp | 1 + .../include/libboot_control/libboot_control.h | 8 +++++++ boot_control/libboot_control.cpp | 21 +++++++++++++++++++ .../bootloader_message/bootloader_message.h | 4 +++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/boot_control/Android.bp b/boot_control/Android.bp index f6a6cedd..b2e68dfd 100644 --- a/boot_control/Android.bp +++ b/boot_control/Android.bp @@ -28,6 +28,7 @@ cc_defaults { ], shared_libs: [ + "android.hardware.boot@1.1", "libbase", "liblog", ], diff --git a/boot_control/include/libboot_control/libboot_control.h b/boot_control/include/libboot_control/libboot_control.h index 6582d024..34a9affe 100644 --- a/boot_control/include/libboot_control/libboot_control.h +++ b/boot_control/include/libboot_control/libboot_control.h @@ -18,11 +18,15 @@ #include +#include + namespace android { namespace bootable { // Helper library to implement the IBootControl HAL using the misc partition. class BootControl { + using MergeStatus = ::android::hardware::boot::V1_1::MergeStatus; + public: bool Init(); unsigned int GetNumberSlots(); @@ -34,6 +38,10 @@ class BootControl { bool IsSlotBootable(unsigned int slot); const char* GetSuffix(unsigned int slot); bool IsSlotMarkedSuccessful(unsigned int slot); + bool SetSnapshotMergeStatus(MergeStatus status); + MergeStatus GetSnapshotMergeStatus(); + + bool IsValidSlot(unsigned int slot); const std::string& misc_device() const { return misc_device_; diff --git a/boot_control/libboot_control.cpp b/boot_control/libboot_control.cpp index 89cf8786..e3bff9ff 100644 --- a/boot_control/libboot_control.cpp +++ b/boot_control/libboot_control.cpp @@ -34,6 +34,8 @@ namespace android { namespace bootable { +using ::android::hardware::boot::V1_1::MergeStatus; + // The number of boot attempts that should be made from a new slot before // rolling back to the previous slot. constexpr unsigned int kDefaultBootAttempts = 7; @@ -327,6 +329,25 @@ bool BootControl::IsSlotMarkedSuccessful(unsigned int slot) { return bootctrl.slot_info[slot].successful_boot && bootctrl.slot_info[slot].tries_remaining; } +bool BootControl::IsValidSlot(unsigned int slot) { + return slot < kMaxNumSlots && slot < num_slots_; +} + +bool BootControl::SetSnapshotMergeStatus(MergeStatus status) { + bootloader_control bootctrl; + if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; + + bootctrl.merge_status = (unsigned int)status; + return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl); +} + +MergeStatus BootControl::GetSnapshotMergeStatus() { + bootloader_control bootctrl; + if (!LoadBootloaderControl(misc_device_, &bootctrl)) return MergeStatus::UNKNOWN; + + return (MergeStatus)bootctrl.merge_status; +} + const char* BootControl::GetSuffix(unsigned int slot) { if (slot >= kMaxNumSlots || slot >= num_slots_) { return nullptr; diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 5c0a450f..b7878308 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -163,8 +163,10 @@ struct bootloader_control { uint8_t nb_slot : 3; // Number of times left attempting to boot recovery. uint8_t recovery_tries_remaining : 3; + // Status of any pending snapshot merge of dynamic partitions. + uint8_t merge_status : 3; // Ensure 4-bytes alignment for slot_info field. - uint8_t reserved0[2]; + uint8_t reserved0[1]; // Per-slot information. Up to 4 slots. struct slot_metadata slot_info[4]; // Reserved for further use. From e4f1a781f5e13168b22d9877128e3bda47217fab Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Thu, 5 Sep 2019 14:29:23 -0700 Subject: [PATCH 086/171] Remove reference to libhwbinder_noltopgo. No longer needed. Bug: 135558503 Test: build only Change-Id: Ia1257513c6276cdb01604fbedb411e7412d02b84 --- tests/Android.bp | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Android.bp b/tests/Android.bp index a8670401..8e150175 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -91,8 +91,6 @@ librecovery_static_libs = [ "libfs_mgr", "libhidl-gen-utils", "libhidlbase", - "libhidltransport", - "libhwbinder_noltopgo", "libbinderthreadstate", "liblp", "libvndksupport", From d81519400cc2cf52dd7c1c7daac4a7cf00d860c3 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 5 Sep 2019 15:34:50 -0700 Subject: [PATCH 087/171] Set LOCAL_INJECT_BSSL_HASH for updater updater is built in Make and statically links libcrypto, so it needs to set LOCAL_INJECT_BSSL_HASH to make the FIPS self test pass. Bug: 137267623 Test: m checkbuild Change-Id: Ib253c870090ac8026f5cbb005d6b0e935c73edd1 --- updater/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/updater/Android.mk b/updater/Android.mk index 93525c12..bc766c45 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -109,6 +109,7 @@ LOCAL_GENERATED_SOURCES := $(inc) inc := LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_INJECT_BSSL_HASH := true include $(BUILD_EXECUTABLE) From 95656827dd186d957430da5c0bc4a27b98b098f9 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 12 Sep 2019 13:03:06 -0700 Subject: [PATCH 088/171] Update the instruction for building recovery image on Pixels. Test: N/A Change-Id: I14d7bba5b214cc0bf43d6294cff1b7dcbea72ee8 --- README.md | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0ccc10b5..bd1cf7de 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,33 @@ The Recovery Image Quick turn-around testing ------------------------- - mm -j && m ramdisk-nodeps && m recoveryimage-nodeps +* Devices using recovery-as-boot (e.g. Pixels, which set BOARD\_USES\_RECOVERY\_AS\_BOOT) - # To boot into the new recovery image - # without flashing the recovery partition: - adb reboot bootloader - fastboot boot $ANDROID_PRODUCT_OUT/recovery.img + # After setting up environment and lunch. + m -j bootimage + adb reboot bootloader + + # Pixel devices don't support booting into recovery mode with `fastboot boot`. + fastboot flash boot + + # Manually choose `Recovery mode` from bootloader menu. + +* Devices with a separate recovery image (e.g. Nexus) + + # After setting up environment and lunch. + mm -j && m ramdisk-nodeps && m recoveryimage-nodeps + adb reboot bootloader + + # To boot into the new recovery image without flashing the recovery partition: + fastboot boot $ANDROID_PRODUCT_OUT/recovery.img Running the tests ----------------- + # After setting up environment and lunch. mmma -j bootable/recovery - # Running the tests on device. + # Running the tests on device (under normal boot). adb root adb sync data From 189d424ceda58a0f0466f1e7c3ad93c3dca24f6f Mon Sep 17 00:00:00 2001 From: Pete Bentley Date: Thu, 12 Sep 2019 19:53:46 +0100 Subject: [PATCH 089/171] Link libcrypto dynamically for recovery unit tests. Tested by running recovery_unit_test as described in https://android.googlesource.com/platform/bootable/recovery/+/refs/heads/master/README.md Attempted to build and boot a recovery image with the same change to confirm it still works, but m recoveryimage-nodeps fails for me. Bug: 140940227 Test: See above Change-Id: I00545968a0e5684823e505f2ddbe7e993319b5d4 --- applypatch/Android.bp | 10 ++++++++-- minadbd/Android.bp | 2 +- tests/Android.bp | 1 - updater/Android.bp | 5 ++++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/applypatch/Android.bp b/applypatch/Android.bp index 42aa5295..ae7e9c5c 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -51,12 +51,15 @@ cc_library_static { "libbase", "libbspatch", "libbz", - "libcrypto", "libedify", "libotautil", "libz", ], + shared_libs: [ + "libcrypto", + ], + target: { darwin: { enabled: false, @@ -78,10 +81,13 @@ cc_library_static { static_libs: [ "libapplypatch", "libbase", - "libcrypto", "libedify", "libotautil", ], + + shared_libs: [ + "libcrypto", + ], } cc_binary { diff --git a/minadbd/Android.bp b/minadbd/Android.bp index afd57ad2..805d12af 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -102,11 +102,11 @@ cc_test { "libfusesideload", "libotautil", "libadbd", - "libcrypto", ], shared_libs: [ "libbase", + "libcrypto", "libcutils", "liblog", ], diff --git a/tests/Android.bp b/tests/Android.bp index 8e150175..232697d7 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -64,7 +64,6 @@ libapplypatch_static_libs = [ "libbase", "libbrotli", "libbz", - "libcrypto", "libz", "libziparchive", ] diff --git a/updater/Android.bp b/updater/Android.bp index 063366e5..872ef86d 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -41,11 +41,14 @@ cc_defaults { "libziparchive", "libz", "libbase", - "libcrypto", "libcrypto_utils", "libcutils", "libutils", ], + + shared_libs: [ + "libcrypto", + ], } cc_defaults { From ff185865fd8c634c009b8caa1cc18bffe89f023f Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 18 Sep 2019 13:28:32 -0700 Subject: [PATCH 090/171] recovery: Don't report unrecognized option in recovery_main. recovery_main is only interested in a subset of options. Suppress the warning for unrecognized options (which are likely intended for recovery). Fixes: 141239600 Test: On Pixel 3a, write `--security` flag into /misc and reboot recovery. Check that recovery_main no longer reports "unrecognized option" warning. # echo "recovery\n--security\n" | \ dd of=/dev/block/by-name/misc conv=notrunc bs=1 seek=64 # killall -9 recovery # cat /tmp/recovery.log Test: Write `--show_text=foo` instead, and check that recovery reports "option `--show_text' doesn't allow an argument". Change-Id: I1cb3a5ed6db91e41453b8aad00c8608f78ac1fa6 --- recovery_main.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/recovery_main.cpp b/recovery_main.cpp index 28197bf4..a04c1bba 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -351,6 +351,12 @@ int main(int argc, char** argv) { std::string locale; std::string reason; + // The code here is only interested in the options that signal the intent to start fastbootd or + // recovery. Unrecognized options are likely meant for recovery, which will be processed later in + // start_recovery(). Suppress the warnings for such -- even if some flags were indeed invalid, the + // code in start_recovery() will capture and report them. + opterr = 0; + int arg; int option_index; while ((arg = getopt_long(args_to_parse.size() - 1, args_to_parse.data(), "", OPTIONS, @@ -374,6 +380,7 @@ int main(int argc, char** argv) { } } optind = 1; + opterr = 1; if (locale.empty()) { if (HasCache()) { From dfb053e81537ed7896ca12cf9ddcc4205952fc7c Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 19 Sep 2019 08:08:54 -0700 Subject: [PATCH 091/171] tests: recovery_unit_test requires root. Bug: 141272654 Test: TreeHugger (recovery_unit_test no longer fails) Change-Id: I47cbee274e659e3d90be5a77b215466d2973c7d6 --- tests/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Android.bp b/tests/Android.bp index 232697d7..3335c0b8 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -99,6 +99,7 @@ librecovery_static_libs = [ cc_test { name: "recovery_unit_test", isolated: true, + require_root: true, defaults: [ "recovery_test_defaults", From 71c35b9fbaac7f6bbbc175e066564b9509b3bde8 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 23 Sep 2019 08:58:44 -0700 Subject: [PATCH 092/171] applypatch: Use static libs for libbrotli and libbz. Prior to this CL, applypatch was the only user of libbrotli.so and libbz.so (update_engine and puffin have been using the static versions). This CL switches applypatch to their static versions to save space in /system. Sizes prior to the change (aosp_taimen-userdebug): /system/bin/applypatch 91296 /system/lib64/libbrotli.so 688536 /system/lib64/libbz.so 79560 After: /system/bin/applypatch 272368 Test: `m dist` Test: Check the built blueline-userdebug target. libbrotli.so and libbz.so are not installed. Change-Id: I08422a0d5a287bbac69aa9f6cfd9c97e5b2e9078 --- CleanSpec.mk | 3 +++ applypatch/Android.bp | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CleanSpec.mk b/CleanSpec.mk index 8405d20e..0980a35f 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -58,6 +58,9 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/testcases/recovery_component_test) $(call add-clean-step, find $(OUT_DIR) -type f -name "SystemUpdaterSample*" -print0 | xargs -0 rm -f) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUpdaterSample) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libbrotli.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libbz.so) + # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/applypatch/Android.bp b/applypatch/Android.bp index ae7e9c5c..55d18520 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -106,13 +106,15 @@ cc_binary { "libapplypatch", "libedify", "libotautil", + + // External dependencies. "libbspatch", + "libbrotli", + "libbz", ], shared_libs: [ "libbase", - "libbrotli", - "libbz", "libcrypto", "liblog", "libz", From 5234ad466c7006430fcd62f0f0ceeb768da5ec93 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 23 Sep 2019 10:28:54 -0700 Subject: [PATCH 093/171] applypatch: Add backup_source parameter to PatchPartition. And set it to false when installing recovery image via applypatch. We only need to back up the source partition when doing in-place update (e.g. when updating a given partition under recovery). When installing recovery image via applypatch, we won't touch the source partition (i.e. /boot). Removing the backup step also allows dropping the dac_override_allowed permission. Previously it was needed due to the access to /cache. Because applypatch runs as root:root, while /cache is owned by system:cache with 0770. Bug: 68319577 Test: Invoke the code that installs recovery image; check that recovery is installed successfully without denials. Test: recovery_unit_test passes on taimen. Change-Id: I549a770b511762189d6672a2835b6e403d695919 --- applypatch/applypatch.cpp | 16 +++++++++------- applypatch/applypatch_modes.cpp | 2 +- applypatch/include/applypatch/applypatch.h | 7 ++++--- tests/unit/applypatch_test.cpp | 4 ++-- updater/install.cpp | 2 +- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 90d8e860..336860cb 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -47,7 +47,7 @@ using namespace std::string_literals; static bool GenerateTarget(const Partition& target, const FileContents& source_file, - const Value& patch, const Value* bonus_data); + const Value& patch, const Value* bonus_data, bool backup_source); bool LoadFileContents(const std::string& filename, FileContents* file) { // No longer allow loading contents from eMMC partitions. @@ -266,7 +266,7 @@ int ShowLicenses() { } bool PatchPartition(const Partition& target, const Partition& source, const Value& patch, - const Value* bonus) { + const Value* bonus, bool backup_source) { LOG(INFO) << "Patching " << target.name; // We try to load and check against the target hash first. @@ -280,7 +280,7 @@ bool PatchPartition(const Partition& target, const Partition& source, const Valu FileContents source_file; if (ReadPartitionToBuffer(source, &source_file, true)) { - return GenerateTarget(target, source_file, patch, bonus); + return GenerateTarget(target, source_file, patch, bonus, backup_source); } LOG(ERROR) << "Failed to find any match"; @@ -326,7 +326,7 @@ bool FlashPartition(const Partition& partition, const std::string& source_filena } static bool GenerateTarget(const Partition& target, const FileContents& source_file, - const Value& patch, const Value* bonus_data) { + const Value& patch, const Value* bonus_data, bool backup_source) { uint8_t expected_sha1[SHA_DIGEST_LENGTH]; if (ParseSha1(target.hash, expected_sha1) != 0) { LOG(ERROR) << "Failed to parse target hash \"" << target.hash << "\""; @@ -351,11 +351,11 @@ static bool GenerateTarget(const Partition& target, const FileContents& source_f } // We write the original source to cache, in case the partition write is interrupted. - if (!CheckAndFreeSpaceOnCache(source_file.data.size())) { + if (backup_source && !CheckAndFreeSpaceOnCache(source_file.data.size())) { LOG(ERROR) << "Not enough free space on /cache"; return false; } - if (!SaveFileContents(Paths::Get().cache_temp_source(), &source_file)) { + if (backup_source && !SaveFileContents(Paths::Get().cache_temp_source(), &source_file)) { LOG(ERROR) << "Failed to back up source file"; return false; } @@ -415,7 +415,9 @@ static bool GenerateTarget(const Partition& target, const FileContents& source_f } // Delete the backup copy of the source. - unlink(Paths::Get().cache_temp_source().c_str()); + if (backup_source) { + unlink(Paths::Get().cache_temp_source().c_str()); + } // Success! return true; diff --git a/applypatch/applypatch_modes.cpp b/applypatch/applypatch_modes.cpp index b4665980..bb5eeae9 100644 --- a/applypatch/applypatch_modes.cpp +++ b/applypatch/applypatch_modes.cpp @@ -87,7 +87,7 @@ static int PatchMode(const std::string& target_emmc, const std::string& source_e bonus = std::make_unique(Value::Type::BLOB, std::move(bonus_contents)); } - return PatchPartition(target, source, patch, bonus.get()) ? 0 : 1; + return PatchPartition(target, source, patch, bonus.get(), false) ? 0 : 1; } static void Usage() { diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index 6fc6f0fc..799f4b2d 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -73,10 +73,11 @@ std::ostream& operator<<(std::ostream& os, const Partition& partition); // the 'target' Partition. While patching, it will backup the data on the source partition to // /cache, so that the patching could be resumed on interruption even if both of the source and // target partitions refer to the same device. The function is idempotent if called multiple times. -// An optional arg 'bonus' can be provided, if the patch was generated with a bonus output. -// Returns the patching result. +// 'bonus' can be provided if the patch was generated with a bonus output, or nullptr. +// 'backup_source' indicates whether the source partition should be backed up prior to the update +// (e.g. when doing in-place update). Returns the patching result. bool PatchPartition(const Partition& target, const Partition& source, const Value& patch, - const Value* bonus); + const Value* bonus, bool backup_source); // Returns whether the contents of the eMMC target or the cached file match the embedded hash. // It will look for the backup on /cache if the given partition doesn't match the checksum. diff --git a/tests/unit/applypatch_test.cpp b/tests/unit/applypatch_test.cpp index 794f2c10..218a224f 100644 --- a/tests/unit/applypatch_test.cpp +++ b/tests/unit/applypatch_test.cpp @@ -141,7 +141,7 @@ TEST_F(ApplyPatchTest, PatchPartition) { ASSERT_TRUE(LoadFileContents(from_testdata_base("bonus.file"), &bonus_fc)); Value bonus(Value::Type::BLOB, std::string(bonus_fc.data.cbegin(), bonus_fc.data.cend())); - ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, &bonus)); + ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, &bonus, false)); } // Tests patching an eMMC target without a separate bonus file (i.e. recovery-from-boot patch has @@ -151,7 +151,7 @@ TEST_F(ApplyPatchTest, PatchPartitionWithoutBonusFile) { ASSERT_TRUE(LoadFileContents(from_testdata_base("recovery-from-boot-with-bonus.p"), &patch_fc)); Value patch(Value::Type::BLOB, std::string(patch_fc.data.cbegin(), patch_fc.data.cend())); - ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, nullptr)); + ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, nullptr, false)); } class FreeCacheTest : public ::testing::Test { diff --git a/updater/install.cpp b/updater/install.cpp index be0ceb06..b617f62c 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -271,7 +271,7 @@ Value* PatchPartitionFn(const char* name, State* state, return StringValue(""); } - bool result = PatchPartition(target, source, *values[0], nullptr); + bool result = PatchPartition(target, source, *values[0], nullptr, true); return StringValue(result ? "t" : ""); } From 0e643e4d7f3f1db678baa4d1f4630d6a0d19f851 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 18 Sep 2019 11:05:34 -0700 Subject: [PATCH 094/171] Use libcrypto_static instead of libcrypto Replace libcrypto with libcrypto_static, which can be protected through visibility to ensure only modules that don't affect FIPS certification can use it. Bug: 141248879 Test: m checkbuild Change-Id: I0affaa292237bdbc772d3adc36086905ed6fbe9d --- updater/Android.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/updater/Android.mk b/updater/Android.mk index bc766c45..6f54d89b 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -44,7 +44,7 @@ updater_common_static_libraries := \ libziparchive \ libz \ libbase \ - libcrypto \ + libcrypto_static \ libcrypto_utils \ libcutils \ libutils @@ -109,7 +109,6 @@ LOCAL_GENERATED_SOURCES := $(inc) inc := LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_INJECT_BSSL_HASH := true include $(BUILD_EXECUTABLE) From 92214baec296e1de024d7482141078fad47de866 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 24 Sep 2019 14:36:40 -0700 Subject: [PATCH 095/171] Import translations. DO NOT MERGE Change-Id: I18c2199dc9439d6013329d1f0eb40dd106618bfb Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-gl/strings.xml | 4 ++-- tools/recovery_l10n/res/values-in/strings.xml | 2 +- tools/recovery_l10n/res/values-ja/strings.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml index e51b36df..e6f2ffd8 100644 --- a/tools/recovery_l10n/res/values-gl/strings.xml +++ b/tools/recovery_l10n/res/values-gl/strings.xml @@ -6,9 +6,9 @@ "Non hai ningún comando" "Erro" "Instalando actualización de seguranza" - "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." + "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." "Tentar de novo" "Restablecemento dos datos de fábrica" - "Queres borrar todos os datos de usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER." + "Queres borrar todos os datos do usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER." "Cancelar" diff --git a/tools/recovery_l10n/res/values-in/strings.xml b/tools/recovery_l10n/res/values-in/strings.xml index 15a78ec4..43c9deb9 100644 --- a/tools/recovery_l10n/res/values-in/strings.xml +++ b/tools/recovery_l10n/res/values-in/strings.xml @@ -9,6 +9,6 @@ "Tidak dapat memuat sistem Android. Data Anda mungkin rusak. Jika terus mendapatkan pesan ini, Anda mungkin perlu melakukan reset ke setelan pabrik dan menghapus semua data pengguna yang disimpan di perangkat ini." "Coba lagi" "Reset ke setelan pabrik" - "Wipe semua data pengguna?\n\n TINDAKAN INI TIDAK DAPAT DIURUNGKAN!" + "Hapus total semua data pengguna?\n\n TINDAKAN INI TIDAK DAPAT DIURUNGKAN!" "Batal" diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml index 3d663727..2d6c0abc 100644 --- a/tools/recovery_l10n/res/values-ja/strings.xml +++ b/tools/recovery_l10n/res/values-ja/strings.xml @@ -6,7 +6,7 @@ "コマンドが指定されていません" "エラーが発生しました。" "セキュリティ アップデートをインストールしています" - "Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、この端末に保存されているすべてのユーザー データを消去することが必要な場合があります。" + "Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、このデバイスに保存されているすべてのユーザー データを消去することが必要な場合があります。" "再試行" "データの初期化" "すべてのユーザー データをワイプしますか?\n\nこの操作は元に戻せません。" From ff9b62b78116e4ca5b3e47cc1f8fac11f5d76ebb Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Wed, 25 Sep 2019 15:10:14 -0700 Subject: [PATCH 096/171] Remove libhidltransport deps Since this was combined into libhidlbase. Bug: 135686713 Test: build only (libhidltransport is empty) Change-Id: I253e50726967044714275ab995fb8a8a57bcde36 --- install/Android.bp | 1 - 1 file changed, 1 deletion(-) diff --git a/install/Android.bp b/install/Android.bp index 89cc3f23..fbc0d136 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -32,7 +32,6 @@ cc_defaults { "libfusesideload", "libhidl-gen-utils", "libhidlbase", - "libhidltransport", "liblog", "libselinux", "libtinyxml2", From 3305d48b0be5c6d1578e04e9835c236335a40a9f Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 26 Sep 2019 00:02:29 -0700 Subject: [PATCH 097/171] minadbd: Export minadbd/types.h to libinstall. Test: mmma bootable/recovery Change-Id: I503e942b23cc51024aa752c1eb3db5455a44a9d1 --- install/Android.bp | 8 ++++---- install/adb_install.cpp | 2 +- minadbd/Android.bp | 11 +++++++++-- minadbd/{minadbd_types.h => include/minadbd/types.h} | 0 minadbd/minadbd.cpp | 2 +- minadbd/minadbd_services.cpp | 2 +- minadbd/minadbd_services_test.cpp | 2 +- 7 files changed, 17 insertions(+), 10 deletions(-) rename minadbd/{minadbd_types.h => include/minadbd/types.h} (100%) diff --git a/install/Android.bp b/install/Android.bp index 89cc3f23..78c3a578 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -19,10 +19,6 @@ cc_defaults { "recovery_defaults", ], - header_libs: [ - "libminadbd_headers", - ], - shared_libs: [ "libbase", "libbootloader_message", @@ -69,6 +65,10 @@ cc_library_static { "wipe_device.cpp", ], + header_libs: [ + "libminadbd_headers", + ], + shared_libs: [ "librecovery_ui", ], diff --git a/install/adb_install.cpp b/install/adb_install.cpp index ed664429..ee79a32c 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -44,7 +44,7 @@ #include "fuse_sideload.h" #include "install/install.h" #include "install/wipe_data.h" -#include "minadbd_types.h" +#include "minadbd/types.h" #include "otautil/sysutil.h" #include "recovery_ui/device.h" #include "recovery_ui/ui.h" diff --git a/minadbd/Android.bp b/minadbd/Android.bp index 805d12af..07171259 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -26,6 +26,10 @@ cc_defaults { include_dirs: [ "system/core/adb", ], + + header_libs: [ + "libminadbd_headers", + ], } // `libminadbd_services` is analogous to the `libadbd_services` for regular `adbd`, but providing @@ -58,9 +62,12 @@ cc_library { cc_library_headers { name: "libminadbd_headers", recovery_available: true, - // TODO create a include dir export_include_dirs: [ - ".", + "include", + ], + // adb_install.cpp + visibility: [ + "//bootable/recovery/install", ], } diff --git a/minadbd/minadbd_types.h b/minadbd/include/minadbd/types.h similarity index 100% rename from minadbd/minadbd_types.h rename to minadbd/include/minadbd/types.h diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp index c80d5490..7b82faa0 100644 --- a/minadbd/minadbd.cpp +++ b/minadbd/minadbd.cpp @@ -28,8 +28,8 @@ #include "adb_auth.h" #include "transport.h" +#include "minadbd/types.h" #include "minadbd_services.h" -#include "minadbd_types.h" using namespace std::string_literals; diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index c31afbe0..cabcdaa0 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -43,7 +43,7 @@ #include "adb_utils.h" #include "fuse_adb_provider.h" #include "fuse_sideload.h" -#include "minadbd_types.h" +#include "minadbd/types.h" #include "services.h" #include "sysdeps.h" diff --git a/minadbd/minadbd_services_test.cpp b/minadbd/minadbd_services_test.cpp index f8787379..b694a57d 100644 --- a/minadbd/minadbd_services_test.cpp +++ b/minadbd/minadbd_services_test.cpp @@ -35,8 +35,8 @@ #include "adb_io.h" #include "fuse_adb_provider.h" #include "fuse_sideload.h" +#include "minadbd/types.h" #include "minadbd_services.h" -#include "minadbd_types.h" #include "socket.h" class MinadbdServicesTest : public ::testing::Test { From 0cc2c5b453f49772b996b36ca08b09e4b90cc8b9 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 27 Sep 2019 11:50:43 -0700 Subject: [PATCH 098/171] minadbd: Update README.md. Test: N/A Change-Id: I7f4648f2b357de7d9700e38552a0156d13c1bd0b --- minadbd/README.md | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/minadbd/README.md b/minadbd/README.md index 5a0a067d..9a195830 100644 --- a/minadbd/README.md +++ b/minadbd/README.md @@ -1,8 +1,24 @@ -minadbd is now mostly built from libadbd. The fuse features are unique to -minadbd, and services.c has been modified as follows: +minadbd +======= - - all services removed - - all host mode support removed - - `sideload_service()` added; this is the only service supported. It - receives a single blob of data, writes it to a fixed filename, and - makes the process exit. +`minadbd` is analogous to the regular `adbd`, but providing the minimal services to support +recovery-specific use cases. Generally speaking, `adbd` = `libadbd` + `libadbd_services`, whereas +`minadbd` = `libadbd` + `libminadbd_services`. + +Although both modules may be installed into the recovery image, only one of them, or none, can be +active at any given time. + +- The start / stop of `adbd` is managed via system property `sys.usb.config`, when setting to `adb` + or `none` respectively. Upon starting recovery mode, `adbd` is started in debuggable builds by + default; otherwise `adbd` will stay off at all times in user builds. See the triggers in + `bootable/recovery/etc/init.rc`. + +- `minadbd` is started by `recovery` as needed. + - When requested to start `minadbd`, `recovery` stops `adbd` first, if it's running; it then forks + and execs `minadbd` in a separate process. + - `minadbd` talks to host-side `adb` server to get user requests. + - `minadbd` handles some requests directly, e.g. querying device properties for rescue service. + - `minadbd` communicates with `recovery` to fulfill requests regarding package installation. See + the comments in `bootable/recovery/install/adb_install.cpp` for the IPC protocol between + `recovery` and `minadbd`. + - Upon exiting `minadbd`, `recovery` restarts `adbd` if it was previously running. From 331e5c4c98907b471e3b871796508cacccbf06f0 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 1 Oct 2019 12:01:08 -0700 Subject: [PATCH 099/171] otautil: roots.cpp no longer needs 'mounts.h'. Commit 0f339e27bb753b0dafb17a315fa4b029ba31aa28 moved part of the mounts implementation into libfs_mgr. As a result, otautil/roots.cpp no longer depends on anything in the local otautil/mounts.h. Test: mmma bootable/recovery Change-Id: If16c3e19a62933358fb0002a10e8556a99f9d29a --- otautil/roots.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/otautil/roots.cpp b/otautil/roots.cpp index 43155178..aa7809b5 100644 --- a/otautil/roots.cpp +++ b/otautil/roots.cpp @@ -42,7 +42,6 @@ #include #include -#include "otautil/mounts.h" #include "otautil/sysutil.h" using android::fs_mgr::Fstab; From d628cfc15316605ad541c4cec683462a57458d75 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 1 Oct 2019 12:08:33 -0700 Subject: [PATCH 100/171] Move mounts.cpp from libotautil into libupdater. All the active users of mounts.h now live in updater/. Test: mmma bootable/recovery Test: Run recovery_unit_test on taimen. Test: Code search shows no reference to otautil/mounts.h in device dirs. Change-Id: I6c35d2e403e92a0111102d00aa4773f4f524650e --- otautil/Android.bp | 1 - updater/Android.bp | 1 + updater/install.cpp | 1 - {otautil => updater}/mounts.cpp | 2 +- {otautil/include/otautil => updater}/mounts.h | 0 updater/simulator_runtime.cpp | 2 +- updater/updater_runtime.cpp | 2 +- 7 files changed, 4 insertions(+), 5 deletions(-) rename {otautil => updater}/mounts.cpp (98%) rename {otautil/include/otautil => updater}/mounts.h (100%) diff --git a/otautil/Android.bp b/otautil/Android.bp index 871dcae9..6f816a17 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -44,7 +44,6 @@ cc_library_static { android: { srcs: [ "logging.cpp", - "mounts.cpp", "parse_install_logs.cpp", "roots.cpp", "thermalutil.cpp", diff --git a/updater/Android.bp b/updater/Android.bp index 872ef86d..8a60ef76 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -81,6 +81,7 @@ cc_library_static { "blockimg.cpp", "commands.cpp", "install.cpp", + "mounts.cpp", "updater.cpp", ], diff --git a/updater/install.cpp b/updater/install.cpp index b617f62c..62ff87e7 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -60,7 +60,6 @@ #include "edify/updater_runtime_interface.h" #include "otautil/dirutil.h" #include "otautil/error_code.h" -#include "otautil/mounts.h" #include "otautil/print_sha1.h" #include "otautil/sysutil.h" diff --git a/otautil/mounts.cpp b/updater/mounts.cpp similarity index 98% rename from otautil/mounts.cpp rename to updater/mounts.cpp index 951311bf..943d35c7 100644 --- a/otautil/mounts.cpp +++ b/updater/mounts.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "otautil/mounts.h" +#include "mounts.h" #include #include diff --git a/otautil/include/otautil/mounts.h b/updater/mounts.h similarity index 100% rename from otautil/include/otautil/mounts.h rename to updater/mounts.h diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp index d2074d69..3ed7bf33 100644 --- a/updater/simulator_runtime.cpp +++ b/updater/simulator_runtime.cpp @@ -32,7 +32,7 @@ #include #include -#include "otautil/mounts.h" +#include "mounts.h" #include "otautil/sysutil.h" std::string SimulatorRuntime::GetProperty(const std::string_view key, diff --git a/updater/updater_runtime.cpp b/updater/updater_runtime.cpp index 761f9997..c4222a56 100644 --- a/updater/updater_runtime.cpp +++ b/updater/updater_runtime.cpp @@ -31,7 +31,7 @@ #include #include -#include "otautil/mounts.h" +#include "mounts.h" #include "otautil/sysutil.h" std::string UpdaterRuntime::GetProperty(const std::string_view key, From f6ed376b20952f7cb7170a2fbb714bee84ecdd55 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 1 Oct 2019 15:50:05 -0700 Subject: [PATCH 101/171] otautil: Drop a few unneeded includes. Test: mmma bootable/recovery Change-Id: I6ed8f9eca7183f84537c770baaffddd22b6e9f28 --- otautil/roots.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/otautil/roots.cpp b/otautil/roots.cpp index aa7809b5..e098b4b7 100644 --- a/otautil/roots.cpp +++ b/otautil/roots.cpp @@ -16,13 +16,10 @@ #include "otautil/roots.h" -#include #include -#include #include #include #include -#include #include #include #include @@ -33,14 +30,12 @@ #include #include -#include #include #include #include #include #include #include -#include #include "otautil/sysutil.h" From e3f09a72f51df5abcf46a63de4a39ca5ed53698b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 1 Oct 2019 11:55:36 -0700 Subject: [PATCH 102/171] otautil: Factor out the utils that're private to recovery. A number of utility functions are intended for serving recovery's own use. Exposing them via libotautil (which is a static lib) would pass the dependencies onto libotautil's users (e.g. recovery image, updater, host simulator, device-specific recovery UI/updater extensions etc). This CL finds a new home for the utils that are private to recovery. Test: mmma bootable/recovery Change-Id: I575e97ad099b85fe1c1c8c7c9458a5a43d4e11e1 --- Android.bp | 5 +- fsck_unshare_blocks.cpp | 2 +- install/Android.bp | 1 + install/fuse_install.cpp | 2 +- install/install.cpp | 4 +- install/wipe_data.cpp | 6 +- otautil/Android.bp | 28 -------- recovery-persist.cpp | 4 +- recovery-refresh.cpp | 3 +- recovery.cpp | 4 +- recovery_main.cpp | 4 +- recovery_utils/Android.bp | 66 +++++++++++++++++++ .../include/recovery_utils}/logging.h | 0 .../recovery_utils}/parse_install_logs.h | 0 .../include/recovery_utils}/roots.h | 0 .../include/recovery_utils}/thermalutil.h | 0 {otautil => recovery_utils}/logging.cpp | 4 +- .../parse_install_logs.cpp | 2 +- {otautil => recovery_utils}/roots.cpp | 2 +- {otautil => recovery_utils}/thermalutil.cpp | 2 +- tests/Android.bp | 1 + tests/unit/parse_install_logs_test.cpp | 2 +- 22 files changed, 92 insertions(+), 50 deletions(-) create mode 100644 recovery_utils/Android.bp rename {otautil/include/otautil => recovery_utils/include/recovery_utils}/logging.h (100%) rename {otautil/include/otautil => recovery_utils/include/recovery_utils}/parse_install_logs.h (100%) rename {otautil/include/otautil => recovery_utils/include/recovery_utils}/roots.h (100%) rename {otautil/include/otautil => recovery_utils/include/recovery_utils}/thermalutil.h (100%) rename {otautil => recovery_utils}/logging.cpp (99%) rename {otautil => recovery_utils}/parse_install_logs.cpp (98%) rename {otautil => recovery_utils}/roots.cpp (99%) rename {otautil => recovery_utils}/thermalutil.cpp (98%) diff --git a/Android.bp b/Android.bp index 0eb5fd9e..0759e08d 100644 --- a/Android.bp +++ b/Android.bp @@ -72,6 +72,7 @@ cc_defaults { "libinstall", "librecovery_fastboot", "libminui", + "librecovery_utils", "libotautil", // external dependencies @@ -148,7 +149,7 @@ cc_binary { ], static_libs: [ - "libotautil", + "librecovery_utils", ], init_rc: [ @@ -174,7 +175,7 @@ cc_binary { ], static_libs: [ - "libotautil", + "librecovery_utils", ], init_rc: [ diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp index 9dc0fd8e..e0b2d966 100644 --- a/fsck_unshare_blocks.cpp +++ b/fsck_unshare_blocks.cpp @@ -36,7 +36,7 @@ #include #include -#include "otautil/roots.h" +#include "recovery_utils/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/install/Android.bp b/install/Android.bp index e2402704..d4606e92 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -37,6 +37,7 @@ cc_defaults { ], static_libs: [ + "librecovery_utils", "libotautil", // external dependencies diff --git a/install/fuse_install.cpp b/install/fuse_install.cpp index 8a7a278e..143b5d3f 100644 --- a/install/fuse_install.cpp +++ b/install/fuse_install.cpp @@ -37,7 +37,7 @@ #include "fuse_provider.h" #include "fuse_sideload.h" #include "install/install.h" -#include "otautil/roots.h" +#include "recovery_utils/roots.h" static constexpr const char* SDCARD_ROOT = "/sdcard"; // How long (in seconds) we wait for the fuse-provided package file to diff --git a/install/install.cpp b/install/install.cpp index 9d67b010..9166f9cf 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -51,11 +51,11 @@ #include "install/wipe_data.h" #include "otautil/error_code.h" #include "otautil/paths.h" -#include "otautil/roots.h" #include "otautil/sysutil.h" -#include "otautil/thermalutil.h" #include "private/setup_commands.h" #include "recovery_ui/ui.h" +#include "recovery_utils/roots.h" +#include "recovery_utils/thermalutil.h" using namespace std::chrono_literals; diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp index 765a8152..82660bef 100644 --- a/install/wipe_data.cpp +++ b/install/wipe_data.cpp @@ -28,9 +28,9 @@ #include #include "otautil/dirutil.h" -#include "otautil/logging.h" -#include "otautil/roots.h" #include "recovery_ui/ui.h" +#include "recovery_utils/logging.h" +#include "recovery_utils/roots.h" constexpr const char* CACHE_ROOT = "/cache"; constexpr const char* DATA_ROOT = "/data"; @@ -120,4 +120,4 @@ bool WipeData(Device* device, bool convert_fbe) { } ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); return success; -} \ No newline at end of file +} diff --git a/otautil/Android.bp b/otautil/Android.bp index 6f816a17..c8f97468 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -39,32 +39,4 @@ cc_library_static { export_include_dirs: [ "include", ], - - target: { - android: { - srcs: [ - "logging.cpp", - "parse_install_logs.cpp", - "roots.cpp", - "thermalutil.cpp", - ], - - include_dirs: [ - "system/vold", - ], - - static_libs: [ - "libfstab", - ], - - shared_libs: [ - "libext4_utils", - "libfs_mgr", - ], - - export_static_lib_headers: [ - "libfstab", - ], - }, - }, } diff --git a/recovery-persist.cpp b/recovery-persist.cpp index 294017a1..6dbf8625 100644 --- a/recovery-persist.cpp +++ b/recovery-persist.cpp @@ -43,8 +43,8 @@ #include #include /* private pmsg functions */ -#include "otautil/logging.h" -#include "otautil/parse_install_logs.h" +#include "recovery_utils/logging.h" +#include "recovery_utils/parse_install_logs.h" constexpr const char* LAST_LOG_FILE = "/data/misc/recovery/last_log"; constexpr const char* LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0"; diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp index d41755d0..42acd05b 100644 --- a/recovery-refresh.cpp +++ b/recovery-refresh.cpp @@ -38,11 +38,12 @@ // #include + #include #include /* private pmsg functions */ -#include "otautil/logging.h" +#include "recovery_utils/logging.h" int main(int argc, char **argv) { static const char filter[] = "recovery/"; diff --git a/recovery.cpp b/recovery.cpp index 4862dfcc..682ddbc4 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -55,12 +55,12 @@ #include "install/wipe_device.h" #include "otautil/boot_state.h" #include "otautil/error_code.h" -#include "otautil/logging.h" #include "otautil/paths.h" -#include "otautil/roots.h" #include "otautil/sysutil.h" #include "recovery_ui/screen_ui.h" #include "recovery_ui/ui.h" +#include "recovery_utils/logging.h" +#include "recovery_utils/roots.h" static constexpr const char* COMMAND_FILE = "/cache/recovery/command"; static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; diff --git a/recovery_main.cpp b/recovery_main.cpp index a04c1bba..89253dcd 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -51,14 +51,14 @@ #include "fastboot/fastboot.h" #include "install/wipe_data.h" #include "otautil/boot_state.h" -#include "otautil/logging.h" #include "otautil/paths.h" -#include "otautil/roots.h" #include "otautil/sysutil.h" #include "recovery.h" #include "recovery_ui/device.h" #include "recovery_ui/stub_ui.h" #include "recovery_ui/ui.h" +#include "recovery_utils/logging.h" +#include "recovery_utils/roots.h" static constexpr const char* COMMAND_FILE = "/cache/recovery/command"; static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale"; diff --git a/recovery_utils/Android.bp b/recovery_utils/Android.bp new file mode 100644 index 00000000..271d0799 --- /dev/null +++ b/recovery_utils/Android.bp @@ -0,0 +1,66 @@ +// 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. + +// A utility lib that's local to recovery (in contrast, libotautil is exposed to device-specific +// recovery_ui lib as well as device-specific updater). +cc_library_static { + name: "librecovery_utils", + + recovery_available: true, + + defaults: [ + "recovery_defaults", + ], + + srcs: [ + "logging.cpp", + "parse_install_logs.cpp", + "roots.cpp", + "thermalutil.cpp", + ], + + shared_libs: [ + "libbase", + "libext4_utils", + "libfs_mgr", + "libselinux", + ], + + export_include_dirs: [ + "include", + ], + + include_dirs: [ + "system/vold", + ], + + static_libs: [ + "libotautil", + + // external dependency + "libfstab", + ], + + export_static_lib_headers: [ + "libfstab", + ], + + // Should avoid exposing to the libs that might be used in device-specific codes (e.g. + // libedify, libotautil, librecovery_ui). + visibility: [ + "//bootable/recovery", + "//bootable/recovery/install", + "//bootable/recovery/tests", + ], +} diff --git a/otautil/include/otautil/logging.h b/recovery_utils/include/recovery_utils/logging.h similarity index 100% rename from otautil/include/otautil/logging.h rename to recovery_utils/include/recovery_utils/logging.h diff --git a/otautil/include/otautil/parse_install_logs.h b/recovery_utils/include/recovery_utils/parse_install_logs.h similarity index 100% rename from otautil/include/otautil/parse_install_logs.h rename to recovery_utils/include/recovery_utils/parse_install_logs.h diff --git a/otautil/include/otautil/roots.h b/recovery_utils/include/recovery_utils/roots.h similarity index 100% rename from otautil/include/otautil/roots.h rename to recovery_utils/include/recovery_utils/roots.h diff --git a/otautil/include/otautil/thermalutil.h b/recovery_utils/include/recovery_utils/thermalutil.h similarity index 100% rename from otautil/include/otautil/thermalutil.h rename to recovery_utils/include/recovery_utils/thermalutil.h diff --git a/otautil/logging.cpp b/recovery_utils/logging.cpp similarity index 99% rename from otautil/logging.cpp rename to recovery_utils/logging.cpp index 3db0e8ac..52f12a8d 100644 --- a/otautil/logging.cpp +++ b/recovery_utils/logging.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "otautil/logging.h" +#include "recovery_utils/logging.h" #include #include @@ -38,7 +38,7 @@ #include "otautil/dirutil.h" #include "otautil/paths.h" -#include "otautil/roots.h" +#include "recovery_utils/roots.h" constexpr const char* LOG_FILE = "/cache/recovery/log"; constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install"; diff --git a/otautil/parse_install_logs.cpp b/recovery_utils/parse_install_logs.cpp similarity index 98% rename from otautil/parse_install_logs.cpp rename to recovery_utils/parse_install_logs.cpp index 13a72992..c8631762 100644 --- a/otautil/parse_install_logs.cpp +++ b/recovery_utils/parse_install_logs.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "otautil/parse_install_logs.h" +#include "recovery_utils/parse_install_logs.h" #include diff --git a/otautil/roots.cpp b/recovery_utils/roots.cpp similarity index 99% rename from otautil/roots.cpp rename to recovery_utils/roots.cpp index e098b4b7..f717ec20 100644 --- a/otautil/roots.cpp +++ b/recovery_utils/roots.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "otautil/roots.h" +#include "recovery_utils/roots.h" #include #include diff --git a/otautil/thermalutil.cpp b/recovery_utils/thermalutil.cpp similarity index 98% rename from otautil/thermalutil.cpp rename to recovery_utils/thermalutil.cpp index 4660e057..5436355d 100644 --- a/otautil/thermalutil.cpp +++ b/recovery_utils/thermalutil.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "otautil/thermalutil.h" +#include "recovery_utils/thermalutil.h" #include #include diff --git a/tests/Android.bp b/tests/Android.bp index 3335c0b8..5b881e36 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -117,6 +117,7 @@ cc_test { "librecovery_ui", "libfusesideload", "libminui", + "librecovery_utils", "libotautil", "libupdater_device", "libupdater_core", diff --git a/tests/unit/parse_install_logs_test.cpp b/tests/unit/parse_install_logs_test.cpp index 72169a0c..052f71c9 100644 --- a/tests/unit/parse_install_logs_test.cpp +++ b/tests/unit/parse_install_logs_test.cpp @@ -22,7 +22,7 @@ #include #include -#include "otautil/parse_install_logs.h" +#include "recovery_utils/parse_install_logs.h" TEST(ParseInstallLogsTest, EmptyFile) { TemporaryFile last_install; From 832c9cd24fcff55da0460b8587a1c5e6145e8ab5 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 27 Sep 2019 23:32:00 -0700 Subject: [PATCH 103/171] Refactor battery info querying functions into librecovery_utils. Bug: 134560109 Test: Run recovery_unit_test. Change-Id: Ibbcdcfd507fa23657ee7ff677208b0003ec382ba --- Android.bp | 5 +- recovery.cpp | 85 +++--------------- recovery_utils/Android.bp | 43 +++++---- recovery_utils/battery_utils.cpp | 89 +++++++++++++++++++ .../include/recovery_utils/battery_utils.h | 33 +++++++ tests/unit/battery_utils_test.cpp | 27 ++++++ 6 files changed, 191 insertions(+), 91 deletions(-) create mode 100644 recovery_utils/battery_utils.cpp create mode 100644 recovery_utils/include/recovery_utils/battery_utils.h create mode 100644 tests/unit/battery_utils_test.cpp diff --git a/Android.bp b/Android.bp index 0759e08d..45aafb04 100644 --- a/Android.bp +++ b/Android.bp @@ -58,7 +58,6 @@ cc_defaults { ], shared_libs: [ - "android.hardware.health@2.0", "libbase", "libbootloader_message", "libcrypto", @@ -74,9 +73,6 @@ cc_defaults { "libminui", "librecovery_utils", "libotautil", - - // external dependencies - "libhealthhalutils", ], } @@ -105,6 +101,7 @@ cc_binary { defaults: [ "libinstall_defaults", "librecovery_defaults", + "librecovery_utils_defaults", ], srcs: [ diff --git a/recovery.cpp b/recovery.cpp index 682ddbc4..f59a940f 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -42,7 +42,6 @@ #include #include /* for property_list */ #include -#include #include #include "bootloader_message/bootloader_message.h" @@ -59,6 +58,7 @@ #include "otautil/sysutil.h" #include "recovery_ui/screen_ui.h" #include "recovery_ui/ui.h" +#include "recovery_utils/battery_utils.h" #include "recovery_utils/logging.h" #include "recovery_utils/roots.h" @@ -453,74 +453,17 @@ static void print_property(const char* key, const char* name, void* /* cookie */ printf("%s=%s\n", key, name); } -static bool is_battery_ok(int* required_battery_level) { - using android::hardware::health::V1_0::BatteryStatus; - using android::hardware::health::V2_0::get_health_service; - using android::hardware::health::V2_0::IHealth; - using android::hardware::health::V2_0::Result; - using android::hardware::health::V2_0::toString; +static bool IsBatteryOk(int* required_battery_level) { + // GmsCore enters recovery mode to install package when having enough battery percentage. + // Normally, the threshold is 40% without charger and 20% with charger. So we check the battery + // level against a slightly lower limit. + constexpr int BATTERY_OK_PERCENTAGE = 20; + constexpr int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15; - android::sp health = get_health_service(); - - static constexpr int BATTERY_READ_TIMEOUT_IN_SEC = 10; - int wait_second = 0; - while (true) { - auto charge_status = BatteryStatus::UNKNOWN; - - if (health == nullptr) { - LOG(WARNING) << "no health implementation is found, assuming defaults"; - } else { - health - ->getChargeStatus([&charge_status](auto res, auto out_status) { - if (res == Result::SUCCESS) { - charge_status = out_status; - } - }) - .isOk(); // should not have transport error - } - - // Treat unknown status as charged. - bool charged = (charge_status != BatteryStatus::DISCHARGING && - charge_status != BatteryStatus::NOT_CHARGING); - - Result res = Result::UNKNOWN; - int32_t capacity = INT32_MIN; - if (health != nullptr) { - health - ->getCapacity([&res, &capacity](auto out_res, auto out_capacity) { - res = out_res; - capacity = out_capacity; - }) - .isOk(); // should not have transport error - } - - LOG(INFO) << "charge_status " << toString(charge_status) << ", charged " << charged - << ", status " << toString(res) << ", capacity " << capacity; - // At startup, the battery drivers in devices like N5X/N6P take some time to load - // the battery profile. Before the load finishes, it reports value 50 as a fake - // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected - // to finish loading the battery profile earlier than 10 seconds after kernel startup. - if (res == Result::SUCCESS && capacity == 50) { - if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) { - sleep(1); - wait_second++; - continue; - } - } - // If we can't read battery percentage, it may be a device without battery. In this - // situation, use 100 as a fake battery percentage. - if (res != Result::SUCCESS) { - capacity = 100; - } - - // GmsCore enters recovery mode to install package when having enough battery percentage. - // Normally, the threshold is 40% without charger and 20% with charger. So we should check - // battery with a slightly lower limitation. - static constexpr int BATTERY_OK_PERCENTAGE = 20; - static constexpr int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15; - *required_battery_level = charged ? BATTERY_WITH_CHARGER_OK_PERCENTAGE : BATTERY_OK_PERCENTAGE; - return capacity >= *required_battery_level; - } + auto battery_info = GetBatteryInfo(); + *required_battery_level = + battery_info.charging ? BATTERY_WITH_CHARGER_OK_PERCENTAGE : BATTERY_OK_PERCENTAGE; + return battery_info.capacity >= *required_battery_level; } // Set the retry count to |retry_count| in BCB. @@ -713,12 +656,10 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("battery capacity is not enough for installing package: %d%% needed\n", required_battery_level); - // Log the error code to last_install when installation skips due to - // low battery. + // Log the error code to last_install when installation skips due to low battery. log_failure_code(kLowBattery, update_package); status = INSTALL_SKIPPED; } else if (retry_count == 0 && bootreason_in_blacklist()) { diff --git a/recovery_utils/Android.bp b/recovery_utils/Android.bp index 271d0799..463f27fd 100644 --- a/recovery_utils/Android.bp +++ b/recovery_utils/Android.bp @@ -12,6 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. +cc_defaults { + name: "librecovery_utils_defaults", + + defaults: [ + "recovery_defaults", + ], + + shared_libs: [ + "android.hardware.health@2.0", + "libbase", + "libext4_utils", + "libfs_mgr", + "libhidlbase", + "libselinux", + "libutils", + ], + + static_libs: [ + "libotautil", + + // External dependencies. + "libfstab", + "libhealthhalutils", + ], +} + // A utility lib that's local to recovery (in contrast, libotautil is exposed to device-specific // recovery_ui lib as well as device-specific updater). cc_library_static { @@ -20,23 +46,17 @@ cc_library_static { recovery_available: true, defaults: [ - "recovery_defaults", + "librecovery_utils_defaults", ], srcs: [ + "battery_utils.cpp", "logging.cpp", "parse_install_logs.cpp", "roots.cpp", "thermalutil.cpp", ], - shared_libs: [ - "libbase", - "libext4_utils", - "libfs_mgr", - "libselinux", - ], - export_include_dirs: [ "include", ], @@ -45,13 +65,6 @@ cc_library_static { "system/vold", ], - static_libs: [ - "libotautil", - - // external dependency - "libfstab", - ], - export_static_lib_headers: [ "libfstab", ], diff --git a/recovery_utils/battery_utils.cpp b/recovery_utils/battery_utils.cpp new file mode 100644 index 00000000..323f5253 --- /dev/null +++ b/recovery_utils/battery_utils.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "recovery_utils/battery_utils.h" + +#include +#include + +#include +#include + +BatteryInfo GetBatteryInfo() { + using android::hardware::health::V1_0::BatteryStatus; + using android::hardware::health::V2_0::get_health_service; + using android::hardware::health::V2_0::IHealth; + using android::hardware::health::V2_0::Result; + using android::hardware::health::V2_0::toString; + + android::sp health = get_health_service(); + + int wait_second = 0; + while (true) { + auto charge_status = BatteryStatus::UNKNOWN; + + if (health == nullptr) { + LOG(WARNING) << "No health implementation is found; assuming defaults"; + } else { + health + ->getChargeStatus([&charge_status](auto res, auto out_status) { + if (res == Result::SUCCESS) { + charge_status = out_status; + } + }) + .isOk(); // should not have transport error + } + + // Treat unknown status as on charger. See hardware/interfaces/health/1.0/types.hal for the + // meaning of the return values. + bool charging = (charge_status != BatteryStatus::DISCHARGING && + charge_status != BatteryStatus::NOT_CHARGING); + + Result res = Result::UNKNOWN; + int32_t capacity = INT32_MIN; + if (health != nullptr) { + health + ->getCapacity([&res, &capacity](auto out_res, auto out_capacity) { + res = out_res; + capacity = out_capacity; + }) + .isOk(); // should not have transport error + } + + LOG(INFO) << "charge_status " << toString(charge_status) << ", charging " << charging + << ", status " << toString(res) << ", capacity " << capacity; + + constexpr int BATTERY_READ_TIMEOUT_IN_SEC = 10; + // At startup, the battery drivers in devices like N5X/N6P take some time to load + // the battery profile. Before the load finishes, it reports value 50 as a fake + // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected + // to finish loading the battery profile earlier than 10 seconds after kernel startup. + if (res == Result::SUCCESS && capacity == 50) { + if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) { + sleep(1); + wait_second++; + continue; + } + } + // If we can't read battery percentage, it may be a device without battery. In this + // situation, use 100 as a fake battery percentage. + if (res != Result::SUCCESS) { + capacity = 100; + } + + return BatteryInfo{ charging, capacity }; + } +} diff --git a/recovery_utils/include/recovery_utils/battery_utils.h b/recovery_utils/include/recovery_utils/battery_utils.h new file mode 100644 index 00000000..a95f71dc --- /dev/null +++ b/recovery_utils/include/recovery_utils/battery_utils.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +struct BatteryInfo { + // Whether the device is on charger. Note that the value will be `true` if the battery status is + // unknown (BATTERY_STATUS_UNKNOWN). + bool charging; + + // The remaining battery capacity percentage (i.e. between 0 and 100). See getCapacity in + // hardware/interfaces/health/2.0/IHealth.hal. Returns 100 in case it fails to read a value from + // the health HAL. + int32_t capacity; +}; + +// Returns the battery status for OTA installation purpose. +BatteryInfo GetBatteryInfo(); diff --git a/tests/unit/battery_utils_test.cpp b/tests/unit/battery_utils_test.cpp new file mode 100644 index 00000000..55639fdb --- /dev/null +++ b/tests/unit/battery_utils_test.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agree to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "recovery_utils/battery_utils.h" + +TEST(BatteryInfoTest, GetBatteryInfo) { + auto info = GetBatteryInfo(); + // 0 <= capacity <= 100 + ASSERT_LE(0, info.capacity); + ASSERT_LE(info.capacity, 100); +} From 640175e9d1de2386693d33f7b0eed8db77166146 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 1 Oct 2019 23:24:30 -0700 Subject: [PATCH 104/171] recovery_utils: Use libvoid_headers. Test: mmm bootable/recovery Change-Id: I2ad7620d0f967cb018a415358379c34804e232c5 --- recovery_utils/Android.bp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/recovery_utils/Android.bp b/recovery_utils/Android.bp index 463f27fd..1f422175 100644 --- a/recovery_utils/Android.bp +++ b/recovery_utils/Android.bp @@ -57,15 +57,16 @@ cc_library_static { "thermalutil.cpp", ], + header_libs: [ + "libvold_headers", + ], + export_include_dirs: [ "include", ], - include_dirs: [ - "system/vold", - ], - export_static_lib_headers: [ + // roots.h includes . "libfstab", ], From 341644d657d84de0b7a2299d311503ea71418af3 Mon Sep 17 00:00:00 2001 From: Bill Peckham Date: Tue, 17 Sep 2019 17:11:50 -0700 Subject: [PATCH 105/171] Moving recovery resources from /system to /vendor This change is part of a topic that moves the recovery resources from the system partition to the vendor partition, if it exists, or the vendor directory on the system partition otherwise. The recovery resources are moving from the system image to the vendor partition so that a single system image may be used with either an A/B or a non-A/B vendor image. The topic removes a delta in the system image that prevented such reuse in the past. The recovery resources that are moving are involved with updating the recovery partition after an update. In a non-A/B configuration, the system boots from the recovery partition, updates the other partitions (system, vendor, etc.) Then, the next time the system boots normally, a script updates the recovery partition (if necessary). This script, the executables it invokes, and the data files that it uses were previously on the system partition. The resources that are moving include the following. * install-recovery.sh * applypatch * recovery-resource.dat (if present) * recovery-from-boot.p (if present) This makes the applypatch executable a vendor module. This change supports making dependencies of the applypatch executable available to applypatch, which is now on vendor. Since install-recovery.sh is now a vendor service, we add the applypatch/vendor_flash_recovery.rc file to /vendor/etc/init to start the service. Bug: 68319577 Test: Ensure that recovery partition is updated correctly. Change-Id: I01c0800ee6078aa6c9d716d5f154ad2d63c7af84 --- CleanSpec.mk | 8 ++++++++ applypatch/Android.bp | 7 +++++++ applypatch/applypatch.cpp | 2 +- applypatch/vendor_flash_recovery.rc | 3 +++ edify/Android.bp | 1 + otautil/Android.bp | 1 + 6 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 applypatch/vendor_flash_recovery.rc diff --git a/CleanSpec.mk b/CleanSpec.mk index 0980a35f..d4e9e437 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -61,6 +61,14 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUpdaterSample $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libbrotli.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libbz.so) +# Move recovery resources from /system to /vendor. +$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/applypatch) +$(call add-clean-step, rm -r $(PRODUCT_OUT)/symbols/system/bin/applypatch) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/bin/applypatch) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/bin/install-recovery.sh) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/etc/recovery-resource.dat) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/recovery-from-boot.p) + # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/applypatch/Android.bp b/applypatch/Android.bp index 55d18520..13a96258 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -31,6 +31,7 @@ cc_library_static { name: "libapplypatch", host_supported: true, + vendor_available: true, defaults: [ "applypatch_defaults", @@ -69,6 +70,7 @@ cc_library_static { cc_library_static { name: "libapplypatch_modes", + vendor_available: true, defaults: [ "applypatch_defaults", @@ -92,6 +94,7 @@ cc_library_static { cc_binary { name: "applypatch", + vendor: true, defaults: [ "applypatch_defaults", @@ -120,6 +123,10 @@ cc_binary { "libz", "libziparchive", ], + + init_rc: [ + "vendor_flash_recovery.rc", + ], } cc_library_host_static { diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 336860cb..adda6976 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -279,7 +279,7 @@ bool PatchPartition(const Partition& target, const Partition& source, const Valu } FileContents source_file; - if (ReadPartitionToBuffer(source, &source_file, true)) { + if (ReadPartitionToBuffer(source, &source_file, backup_source)) { return GenerateTarget(target, source_file, patch, bonus, backup_source); } diff --git a/applypatch/vendor_flash_recovery.rc b/applypatch/vendor_flash_recovery.rc new file mode 100644 index 00000000..37a7c2be --- /dev/null +++ b/applypatch/vendor_flash_recovery.rc @@ -0,0 +1,3 @@ +service vendor_flash_recovery /vendor/bin/install-recovery.sh + class main + oneshot diff --git a/edify/Android.bp b/edify/Android.bp index 42947eb4..73048d21 100644 --- a/edify/Android.bp +++ b/edify/Android.bp @@ -16,6 +16,7 @@ cc_library_static { name: "libedify", host_supported: true, + vendor_available: true, srcs: [ "expr.cpp", diff --git a/otautil/Android.bp b/otautil/Android.bp index c8f97468..3b3f9cbc 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -16,6 +16,7 @@ cc_library_static { name: "libotautil", host_supported: true, + vendor_available: true, recovery_available: true, defaults: [ From b548bea77884649e84cdaaf6765dfd70c7d43ab1 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 30 Sep 2019 16:16:07 -0700 Subject: [PATCH 106/171] minadbd: Return battery level via getprop. `adb rescue getprop rescue.battery_level` returns the current battery level. Bug: 134560109 Test: Build and boot into rescue mode. Test: `adb rescue getprop ro.build.fingerprint` Test: `adb rescue getprop rescue.battery_level` Test: `adb rescue getprop` to dump all the properties. Test: Run recovery_unit_test. Change-Id: I78a9e8ab9783ffc8532cb93e6a64fb2157c19bd5 --- minadbd/Android.bp | 4 ++++ minadbd/minadbd_services.cpp | 18 ++++++++++++++++-- recovery_utils/Android.bp | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/minadbd/Android.bp b/minadbd/Android.bp index 07171259..b7075e67 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -40,6 +40,7 @@ cc_library { defaults: [ "minadbd_defaults", + "librecovery_utils_defaults", ], srcs: [ @@ -48,6 +49,7 @@ cc_library { ], static_libs: [ + "librecovery_utils", "libotautil", ], @@ -97,6 +99,7 @@ cc_test { defaults: [ "minadbd_defaults", + "librecovery_utils_defaults", ], srcs: [ @@ -107,6 +110,7 @@ cc_test { static_libs: [ "libminadbd_services", "libfusesideload", + "librecovery_utils", "libotautil", "libadbd", ], diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index cabcdaa0..eb91fb3e 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -44,6 +44,7 @@ #include "fuse_adb_provider.h" #include "fuse_sideload.h" #include "minadbd/types.h" +#include "recovery_utils/battery_utils.h" #include "services.h" #include "sysdeps.h" @@ -160,7 +161,10 @@ static void RescueInstallHostService(unique_fd sfd, const std::string& args) { // If given an empty string, dumps all the supported properties (analogous to `adb shell getprop`) // in lines, e.g. "[prop]: [value]". static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { + constexpr const char* kRescueBatteryLevelProp = "rescue.battery_level"; static const std::set kGetpropAllowedProps = { + // clang-format off + kRescueBatteryLevelProp, "ro.build.date.utc", "ro.build.fingerprint", "ro.build.flavor", @@ -170,18 +174,28 @@ static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { "ro.build.version.incremental", "ro.product.device", "ro.product.vendor.device", + // clang-format on }; + + auto query_prop = [](const std::string& key) { + if (key == kRescueBatteryLevelProp) { + auto battery_info = GetBatteryInfo(); + return std::to_string(battery_info.capacity); + } + return android::base::GetProperty(key, ""); + }; + std::string result; if (prop.empty()) { for (const auto& key : kGetpropAllowedProps) { - auto value = android::base::GetProperty(key, ""); + auto value = query_prop(key); if (value.empty()) { continue; } result += "[" + key + "]: [" + value + "]\n"; } } else if (kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end()) { - result = android::base::GetProperty(prop, "") + "\n"; + result = query_prop(prop) + "\n"; } if (result.empty()) { result = "\n"; diff --git a/recovery_utils/Android.bp b/recovery_utils/Android.bp index 1f422175..bf79a2e8 100644 --- a/recovery_utils/Android.bp +++ b/recovery_utils/Android.bp @@ -75,6 +75,7 @@ cc_library_static { visibility: [ "//bootable/recovery", "//bootable/recovery/install", + "//bootable/recovery/minadbd", "//bootable/recovery/tests", ], } From 97692467a1e9f04bf866c8fb5290add7db554dba Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Sat, 5 Oct 2019 16:59:15 -0700 Subject: [PATCH 107/171] Run BORINGSSL_self_test() in updater_main We need to run the these tests when starting updater to verify the statically linked libcrypto. The test function is based on the known answer tests, and it doesn't compute the hash of the libcrypto library. Bug: 141003171 Test: unit tests pass, run a updater on cuttlefish Change-Id: I897918a54bca76ea0c928102e7287df27505e1cc --- updater/updater_main.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/updater/updater_main.cpp b/updater/updater_main.cpp index 055a8ac7..33d5b5b4 100644 --- a/updater/updater_main.cpp +++ b/updater/updater_main.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -56,22 +57,28 @@ int main(int argc, char** argv) { // (which is redirected to recovery.log). android::base::InitLogging(argv, &UpdaterLogger); + // Run the libcrypto KAT(known answer tests) based self tests. + if (BORINGSSL_self_test() != 1) { + LOG(ERROR) << "Failed to run the boringssl self tests"; + return EXIT_FAILURE; + } + if (argc != 4 && argc != 5) { LOG(ERROR) << "unexpected number of arguments: " << argc; - return 1; + return EXIT_FAILURE; } char* version = argv[1]; if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') { // We support version 1, 2, or 3. LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1]; - return 1; + return EXIT_FAILURE; } int fd; if (!android::base::ParseInt(argv[2], &fd)) { LOG(ERROR) << "Failed to parse fd in " << argv[2]; - return 1; + return EXIT_FAILURE; } std::string package_name = argv[3]; @@ -82,7 +89,7 @@ int main(int argc, char** argv) { is_retry = true; } else { LOG(ERROR) << "unexpected argument: " << argv[4]; - return 1; + return EXIT_FAILURE; } } @@ -98,12 +105,12 @@ int main(int argc, char** argv) { Updater updater(std::make_unique(sehandle)); if (!updater.Init(fd, package_name, is_retry)) { - return 1; + return EXIT_FAILURE; } if (!updater.RunUpdate()) { - return 1; + return EXIT_FAILURE; } - return 0; + return EXIT_SUCCESS; } \ No newline at end of file From 7d47aebd796cd8a2c35829e445c2ed239d573559 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 8 Oct 2019 20:32:42 -0700 Subject: [PATCH 108/171] Update OWNERS. Test: N/A Change-Id: I290fe43ee14fa3751f9ddb2c009bff235d8e1cff --- OWNERS | 3 ++- updater_sample/OWNERS | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 updater_sample/OWNERS diff --git a/OWNERS b/OWNERS index b3f11dcd..fe1c33d4 100644 --- a/OWNERS +++ b/OWNERS @@ -1,3 +1,4 @@ enh@google.com -tbao@google.com +nhdo@google.com xunchang@google.com +zhaojiac@google.com diff --git a/updater_sample/OWNERS b/updater_sample/OWNERS deleted file mode 100644 index 5c1c3706..00000000 --- a/updater_sample/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -zhaojiac@google.com -zhomart@google.com From 1cb510d96fce332570eb0d139eab7aebdc039e11 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Thu, 10 Oct 2019 16:33:58 -0700 Subject: [PATCH 109/171] [bootable][recovery] fix -Wreorder-init-list C++20 will require members in a designated initializer to be in order unlike C99. Bug: 139945549 Test: mm Change-Id: I6f8d658448f7e5dd980bf95b890b15cb0aab7407 Signed-off-by: Nick Desaulniers --- recovery_utils/roots.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/recovery_utils/roots.cpp b/recovery_utils/roots.cpp index f717ec20..fe3a07aa 100644 --- a/recovery_utils/roots.cpp +++ b/recovery_utils/roots.cpp @@ -54,7 +54,11 @@ void load_volume_table() { } fstab.emplace_back(FstabEntry{ - .mount_point = "/tmp", .fs_type = "ramdisk", .blk_device = "ramdisk", .length = 0 }); + .blk_device = "ramdisk", + .mount_point = "/tmp", + .fs_type = "ramdisk", + .length = 0, + }); std::cout << "recovery filesystem table" << std::endl << "=========================" << std::endl; for (size_t i = 0; i < fstab.size(); ++i) { From 58a27693b22da9b93c634d053a53deb4c4a71e4e Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 6 Aug 2019 12:32:05 -0700 Subject: [PATCH 110/171] Force package installation with FUSE unless the package stores on device The non-A/B package installation is subject to TOC/TOU flaw if the attacker can switch the package in the middle of installation. And the most pratical case is to store the package on an external device, e.g. a sdcard, and swap the device in the middle. To prevent that, we can adopt the same protection as used in sideloading a package with FUSE. Specifically, when we install the package with FUSE, we read the entire package to cryptographically verify its signature. The hash for each transfer block is recorded in the memory (TOC), and the subsequent reads (TOU) will be rejected upon dectecting a mismatch. This CL forces the package installation with FUSE when the package stays on a removable media. Bug: 136498130 Test: Run bin/recovery --update_package with various paths; and packages are installed from FUSE as expected Change-Id: Ibc9b095036a2fa624e8edf6c347ed4f12aef072f --- Android.bp | 1 + install/include/install/install.h | 4 +++ install/install.cpp | 47 +++++++++++++++++++++++++++++++ recovery.cpp | 12 ++++---- tests/Android.bp | 1 + tests/unit/install_test.cpp | 28 ++++++++++++++++++ 6 files changed, 86 insertions(+), 7 deletions(-) diff --git a/Android.bp b/Android.bp index 0759e08d..a9a08a1a 100644 --- a/Android.bp +++ b/Android.bp @@ -69,6 +69,7 @@ cc_defaults { ], static_libs: [ + "libc++fs", "libinstall", "librecovery_fastboot", "libminui", diff --git a/install/include/install/install.h b/install/include/install/install.h index b4b3a914..c3331c8b 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -67,3 +67,7 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip); // pre-device and serial number (if presents). A/B OTA specific checks: pre-build version, // fingerprint, timestamp. bool CheckPackageMetadata(const std::map& metadata, OtaType ota_type); + +// Ensures the path to the update package is mounted. Also set the |should_use_fuse| to true if the +// package stays on a removable media. +bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse); diff --git a/install/install.cpp b/install/install.cpp index 9166f9cf..43916b3d 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -722,3 +723,49 @@ bool verify_package(Package* package, RecoveryUI* ui) { } return true; } + +bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse) { + CHECK(should_use_fuse != nullptr); + + if (package_path.empty()) { + return false; + } + + *should_use_fuse = true; + if (package_path[0] == '@') { + auto block_map_path = package_path.substr(1); + if (ensure_path_mounted(block_map_path) != 0) { + LOG(ERROR) << "Failed to mount " << block_map_path; + return false; + } + // uncrypt only produces block map only if the package stays on /data. + *should_use_fuse = false; + return true; + } + + // Package is not a block map file. + if (ensure_path_mounted(package_path) != 0) { + LOG(ERROR) << "Failed to mount " << package_path; + return false; + } + + // Reject the package if the input path doesn't equal the canonicalized path. + // e.g. /cache/../sdcard/update_package. + std::error_code ec; + auto canonical_path = std::filesystem::canonical(package_path, ec); + if (ec) { + LOG(ERROR) << "Failed to get canonical of " << package_path << ", " << ec.message(); + return false; + } + if (canonical_path.string() != package_path) { + LOG(ERROR) << "Installation aborts. The canonical path " << canonical_path.string() + << " doesn't equal the original path " << package_path; + return false; + } + + constexpr const char* CACHE_ROOT = "/cache"; + if (android::base::StartsWith(package_path, CACHE_ROOT)) { + *should_use_fuse = false; + } + return true; +} diff --git a/recovery.cpp b/recovery.cpp index 682ddbc4..8f8f7dc0 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -733,13 +733,11 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector& file_map, int fd, int compression_type) { @@ -595,3 +596,30 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { "\n"); TestCheckPackageMetadata(metadata, OtaType::AB, true); } + +TEST(InstallTest, SetupPackageMount_package_path) { + load_volume_table(); + bool install_with_fuse; + + // Setup should fail if the input path doesn't exist. + ASSERT_FALSE(SetupPackageMount("/does_not_exist", &install_with_fuse)); + + // Package should be installed with fuse if it's not in /cache. + TemporaryDir temp_dir; + TemporaryFile update_package(temp_dir.path); + ASSERT_TRUE(SetupPackageMount(update_package.path, &install_with_fuse)); + ASSERT_TRUE(install_with_fuse); + + // Setup should fail if the input path isn't canonicalized. + std::string uncanonical_package_path = android::base::Join( + std::vector{ + temp_dir.path, + "..", + android::base::Basename(temp_dir.path), + android::base::Basename(update_package.path), + }, + '/'); + + ASSERT_EQ(0, access(uncanonical_package_path.c_str(), R_OK)); + ASSERT_FALSE(SetupPackageMount(uncanonical_package_path, &install_with_fuse)); +} From 1b03fc549311f66e14d789c734b609ccca9a1fd3 Mon Sep 17 00:00:00 2001 From: George Burgess IV Date: Wed, 23 Oct 2019 12:09:07 -0700 Subject: [PATCH 111/171] bootable: leak less memory `misc_device_` is a std::string, so it allocates and manages its own memory. Hence, the strdup here is immediately leaked. Caught by the static analyzer Bug: None Test: TreeHugger Change-Id: Iffb1ff60f6087e470a0979d202150567272e8b1c --- boot_control/libboot_control.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/boot_control/libboot_control.cpp b/boot_control/libboot_control.cpp index e3bff9ff..ff4eaabf 100644 --- a/boot_control/libboot_control.cpp +++ b/boot_control/libboot_control.cpp @@ -218,7 +218,8 @@ bool BootControl::Init() { } // Note that since there isn't a module unload function this memory is leaked. - misc_device_ = strdup(device.c_str()); + // We use `device` below sometimes, so it's not moved out of here. + misc_device_ = device; initialized_ = true; // Validate the loaded data, otherwise we will destroy it and re-initialize it From a91e456fd491190a59588e3fcab701f150ba9f4d Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Tue, 22 Oct 2019 16:10:48 -0700 Subject: [PATCH 112/171] Use adbd_system_binaries to track adbd's dependencies. required doesn't propagate from apexes, so we need a separate phony target to track adbd's dependenecies. Test: m Change-Id: I13977d1376de63839bf182d2cfa56b5c6c63aba9 --- minadbd/Android.bp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/minadbd/Android.bp b/minadbd/Android.bp index b7075e67..c39c7344 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -91,6 +91,10 @@ cc_binary { "libcrypto", "libminadbd_services", ], + + required: [ + "adbd_system_binaries_recovery", + ] } cc_test { From 7d5c341962e9721bdeecdb36c37511e1d8c25e23 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 29 Oct 2019 21:44:39 -0700 Subject: [PATCH 113/171] Start adbd in user mode if bootloader is unlocked During automatic tests, we sometimes want to reboot the device out of the rescue party remotely. And per http://go/recovery-adb-access, one option is to start adbd in user build if the device has an unlocked bootloader. This should not add more surface of attack. Because verified boot is off with the unlocked bootloader, and the user can always flash a custom recovery image that always starts adbd. Bug: 141247819 Test: check adbd doesn't start in user build, unlock bootloader, and check adbd starts. Change-Id: I851746245f862cb4dfb01e6c3ad035f2c9f9ccec --- etc/init.rc | 4 ++++ recovery_main.cpp | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/etc/init.rc b/etc/init.rc index 0822aba0..9786f610 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -99,6 +99,10 @@ on property:service.adb.root=1 on fs && property:ro.debuggable=1 setprop sys.usb.config adb +# Also start adbd on user build with an unlocked bootloader +on fs && property:ro.debuggable=0 && androidboot.verifiedbootstate=orange + setprop sys.usb.config adb + on fs && property:sys.usb.configfs=1 mount configfs none /config mkdir /config/usb_gadget/g1 0770 shell shell diff --git a/recovery_main.cpp b/recovery_main.cpp index 89253dcd..30a1fc0a 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -69,6 +69,10 @@ static bool IsRoDebuggable() { return android::base::GetBoolProperty("ro.debuggable", false); } +static bool IsDeviceUnlocked() { + return "orange" == android::base::GetProperty("ro.boot.verifiedbootstate", ""); +} + static void UiLogger(android::base::LogId /* id */, android::base::LogSeverity severity, const char* /* tag */, const char* /* file */, unsigned int /* line */, const char* message) { @@ -463,7 +467,9 @@ int main(int argc, char** argv) { listener_thread.detach(); while (true) { - std::string usb_config = fastboot ? "fastboot" : IsRoDebuggable() ? "adb" : "none"; + // We start adbd in recovery for the device with userdebug build or a unlocked bootloader. + std::string usb_config = + fastboot ? "fastboot" : IsRoDebuggable() || IsDeviceUnlocked() ? "adb" : "none"; std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); if (usb_config != usb_state) { if (!SetUsbConfig("none")) { From 4ff4cbdd6c8199e13c86bf4674b7863e1394dee5 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 4 Nov 2019 13:09:38 -0800 Subject: [PATCH 114/171] bootloader_message: Carve out space reserved for system. This reduces the wipe space from 32K to 16K. The wipe space is now at the 16K-32K region. The 32K-64K region is now "system space", to complement the vendor space, for generic AOSP usage. Bug: 139156011 Test: manual test Change-Id: I1474bfa65a5f21049ab64ec0aee2f4585b55f60f --- bootloader_message/bootloader_message.cpp | 7 +++++++ .../include/bootloader_message/bootloader_message.h | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index b15a9b9f..f838930f 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -250,6 +250,13 @@ bool write_wipe_package(const std::string& package_data, std::string* err) { if (misc_blk_device.empty()) { return false; } + static constexpr size_t kMaximumWipePackageSize = + SYSTEM_SPACE_OFFSET_IN_MISC - WIPE_PACKAGE_OFFSET_IN_MISC; + if (package_data.size() > kMaximumWipePackageSize) { + *err = "Wipe package size " + std::to_string(package_data.size()) + " exceeds " + + std::to_string(kMaximumWipePackageSize) + " bytes"; + return false; + } return write_misc_partition(package_data.data(), package_data.size(), misc_blk_device, WIPE_PACKAGE_OFFSET_IN_MISC, err); } diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index b7878308..e3425fc8 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -25,12 +25,15 @@ // 0 - 2K For bootloader_message // 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used // as bootloader_message_ab struct) -// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices +// 16K - 32K Used by uncrypt and recovery to store wipe_package for A/B devices +// 32K - 64K System space, used for miscellanious AOSP features. See below. // Note that these offsets are admitted by bootloader,recovery and uncrypt, so they // are not configurable without changing all of them. constexpr size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0; constexpr size_t VENDOR_SPACE_OFFSET_IN_MISC = 2 * 1024; constexpr size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024; +constexpr size_t SYSTEM_SPACE_OFFSET_IN_MISC = 32 * 1024; +constexpr size_t SYSTEM_SPACE_SIZE_IN_MISC = 32 * 1024; /* Bootloader Message (2-KiB) * From 0a19ef8cb67bd9ced4ba6b1e83d560056a807bac Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Wed, 6 Nov 2019 16:59:39 -0800 Subject: [PATCH 115/171] Stop setting usb config from recovery init script We used to set sys.usb.config to adb in the init script. And the purpose is to start adbd. This is a duplicate of code because we always check and reset the usb config in recovery_main. Test: check adbd starts Change-Id: I6e2842ff8aebf6ccf3bd3f2ae85323899a2b9de4 --- etc/init.rc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/etc/init.rc b/etc/init.rc index 9786f610..d5b056ae 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -95,14 +95,6 @@ service fastbootd /system/bin/fastbootd on property:service.adb.root=1 restart adbd -# Always start adbd on userdebug and eng builds -on fs && property:ro.debuggable=1 - setprop sys.usb.config adb - -# Also start adbd on user build with an unlocked bootloader -on fs && property:ro.debuggable=0 && androidboot.verifiedbootstate=orange - setprop sys.usb.config adb - on fs && property:sys.usb.configfs=1 mount configfs none /config mkdir /config/usb_gadget/g1 0770 shell shell From bcd3f35462b9d042feae0e9b42a8c09153155bc5 Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Mon, 4 Nov 2019 15:17:17 -0800 Subject: [PATCH 116/171] Move init and ueventd scripts from / to /system/etc There is no reason for these scripts to continue to exist in /, when they are better suited for /system/etc. There are problems keeping them at / as well, particularly that they cannot be updated with overlayfs. Bug: 131087886 Bug: 140313207 Test: build/boot + boot to recovery Merged-In: I1fb6690d4302a1884d8521c21a9754b2ca710d5a Change-Id: I1fb6690d4302a1884d8521c21a9754b2ca710d5a --- Android.bp | 10 ++++++++++ etc/init.rc | 9 --------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Android.bp b/Android.bp index 45aafb04..d78c3fa9 100644 --- a/Android.bp +++ b/Android.bp @@ -94,6 +94,14 @@ cc_library_static { ], } +prebuilt_etc { + name: "init_recovery.rc", + filename: "init.rc", + src: "etc/init.rc", + sub_dir: "init/hw", + recovery: true, +} + cc_binary { name: "recovery", recovery: true, @@ -119,11 +127,13 @@ cc_binary { required: [ "e2fsdroid.recovery", + "init_recovery.rc", "librecovery_ui_ext", "minadbd", "mke2fs.conf.recovery", "mke2fs.recovery", "recovery_deps", + "ueventd.rc.recovery", ], } diff --git a/etc/init.rc b/etc/init.rc index d5b056ae..c54de5af 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -44,10 +44,6 @@ on boot class_start default -# Load properties from /system/ + /factory after fs mount. -on load_system_props_action - load_system_props - on firmware_mounts_complete rm /dev/.booting @@ -58,11 +54,6 @@ on late-init trigger post-fs trigger post-fs-data - # Load properties from /system/ + /factory after fs mount. Place - # this in another action so that the load will be scheduled after the prior - # issued fs triggers have completed. - trigger load_system_props_action - # Remove a file to wake up anything waiting for firmware trigger firmware_mounts_complete From 24dd3146e17294cab81bc1e79baed2298de1e15a Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Mon, 4 Nov 2019 15:17:17 -0800 Subject: [PATCH 117/171] Move init and ueventd scripts from / to /system/etc There is no reason for these scripts to continue to exist in /, when they are better suited for /system/etc. There are problems keeping them at / as well, particularly that they cannot be updated with overlayfs. Bug: 131087886 Bug: 140313207 Test: build/boot + boot to recovery Merged-In: I1fb6690d4302a1884d8521c21a9754b2ca710d5a Change-Id: I1fb6690d4302a1884d8521c21a9754b2ca710d5a --- Android.bp | 10 ++++++++++ etc/init.rc | 9 --------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Android.bp b/Android.bp index 45aafb04..d78c3fa9 100644 --- a/Android.bp +++ b/Android.bp @@ -94,6 +94,14 @@ cc_library_static { ], } +prebuilt_etc { + name: "init_recovery.rc", + filename: "init.rc", + src: "etc/init.rc", + sub_dir: "init/hw", + recovery: true, +} + cc_binary { name: "recovery", recovery: true, @@ -119,11 +127,13 @@ cc_binary { required: [ "e2fsdroid.recovery", + "init_recovery.rc", "librecovery_ui_ext", "minadbd", "mke2fs.conf.recovery", "mke2fs.recovery", "recovery_deps", + "ueventd.rc.recovery", ], } diff --git a/etc/init.rc b/etc/init.rc index d5b056ae..c54de5af 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -44,10 +44,6 @@ on boot class_start default -# Load properties from /system/ + /factory after fs mount. -on load_system_props_action - load_system_props - on firmware_mounts_complete rm /dev/.booting @@ -58,11 +54,6 @@ on late-init trigger post-fs trigger post-fs-data - # Load properties from /system/ + /factory after fs mount. Place - # this in another action so that the load will be scheduled after the prior - # issued fs triggers have completed. - trigger load_system_props_action - # Remove a file to wake up anything waiting for firmware trigger firmware_mounts_complete From cf8427af89f47075953dad10d6ea9b848736527f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 4 Nov 2019 14:08:11 -0800 Subject: [PATCH 118/171] bootloader_message: Add helpers for handling IBootControl MergeStatus. Move merge_status from bootloader_control_ab, which is in vendor space, to a new generic AOSP struct in system space. This will allow more devices to share the same HAL implementation. This patch also changes libboot_control to compensate for merge_status moving out of vendor space. The reference HAL library now also provides separate helper functions for managing the merge status, so devices using a custom boot control HAL can still take advantage of the new misc implementation. Bug: 139156011 Test: manual test Change-Id: I5cd824e25f9d07aad1476301def5cdc3f506b029 --- .../include/libboot_control/libboot_control.h | 23 ++++++ boot_control/libboot_control.cpp | 80 ++++++++++++++++--- bootloader_message/bootloader_message.cpp | 43 ++++++++++ .../bootloader_message/bootloader_message.h | 26 ++++++ 4 files changed, 163 insertions(+), 9 deletions(-) diff --git a/boot_control/include/libboot_control/libboot_control.h b/boot_control/include/libboot_control/libboot_control.h index 34a9affe..54686588 100644 --- a/boot_control/include/libboot_control/libboot_control.h +++ b/boot_control/include/libboot_control/libboot_control.h @@ -62,5 +62,28 @@ class BootControl { unsigned int current_slot_ = 0; }; +// Helper functions to write the Virtual A/B merge status message. These are +// separate because BootControl uses bootloader_control_ab in vendor space, +// whereas the Virtual A/B merge status is in system space. A HAL might not +// use bootloader_control_ab, but may want to use the AOSP method of maintaining +// the merge status. + +// If the Virtual A/B message has not yet been initialized, then initialize it. +// This should be called when the BootControl HAL first loads. +// +// If the Virtual A/B message in misc was already initialized, true is returned. +// If initialization was attempted, but failed, false is returned, and the HAL +// should fail to load. +bool InitMiscVirtualAbMessageIfNeeded(); + +// Save the current merge status as well as the current slot. +bool SetMiscVirtualAbMergeStatus(unsigned int current_slot, + android::hardware::boot::V1_1::MergeStatus status); + +// Return the current merge status. If the saved status is SNAPSHOTTED but the +// slot hasn't changed, the status returned will be NONE. +bool GetMiscVirtualAbMergeStatus(unsigned int current_slot, + android::hardware::boot::V1_1::MergeStatus* status); + } // namespace bootable } // namespace android diff --git a/boot_control/libboot_control.cpp b/boot_control/libboot_control.cpp index ff4eaabf..70218397 100644 --- a/boot_control/libboot_control.cpp +++ b/boot_control/libboot_control.cpp @@ -232,6 +232,10 @@ bool BootControl::Init() { UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl); } + if (!InitMiscVirtualAbMessageIfNeeded()) { + return false; + } + num_slots_ = boot_ctrl.nb_slot; return true; } @@ -335,18 +339,15 @@ bool BootControl::IsValidSlot(unsigned int slot) { } bool BootControl::SetSnapshotMergeStatus(MergeStatus status) { - bootloader_control bootctrl; - if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; - - bootctrl.merge_status = (unsigned int)status; - return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl); + return SetMiscVirtualAbMergeStatus(current_slot_, status); } MergeStatus BootControl::GetSnapshotMergeStatus() { - bootloader_control bootctrl; - if (!LoadBootloaderControl(misc_device_, &bootctrl)) return MergeStatus::UNKNOWN; - - return (MergeStatus)bootctrl.merge_status; + MergeStatus status; + if (!GetMiscVirtualAbMergeStatus(current_slot_, &status)) { + return MergeStatus::UNKNOWN; + } + return status; } const char* BootControl::GetSuffix(unsigned int slot) { @@ -356,5 +357,66 @@ const char* BootControl::GetSuffix(unsigned int slot) { return kSlotSuffixes[slot]; } +bool InitMiscVirtualAbMessageIfNeeded() { + std::string err; + misc_virtual_ab_message message; + if (!ReadMiscVirtualAbMessage(&message, &err)) { + LOG(ERROR) << "Could not read merge status: " << err; + return false; + } + + if (message.version == MISC_VIRTUAL_AB_MESSAGE_VERSION) { + // Already initialized. + return true; + } + + message = {}; + message.version = MISC_VIRTUAL_AB_MESSAGE_VERSION; + if (!WriteMiscVirtualAbMessage(message, &err)) { + LOG(ERROR) << "Could not write merge status: " << err; + return false; + } + return true; +} + +bool SetMiscVirtualAbMergeStatus(unsigned int current_slot, + android::hardware::boot::V1_1::MergeStatus status) { + std::string err; + misc_virtual_ab_message message; + + if (!ReadMiscVirtualAbMessage(&message, &err)) { + LOG(ERROR) << "Could not read merge status: " << err; + return false; + } + + message.merge_status = static_cast(status); + message.source_slot = current_slot; + if (!WriteMiscVirtualAbMessage(message, &err)) { + LOG(ERROR) << "Could not write merge status: " << err; + return false; + } + return true; +} + +bool GetMiscVirtualAbMergeStatus(unsigned int current_slot, + android::hardware::boot::V1_1::MergeStatus* status) { + std::string err; + misc_virtual_ab_message message; + + if (!ReadMiscVirtualAbMessage(&message, &err)) { + LOG(ERROR) << "Could not read merge status: " << err; + return false; + } + + // If the slot reverted after having created a snapshot, then the snapshot will + // be thrown away at boot. Thus we don't count this as being in a snapshotted + // state. + *status = static_cast(message.merge_status); + if (*status == MergeStatus::SNAPSHOTTED && current_slot == message.source_slot) { + *status = MergeStatus::NONE; + } + return true; +} + } // namespace bootable } // namespace android diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index f838930f..4f7085db 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -292,6 +292,49 @@ bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, err); } +static bool ValidateSystemSpaceRegion(size_t offset, size_t size, std::string* err) { + if (size <= SYSTEM_SPACE_SIZE_IN_MISC && offset <= (SYSTEM_SPACE_SIZE_IN_MISC - size)) { + return true; + } + *err = android::base::StringPrintf("Out of bound access (offset %zu size %zu)", offset, size); + return false; +} + +static bool ReadMiscPartitionSystemSpace(void* data, size_t size, size_t offset, std::string* err) { + if (!ValidateSystemSpaceRegion(offset, size, err)) { + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return read_misc_partition(data, size, misc_blk_device, SYSTEM_SPACE_OFFSET_IN_MISC + offset, + err); +} + +static bool WriteMiscPartitionSystemSpace(const void* data, size_t size, size_t offset, + std::string* err) { + if (!ValidateSystemSpaceRegion(offset, size, err)) { + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return write_misc_partition(data, size, misc_blk_device, SYSTEM_SPACE_OFFSET_IN_MISC + offset, + err); +} + +bool ReadMiscVirtualAbMessage(misc_virtual_ab_message* message, std::string* err) { + return ReadMiscPartitionSystemSpace(message, sizeof(*message), + offsetof(misc_system_space_layout, virtual_ab_message), err); +} + +bool WriteMiscVirtualAbMessage(const misc_virtual_ab_message& message, std::string* err) { + return WriteMiscPartitionSystemSpace(&message, sizeof(message), + offsetof(misc_system_space_layout, virtual_ab_message), err); +} + extern "C" bool write_reboot_bootloader(void) { std::string err; return write_reboot_bootloader(&err); diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index e3425fc8..3a3b862a 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -185,6 +185,28 @@ static_assert(sizeof(struct bootloader_control) == "struct bootloader_control has wrong size"); #endif +// Holds Virtual A/B merge status information. Current version is 1. New fields +// must be added to the end. +struct misc_virtual_ab_message { + uint8_t version; + uint8_t merge_status; // IBootControl 1.1, MergeStatus enum. + uint8_t source_slot; // Slot number when merge_status was written. + uint8_t reserved[61]; +} __attribute__((packed)); + +#define MISC_VIRTUAL_AB_MESSAGE_VERSION 1 + +#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) +static_assert(sizeof(struct misc_virtual_ab_message) == 64, + "struct misc_virtual_ab_message has wrong size"); +#endif + +// This struct is not meant to be used directly, rather, it is to make +// computation of offsets easier. New fields must be added to the end. +struct misc_system_space_layout { + misc_virtual_ab_message virtual_ab_message; +} __attribute__((packed)); + #ifdef __cplusplus #include @@ -247,6 +269,10 @@ bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::s // offset is in relative to the start of the vendor space. bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err); +// Read or write the Virtual A/B message from system space in /misc. +bool ReadMiscVirtualAbMessage(misc_virtual_ab_message* message, std::string* err); +bool WriteMiscVirtualAbMessage(const misc_virtual_ab_message& message, std::string* err); + #else #include From 89d2d050a0c5784b7993eaaa08be21ef93084156 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 15 Oct 2019 13:22:20 -0700 Subject: [PATCH 119/171] Force merges to complete before wiping data or metadata. After an OTA is applied, a wipe in recovery may overwrite components of dynamic partitions living in userdata. If the OTA has not yet begun merging, we mark the current slot unbootable. If the OTA has begun merging, we wait for the merge to complete. This logic is encapsulated in libsnapshot. Bug: 139156011 Test: manual test Change-Id: Id6544a1b8583afcbba11559d46214ec2e68ffa40 --- Android.bp | 5 +++ install/Android.bp | 2 + install/include/install/snapshot_utils.h | 21 ++++++++++ install/snapshot_utils.cpp | 49 ++++++++++++++++++++++++ install/wipe_data.cpp | 7 ++++ 5 files changed, 84 insertions(+) create mode 100644 install/include/install/snapshot_utils.h create mode 100644 install/snapshot_utils.cpp diff --git a/Android.bp b/Android.bp index 45aafb04..7df53a43 100644 --- a/Android.bp +++ b/Android.bp @@ -58,12 +58,16 @@ cc_defaults { ], shared_libs: [ + "android.hardware.boot@1.0", + "android.hardware.boot@1.1", "libbase", "libbootloader_message", "libcrypto", "libcutils", "libfs_mgr", + "liblp", "liblog", + "libprotobuf-cpp-lite", "libziparchive", ], @@ -73,6 +77,7 @@ cc_defaults { "libminui", "librecovery_utils", "libotautil", + "libsnapshot_nobinder", ], } diff --git a/install/Android.bp b/install/Android.bp index d4606e92..be08506e 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -39,6 +39,7 @@ cc_defaults { static_libs: [ "librecovery_utils", "libotautil", + "libsnapshot_nobinder", // external dependencies "libvintf_recovery", @@ -60,6 +61,7 @@ cc_library_static { "fuse_install.cpp", "install.cpp", "package.cpp", + "snapshot_utils.cpp", "verifier.cpp", "wipe_data.cpp", "wipe_device.cpp", diff --git a/install/include/install/snapshot_utils.h b/install/include/install/snapshot_utils.h new file mode 100644 index 00000000..00c3ef7e --- /dev/null +++ b/install/include/install/snapshot_utils.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "recovery_ui/device.h" + +bool FinishPendingSnapshotMerges(Device* device); diff --git a/install/snapshot_utils.cpp b/install/snapshot_utils.cpp new file mode 100644 index 00000000..69da5eea --- /dev/null +++ b/install/snapshot_utils.cpp @@ -0,0 +1,49 @@ + +/* + * 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 +#include + +#include "recovery_ui/device.h" +#include "recovery_ui/ui.h" +#include "recovery_utils/roots.h" + +using android::snapshot::SnapshotManager; + +bool FinishPendingSnapshotMerges(Device* device) { + if (!android::base::GetBoolProperty("ro.virtual_ab.enabled", false)) { + return true; + } + + RecoveryUI* ui = device->GetUI(); + auto sm = SnapshotManager::NewForFirstStageMount(); + if (!sm) { + ui->Print("Could not create SnapshotManager.\n"); + return false; + } + + auto callback = [&]() -> void { + double progress; + sm->GetUpdateState(&progress); + ui->Print("Waiting for merge to complete: %.2f\n", progress); + }; + if (!sm->HandleImminentDataWipe(callback)) { + ui->Print("Unable to check merge status and/or complete update merge.\n"); + return false; + } + return true; +} diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp index 82660bef..28720858 100644 --- a/install/wipe_data.cpp +++ b/install/wipe_data.cpp @@ -27,6 +27,7 @@ #include #include +#include "install/snapshot_utils.h" #include "otautil/dirutil.h" #include "recovery_ui/ui.h" #include "recovery_utils/logging.h" @@ -104,6 +105,12 @@ bool WipeCache(RecoveryUI* ui, const std::function& confirm_func) { bool WipeData(Device* device, bool convert_fbe) { RecoveryUI* ui = device->GetUI(); ui->Print("\n-- Wiping data...\n"); + + if (!FinishPendingSnapshotMerges(device)) { + ui->Print("Unable to check update status or complete merge, cannot wipe partitions.\n"); + return false; + } + bool success = device->PreWipeData(); if (success) { success &= EraseVolume(DATA_ROOT, ui, convert_fbe); From 3d57c84476ce542a7d8d623cf1f208efc1a20026 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Sat, 9 Nov 2019 22:07:20 -0800 Subject: [PATCH 120/171] Consolidate the vendor space misc usage for Pixels The layout of the vendor space /misc partition was pretty confusing and lead to some usage conflicts. To formalize the layout, we create a pixel specific library with the definition & offset of various flags. The new library also handles the R/W. As a result, we will leave system domain /misc definitions in the libbootloader_message. We also switch the misc_writer binary to use more specific options instead of writing an arbitrary hex string. So we can avoid redefining the string & offset in both init script and recovery ui. Bug: 131775112 Test: unit tests pass, run misc_writer and check contents of /misc Change-Id: I00f8842a81d1929e31a1de4d5eb09575ffad47c0 --- bootloader_message/bootloader_message.cpp | 37 +---- .../bootloader_message/bootloader_message.h | 15 +- misc_writer/Android.bp | 84 ++++++++++- misc_writer/include/misc_writer/misc_writer.h | 66 +++++++++ misc_writer/misc_writer.cpp | 127 +++++++--------- misc_writer/misc_writer_main.cpp | 109 ++++++++++++++ misc_writer/misc_writer_test.cpp | 140 ++++++++++++++++++ tests/unit/bootloader_message_test.cpp | 34 ----- 8 files changed, 454 insertions(+), 158 deletions(-) create mode 100644 misc_writer/include/misc_writer/misc_writer.h create mode 100644 misc_writer/misc_writer_main.cpp create mode 100644 misc_writer/misc_writer_test.cpp diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index 4f7085db..b70d54e5 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -45,7 +45,7 @@ void SetMiscBlockDeviceForTest(std::string_view misc_device) { g_misc_device_for_test = misc_device; } -static std::string get_misc_blk_device(std::string* err) { +std::string get_misc_blk_device(std::string* err) { if (g_misc_device_for_test.has_value() && !g_misc_device_for_test->empty()) { return *g_misc_device_for_test; } @@ -111,8 +111,8 @@ static bool read_misc_partition(void* p, size_t size, const std::string& misc_bl return true; } -static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device, - size_t offset, std::string* err) { +bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device, + size_t offset, std::string* err) { android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY)); if (fd == -1) { *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(), @@ -261,37 +261,6 @@ bool write_wipe_package(const std::string& package_data, std::string* err) { WIPE_PACKAGE_OFFSET_IN_MISC, err); } -static bool OffsetAndSizeInVendorSpace(size_t offset, size_t size) { - auto total_size = WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC; - return size <= total_size && offset <= total_size - size; -} - -bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err) { - if (!OffsetAndSizeInVendorSpace(offset, size)) { - *err = android::base::StringPrintf("Out of bound read (offset %zu size %zu)", offset, size); - return false; - } - auto misc_blk_device = get_misc_blk_device(err); - if (misc_blk_device.empty()) { - return false; - } - return read_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, - err); -} - -bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err) { - if (!OffsetAndSizeInVendorSpace(offset, size)) { - *err = android::base::StringPrintf("Out of bound write (offset %zu size %zu)", offset, size); - return false; - } - auto misc_blk_device = get_misc_blk_device(err); - if (misc_blk_device.empty()) { - return false; - } - return write_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, - err); -} - static bool ValidateSystemSpaceRegion(size_t offset, size_t size, std::string* err) { if (size <= SYSTEM_SPACE_SIZE_IN_MISC && offset <= (SYSTEM_SPACE_SIZE_IN_MISC - size)) { return true; diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 3a3b862a..a27e80bc 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -212,11 +212,18 @@ struct misc_system_space_layout { #include #include +// Gets the block device name of /misc partition. +std::string get_misc_blk_device(std::string* err); // Return the block device name for the bootloader message partition and waits // for the device for up to 10 seconds. In case of error returns the empty // string. std::string get_bootloader_message_blk_device(std::string* err); +// Writes |size| bytes of data from buffer |p| to |misc_blk_device| at |offset|. If the write fails, +// sets the error message in |err|. +bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device, + size_t offset, std::string* err); + // Read bootloader message into boot. Error message will be set in err. bool read_bootloader_message(bootloader_message* boot, std::string* err); @@ -261,14 +268,6 @@ bool read_wipe_package(std::string* package_data, size_t size, std::string* err) // Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC). bool write_wipe_package(const std::string& package_data, std::string* err); -// Reads data from the vendor space in /misc partition, with the given offset and size. Note that -// offset is in relative to the start of vendor space. -bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err); - -// Writes the given data to the vendor space in /misc partition, at the given offset. Note that -// offset is in relative to the start of the vendor space. -bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err); - // Read or write the Virtual A/B message from system space in /misc. bool ReadMiscVirtualAbMessage(misc_virtual_ab_message* message, std::string* err); bool WriteMiscVirtualAbMessage(const misc_virtual_ab_message& message, std::string* err); diff --git a/misc_writer/Android.bp b/misc_writer/Android.bp index 567143c7..73c44d2e 100644 --- a/misc_writer/Android.bp +++ b/misc_writer/Android.bp @@ -14,14 +14,9 @@ // limitations under the License. // -cc_binary { - name: "misc_writer", +cc_defaults { + name: "misc_writer_defaults", vendor: true, - - srcs: [ - "misc_writer.cpp", - ], - cpp_std: "experimental", cflags: [ @@ -38,3 +33,78 @@ cc_binary { "libfstab", ], } + +// TODO(xunchang) Remove duplicates after we convert the device specific librecovery_ui to recovery +// module. Then libmisc_writer can build as a vendor module available in recovery. +cc_library_static { + name: "libmisc_writer", + cpp_std: "experimental", + + cflags: [ + "-Wall", + "-Werror", + ], + + shared_libs: [ + "libbase", + ], + + static_libs: [ + "libbootloader_message", + "libfstab", + ], + + srcs: [ + "misc_writer.cpp", + ], + + export_include_dirs: [ + "include", + ], +} + +cc_library_static { + name: "libmisc_writer_vendor", + defaults: [ + "misc_writer_defaults", + ], + + srcs: [ + "misc_writer.cpp", + ], + + export_include_dirs: [ + "include", + ], +} + +cc_binary { + name: "misc_writer", + defaults: [ + "misc_writer_defaults", + ], + + srcs: [ + "misc_writer_main.cpp", + ], + + static_libs: [ + "libmisc_writer_vendor", + ] +} + +cc_test { + name: "misc_writer_test", + defaults: [ + "misc_writer_defaults", + ], + + srcs: [ + "misc_writer_test.cpp", + ], + test_suites: ["device-tests"], + + static_libs: [ + "libmisc_writer_vendor", + ] +} diff --git a/misc_writer/include/misc_writer/misc_writer.h b/misc_writer/include/misc_writer/misc_writer.h new file mode 100644 index 00000000..6a32ffe4 --- /dev/null +++ b/misc_writer/include/misc_writer/misc_writer.h @@ -0,0 +1,66 @@ +/* + * 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 +#include + +#include +#include + +namespace android { +namespace hardware { +namespace google { +namespace pixel { + +enum class MiscWriterActions : int32_t { + kSetDarkThemeFlag = 0, + kClearDarkThemeFlag, + kSetSotaFlag, + kClearSotaFlag, + + kUnset = -1, +}; + +class MiscWriter { + public: + static constexpr uint32_t kThemeFlagOffsetInVendorSpace = 0; + static constexpr char kDarkThemeFlag[] = "theme-dark"; + static constexpr uint32_t kSotaFlagOffsetInVendorSpace = 32; + static constexpr char kSotaFlag[] = "enable-sota"; + + // Returns true of |size| bytes data starting from |offset| is fully inside the vendor space. + static bool OffsetAndSizeInVendorSpace(size_t offset, size_t size); + // Writes the given data to the vendor space in /misc partition, at the given offset. Note that + // offset is in relative to the start of the vendor space. + static bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, + std::string* err); + + explicit MiscWriter(const MiscWriterActions& action) : action_(action) {} + + // Performs the stored MiscWriterActions. If |override_offset| is set, writes to the input offset + // in the vendor space of /misc instead of the default offset. + bool PerformAction(std::optional override_offset = std::nullopt); + + private: + MiscWriterActions action_{ MiscWriterActions::kUnset }; +}; + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android diff --git a/misc_writer/misc_writer.cpp b/misc_writer/misc_writer.cpp index 1d9702eb..bf589d31 100644 --- a/misc_writer/misc_writer.cpp +++ b/misc_writer/misc_writer.cpp @@ -14,93 +14,70 @@ * limitations under the License. */ -#include -#include -#include -#include -#include +#include "misc_writer/misc_writer.h" -#include -#include -#include -#include +#include +#include #include -#include +#include #include -using namespace std::string_literals; +namespace android { +namespace hardware { +namespace google { +namespace pixel { -static std::vector ParseHexString(std::string_view hex_string) { - auto length = hex_string.size(); - if (length % 2 != 0 || length == 0) { - return {}; - } - - std::vector result(length / 2); - for (size_t i = 0; i < length / 2; i++) { - auto sub = "0x" + std::string(hex_string.substr(i * 2, 2)); - if (!android::base::ParseUint(sub, &result[i])) { - return {}; - } - } - return result; +bool MiscWriter::OffsetAndSizeInVendorSpace(size_t offset, size_t size) { + auto total_size = WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC; + return size <= total_size && offset <= total_size - size; } -static int Usage(std::string_view name) { - std::cerr << name << " usage:\n"; - std::cerr << name << " [--vendor-space-offset ] --hex-string 0xABCDEF\n"; - std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc " - "partition. Offset defaults to 0 if unspecified.\n"; - return EXIT_FAILURE; +bool MiscWriter::WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, + std::string* err) { + if (!OffsetAndSizeInVendorSpace(offset, size)) { + *err = android::base::StringPrintf("Out of bound write (offset %zu size %zu)", offset, size); + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return write_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, + err); } -// misc_writer is a vendor tool that writes data to the vendor space in /misc. -int main(int argc, char** argv) { - constexpr struct option OPTIONS[] = { - { "vendor-space-offset", required_argument, nullptr, 0 }, - { "hex-string", required_argument, nullptr, 0 }, - { nullptr, 0, nullptr, 0 }, - }; - - // Offset defaults to 0 if unspecified. +bool MiscWriter::PerformAction(std::optional override_offset) { size_t offset = 0; - std::string_view hex_string; - - int arg; - int option_index; - while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) { - if (arg != 0) { - LOG(ERROR) << "Invalid command argument"; - return Usage(argv[0]); - } - auto option_name = OPTIONS[option_index].name; - if (option_name == "vendor-space-offset"s) { - if (!android::base::ParseUint(optarg, &offset)) { - LOG(ERROR) << "Failed to parse the offset: " << optarg; - return Usage(argv[0]); - } - } else if (option_name == "hex-string"s) { - hex_string = optarg; - } + std::string content; + switch (action_) { + case MiscWriterActions::kSetDarkThemeFlag: + case MiscWriterActions::kClearDarkThemeFlag: + offset = override_offset.value_or(kThemeFlagOffsetInVendorSpace); + content = (action_ == MiscWriterActions::kSetDarkThemeFlag) + ? kDarkThemeFlag + : std::string(strlen(kDarkThemeFlag), 0); + break; + case MiscWriterActions::kSetSotaFlag: + case MiscWriterActions::kClearSotaFlag: + offset = override_offset.value_or(kSotaFlagOffsetInVendorSpace); + content = (action_ == MiscWriterActions::kSetSotaFlag) ? kSotaFlag + : std::string(strlen(kSotaFlag), 0); + break; + case MiscWriterActions::kUnset: + LOG(ERROR) << "The misc writer action must be set"; + return false; } - if (hex_string.starts_with("0x") || hex_string.starts_with("0X")) { - hex_string = hex_string.substr(2); + if (std::string err; + !WriteMiscPartitionVendorSpace(content.data(), content.size(), offset, &err)) { + LOG(ERROR) << "Failed to write " << content << " at offset " << offset << " : " << err; + return false; } - if (hex_string.empty()) { - LOG(ERROR) << "Invalid input hex string: " << hex_string; - return Usage(argv[0]); - } - - auto data = ParseHexString(hex_string); - if (data.empty()) { - LOG(ERROR) << "Failed to parse the input hex string: " << hex_string; - return EXIT_FAILURE; - } - if (std::string err; !WriteMiscPartitionVendorSpace(data.data(), data.size(), offset, &err)) { - LOG(ERROR) << "Failed to write to misc partition: " << err; - return EXIT_FAILURE; - } - return EXIT_SUCCESS; + return true; } + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android diff --git a/misc_writer/misc_writer_main.cpp b/misc_writer/misc_writer_main.cpp new file mode 100644 index 00000000..69a9fe3d --- /dev/null +++ b/misc_writer/misc_writer_main.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "misc_writer/misc_writer.h" + +using namespace std::string_literals; +using android::hardware::google::pixel::MiscWriter; +using android::hardware::google::pixel::MiscWriterActions; + +static int Usage(std::string_view name) { + std::cerr << name << " usage:\n"; + std::cerr << name << " [--override-vendor-space-offset ] --\n"; + std::cerr << "Supported misc_writer_action is one of: \n"; + std::cerr << " --set-dark-theme Write the dark theme flag\n"; + std::cerr << " --clear-dark-theme Clear the dark theme flag\n"; + std::cerr << " --set-sota Write the silent OTA flag\n"; + std::cerr << " --clear-sota Clear the silent OTA flag\n"; + std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc " + "partition.\nDefault offset is used for each action unless " + "--override-vendor-space-offset is specified.\n"; + return EXIT_FAILURE; +} + +// misc_writer is a vendor tool that writes data to the vendor space in /misc. +int main(int argc, char** argv) { + constexpr struct option OPTIONS[] = { + { "set-dark-theme", no_argument, nullptr, 0 }, + { "clear-dark-theme", no_argument, nullptr, 0 }, + { "set-sota", no_argument, nullptr, 0 }, + { "clear-sota", no_argument, nullptr, 0 }, + { "override-vendor-space-offset", required_argument, nullptr, 0 }, + { nullptr, 0, nullptr, 0 }, + }; + + std::map action_map{ + { "set-dark-theme", MiscWriterActions::kSetDarkThemeFlag }, + { "clear-dark-theme", MiscWriterActions::kClearDarkThemeFlag }, + { "set-sota", MiscWriterActions::kSetSotaFlag }, + { "clear-sota", MiscWriterActions::kClearSotaFlag }, + }; + + std::unique_ptr misc_writer; + std::optional override_offset; + + int arg; + int option_index = 0; + while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) { + if (arg != 0) { + LOG(ERROR) << "Invalid command argument"; + return Usage(argv[0]); + } + auto option_name = OPTIONS[option_index].name; + if (option_name == "override-vendor-space-offset"s) { + LOG(WARNING) << "Overriding the vendor space offset in misc partition to " << optarg; + size_t offset; + if (!android::base::ParseUint(optarg, &offset)) { + LOG(ERROR) << "Failed to parse the offset: " << optarg; + return Usage(argv[0]); + } + override_offset = offset; + } else if (auto iter = action_map.find(option_name); iter != action_map.end()) { + if (misc_writer) { + LOG(ERROR) << "Misc writer action has already been set"; + return Usage(argv[0]); + } + misc_writer = std::make_unique(iter->second); + } else { + LOG(FATAL) << "Unreachable path, option_name: " << option_name; + } + } + + if (!misc_writer) { + LOG(ERROR) << "An action must be specified for misc writer"; + return Usage(argv[0]); + } + + if (!misc_writer->PerformAction(override_offset)) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/misc_writer/misc_writer_test.cpp b/misc_writer/misc_writer_test.cpp new file mode 100644 index 00000000..e8b207af --- /dev/null +++ b/misc_writer/misc_writer_test.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2016 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 +#include +#include +#include + +#include +#include +#include + +#include "misc_writer/misc_writer.h" + +using namespace std::string_literals; + +namespace android { +namespace hardware { +namespace google { +namespace pixel { + +class MiscWriterTest : public ::testing::Test { + protected: + void TearDown() override { + // Clear the vendor space. + auto zeros = std::string(WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC, 0); + std::string err; + ASSERT_TRUE(MiscWriter::WriteMiscPartitionVendorSpace(zeros.data(), zeros.size(), 0, &err)) + << err; + } + + void CheckMiscPartitionVendorSpaceContent(size_t offset, const std::string& expected); + + std::unique_ptr misc_writer_; +}; + +void MiscWriterTest::CheckMiscPartitionVendorSpaceContent(size_t offset, + const std::string& expected) { + ASSERT_TRUE(MiscWriter::OffsetAndSizeInVendorSpace(offset, expected.size())); + std::string err; + auto misc_blk_device = get_misc_blk_device(&err); + ASSERT_FALSE(misc_blk_device.empty()); + android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY)); + ASSERT_NE(-1, fd); + + std::string content(expected.size(), 0); + ASSERT_TRUE(android::base::ReadFullyAtOffset(fd, content.data(), content.size(), + VENDOR_SPACE_OFFSET_IN_MISC + offset)); + ASSERT_EQ(expected, content); +} + +TEST_F(MiscWriterTest, SetClearDarkTheme) { + misc_writer_ = std::make_unique(MiscWriterActions::kSetDarkThemeFlag); + ASSERT_TRUE(misc_writer_); + ASSERT_TRUE(misc_writer_->PerformAction()); + std::string expected = "theme-dark"; + CheckMiscPartitionVendorSpaceContent(0, expected); + + misc_writer_ = std::make_unique(MiscWriterActions::kClearDarkThemeFlag); + ASSERT_TRUE(misc_writer_->PerformAction()); + std::string zeros(expected.size(), 0); + CheckMiscPartitionVendorSpaceContent(0, zeros); +} + +TEST_F(MiscWriterTest, SetClearDarkTheme_OffsetOverride) { + misc_writer_ = std::make_unique(MiscWriterActions::kSetDarkThemeFlag); + size_t offset = 12360; + ASSERT_TRUE(misc_writer_->PerformAction(offset)); + std::string expected = "theme-dark"; + CheckMiscPartitionVendorSpaceContent(offset, expected); + + misc_writer_ = std::make_unique(MiscWriterActions::kClearDarkThemeFlag); + ASSERT_TRUE(misc_writer_->PerformAction(offset)); + std::string zeros(expected.size(), 0); + CheckMiscPartitionVendorSpaceContent(offset, zeros); +} + +TEST_F(MiscWriterTest, SetClearSota) { + misc_writer_ = std::make_unique(MiscWriterActions::kSetSotaFlag); + ASSERT_TRUE(misc_writer_); + ASSERT_TRUE(misc_writer_->PerformAction()); + std::string expected = "enable-sota"; + CheckMiscPartitionVendorSpaceContent(32, expected); + + // Test we can write to the override offset. + size_t override_offset = 12360; + ASSERT_FALSE(misc_writer_->PerformAction(override_offset)); + CheckMiscPartitionVendorSpaceContent(override_offset, expected); + + misc_writer_ = std::make_unique(MiscWriterActions::kClearSotaFlag); + ASSERT_TRUE(misc_writer_->PerformAction()); + std::string zeros(expected.size(), 0); + CheckMiscPartitionVendorSpaceContent(32, zeros); +} + +TEST_F(MiscWriterTest, WriteMiscPartitionVendorSpace) { + std::string kTestMessage = "kTestMessage"; + std::string err; + ASSERT_TRUE( + MiscWriter::WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), 0, &err)); + + CheckMiscPartitionVendorSpaceContent(0, kTestMessage); + + // Write with an offset. + ASSERT_TRUE(MiscWriter::WriteMiscPartitionVendorSpace("\x00\x00", 2, 5, &err)); + CheckMiscPartitionVendorSpaceContent(0, "kTest\x00\x00ssage"s); + + // Write with the right size. + auto start_offset = + WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC - kTestMessage.size(); + ASSERT_TRUE(MiscWriter::WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), + start_offset, &err)); + + // Out-of-bound write. + ASSERT_FALSE(MiscWriter::WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), + start_offset + 1, &err)); + + // Message won't fit. + std::string long_message(WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC + 1, 'a'); + ASSERT_FALSE( + MiscWriter::WriteMiscPartitionVendorSpace(long_message.data(), long_message.size(), 0, &err)); +} + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android diff --git a/tests/unit/bootloader_message_test.cpp b/tests/unit/bootloader_message_test.cpp index 95d875e6..731c8feb 100644 --- a/tests/unit/bootloader_message_test.cpp +++ b/tests/unit/bootloader_message_test.cpp @@ -118,37 +118,3 @@ TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_long) { ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'), std::string(boot.reserved, sizeof(boot.reserved))); } - -TEST(BootloaderMessageTest, WriteMiscPartitionVendorSpace) { - TemporaryFile temp_misc; - ASSERT_TRUE(android::base::WriteStringToFile(std::string(4096, '\x00'), temp_misc.path)); - SetMiscBlockDeviceForTest(temp_misc.path); - - constexpr std::string_view kTestMessage = "kTestMessage"; - std::string err; - ASSERT_TRUE(WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), 0, &err)); - - std::string message; - message.resize(kTestMessage.size()); - ASSERT_TRUE(ReadMiscPartitionVendorSpace(message.data(), message.size(), 0, &err)); - ASSERT_EQ(kTestMessage, message); - - // Write with an offset. - ASSERT_TRUE(WriteMiscPartitionVendorSpace("\x00\x00", 2, 5, &err)); - ASSERT_TRUE(ReadMiscPartitionVendorSpace(message.data(), message.size(), 0, &err)); - ASSERT_EQ("kTest\x00\x00ssage"s, message); - - // Write with the right size. - auto start_offset = - WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC - kTestMessage.size(); - ASSERT_TRUE( - WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), start_offset, &err)); - - // Out-of-bound write. - ASSERT_FALSE(WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), - start_offset + 1, &err)); - - // Message won't fit. - std::string long_message(WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC + 1, 'a'); - ASSERT_FALSE(WriteMiscPartitionVendorSpace(long_message.data(), long_message.size(), 0, &err)); -} From df35405a4b5033bff2cb58b82128aba15fa86677 Mon Sep 17 00:00:00 2001 From: Dmytro Prokopchuk Date: Wed, 13 Nov 2019 13:30:17 +0200 Subject: [PATCH 121/171] Fixed typo during stopping fastboot Change-Id: I0a589d068807e255654c7e62831423f944b5cdc3 Signed-off-by: Dmytro Prokopchuk --- etc/init.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/init.rc b/etc/init.rc index d5b056ae..d1c4f049 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -130,7 +130,7 @@ on property:sys.usb.config=fastboot on property:sys.usb.config=none && property:sys.usb.configfs=0 stop adbd - stop fastboot + stop fastbootd write /sys/class/android_usb/android0/enable 0 setprop sys.usb.state ${sys.usb.config} From 28064ccf0b9615c157cc94e54cebf224af0bef89 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 13 Nov 2019 15:52:44 -0800 Subject: [PATCH 122/171] Move misc_writer to hardare/google/pixel Because the library is pixel specific. Bug: 131775112 Test: build Change-Id: Ib62230bb2ab4d67864827e08375250fbfe189ed4 --- misc_writer/Android.bp | 110 -------------- misc_writer/include/misc_writer/misc_writer.h | 66 --------- misc_writer/misc_writer.cpp | 83 ----------- misc_writer/misc_writer_main.cpp | 109 -------------- misc_writer/misc_writer_test.cpp | 140 ------------------ 5 files changed, 508 deletions(-) delete mode 100644 misc_writer/Android.bp delete mode 100644 misc_writer/include/misc_writer/misc_writer.h delete mode 100644 misc_writer/misc_writer.cpp delete mode 100644 misc_writer/misc_writer_main.cpp delete mode 100644 misc_writer/misc_writer_test.cpp diff --git a/misc_writer/Android.bp b/misc_writer/Android.bp deleted file mode 100644 index 73c44d2e..00000000 --- a/misc_writer/Android.bp +++ /dev/null @@ -1,110 +0,0 @@ -// -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -cc_defaults { - name: "misc_writer_defaults", - vendor: true, - cpp_std: "experimental", - - cflags: [ - "-Wall", - "-Werror", - ], - - shared_libs: [ - "libbase", - ], - - static_libs: [ - "libbootloader_message_vendor", - "libfstab", - ], -} - -// TODO(xunchang) Remove duplicates after we convert the device specific librecovery_ui to recovery -// module. Then libmisc_writer can build as a vendor module available in recovery. -cc_library_static { - name: "libmisc_writer", - cpp_std: "experimental", - - cflags: [ - "-Wall", - "-Werror", - ], - - shared_libs: [ - "libbase", - ], - - static_libs: [ - "libbootloader_message", - "libfstab", - ], - - srcs: [ - "misc_writer.cpp", - ], - - export_include_dirs: [ - "include", - ], -} - -cc_library_static { - name: "libmisc_writer_vendor", - defaults: [ - "misc_writer_defaults", - ], - - srcs: [ - "misc_writer.cpp", - ], - - export_include_dirs: [ - "include", - ], -} - -cc_binary { - name: "misc_writer", - defaults: [ - "misc_writer_defaults", - ], - - srcs: [ - "misc_writer_main.cpp", - ], - - static_libs: [ - "libmisc_writer_vendor", - ] -} - -cc_test { - name: "misc_writer_test", - defaults: [ - "misc_writer_defaults", - ], - - srcs: [ - "misc_writer_test.cpp", - ], - test_suites: ["device-tests"], - - static_libs: [ - "libmisc_writer_vendor", - ] -} diff --git a/misc_writer/include/misc_writer/misc_writer.h b/misc_writer/include/misc_writer/misc_writer.h deleted file mode 100644 index 6a32ffe4..00000000 --- a/misc_writer/include/misc_writer/misc_writer.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include -#include - -namespace android { -namespace hardware { -namespace google { -namespace pixel { - -enum class MiscWriterActions : int32_t { - kSetDarkThemeFlag = 0, - kClearDarkThemeFlag, - kSetSotaFlag, - kClearSotaFlag, - - kUnset = -1, -}; - -class MiscWriter { - public: - static constexpr uint32_t kThemeFlagOffsetInVendorSpace = 0; - static constexpr char kDarkThemeFlag[] = "theme-dark"; - static constexpr uint32_t kSotaFlagOffsetInVendorSpace = 32; - static constexpr char kSotaFlag[] = "enable-sota"; - - // Returns true of |size| bytes data starting from |offset| is fully inside the vendor space. - static bool OffsetAndSizeInVendorSpace(size_t offset, size_t size); - // Writes the given data to the vendor space in /misc partition, at the given offset. Note that - // offset is in relative to the start of the vendor space. - static bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, - std::string* err); - - explicit MiscWriter(const MiscWriterActions& action) : action_(action) {} - - // Performs the stored MiscWriterActions. If |override_offset| is set, writes to the input offset - // in the vendor space of /misc instead of the default offset. - bool PerformAction(std::optional override_offset = std::nullopt); - - private: - MiscWriterActions action_{ MiscWriterActions::kUnset }; -}; - -} // namespace pixel -} // namespace google -} // namespace hardware -} // namespace android diff --git a/misc_writer/misc_writer.cpp b/misc_writer/misc_writer.cpp deleted file mode 100644 index bf589d31..00000000 --- a/misc_writer/misc_writer.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "misc_writer/misc_writer.h" - -#include - -#include -#include -#include -#include - -namespace android { -namespace hardware { -namespace google { -namespace pixel { - -bool MiscWriter::OffsetAndSizeInVendorSpace(size_t offset, size_t size) { - auto total_size = WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC; - return size <= total_size && offset <= total_size - size; -} - -bool MiscWriter::WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, - std::string* err) { - if (!OffsetAndSizeInVendorSpace(offset, size)) { - *err = android::base::StringPrintf("Out of bound write (offset %zu size %zu)", offset, size); - return false; - } - auto misc_blk_device = get_misc_blk_device(err); - if (misc_blk_device.empty()) { - return false; - } - return write_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, - err); -} - -bool MiscWriter::PerformAction(std::optional override_offset) { - size_t offset = 0; - std::string content; - switch (action_) { - case MiscWriterActions::kSetDarkThemeFlag: - case MiscWriterActions::kClearDarkThemeFlag: - offset = override_offset.value_or(kThemeFlagOffsetInVendorSpace); - content = (action_ == MiscWriterActions::kSetDarkThemeFlag) - ? kDarkThemeFlag - : std::string(strlen(kDarkThemeFlag), 0); - break; - case MiscWriterActions::kSetSotaFlag: - case MiscWriterActions::kClearSotaFlag: - offset = override_offset.value_or(kSotaFlagOffsetInVendorSpace); - content = (action_ == MiscWriterActions::kSetSotaFlag) ? kSotaFlag - : std::string(strlen(kSotaFlag), 0); - break; - case MiscWriterActions::kUnset: - LOG(ERROR) << "The misc writer action must be set"; - return false; - } - - if (std::string err; - !WriteMiscPartitionVendorSpace(content.data(), content.size(), offset, &err)) { - LOG(ERROR) << "Failed to write " << content << " at offset " << offset << " : " << err; - return false; - } - return true; -} - -} // namespace pixel -} // namespace google -} // namespace hardware -} // namespace android diff --git a/misc_writer/misc_writer_main.cpp b/misc_writer/misc_writer_main.cpp deleted file mode 100644 index 69a9fe3d..00000000 --- a/misc_writer/misc_writer_main.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "misc_writer/misc_writer.h" - -using namespace std::string_literals; -using android::hardware::google::pixel::MiscWriter; -using android::hardware::google::pixel::MiscWriterActions; - -static int Usage(std::string_view name) { - std::cerr << name << " usage:\n"; - std::cerr << name << " [--override-vendor-space-offset ] --\n"; - std::cerr << "Supported misc_writer_action is one of: \n"; - std::cerr << " --set-dark-theme Write the dark theme flag\n"; - std::cerr << " --clear-dark-theme Clear the dark theme flag\n"; - std::cerr << " --set-sota Write the silent OTA flag\n"; - std::cerr << " --clear-sota Clear the silent OTA flag\n"; - std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc " - "partition.\nDefault offset is used for each action unless " - "--override-vendor-space-offset is specified.\n"; - return EXIT_FAILURE; -} - -// misc_writer is a vendor tool that writes data to the vendor space in /misc. -int main(int argc, char** argv) { - constexpr struct option OPTIONS[] = { - { "set-dark-theme", no_argument, nullptr, 0 }, - { "clear-dark-theme", no_argument, nullptr, 0 }, - { "set-sota", no_argument, nullptr, 0 }, - { "clear-sota", no_argument, nullptr, 0 }, - { "override-vendor-space-offset", required_argument, nullptr, 0 }, - { nullptr, 0, nullptr, 0 }, - }; - - std::map action_map{ - { "set-dark-theme", MiscWriterActions::kSetDarkThemeFlag }, - { "clear-dark-theme", MiscWriterActions::kClearDarkThemeFlag }, - { "set-sota", MiscWriterActions::kSetSotaFlag }, - { "clear-sota", MiscWriterActions::kClearSotaFlag }, - }; - - std::unique_ptr misc_writer; - std::optional override_offset; - - int arg; - int option_index = 0; - while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) { - if (arg != 0) { - LOG(ERROR) << "Invalid command argument"; - return Usage(argv[0]); - } - auto option_name = OPTIONS[option_index].name; - if (option_name == "override-vendor-space-offset"s) { - LOG(WARNING) << "Overriding the vendor space offset in misc partition to " << optarg; - size_t offset; - if (!android::base::ParseUint(optarg, &offset)) { - LOG(ERROR) << "Failed to parse the offset: " << optarg; - return Usage(argv[0]); - } - override_offset = offset; - } else if (auto iter = action_map.find(option_name); iter != action_map.end()) { - if (misc_writer) { - LOG(ERROR) << "Misc writer action has already been set"; - return Usage(argv[0]); - } - misc_writer = std::make_unique(iter->second); - } else { - LOG(FATAL) << "Unreachable path, option_name: " << option_name; - } - } - - if (!misc_writer) { - LOG(ERROR) << "An action must be specified for misc writer"; - return Usage(argv[0]); - } - - if (!misc_writer->PerformAction(override_offset)) { - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/misc_writer/misc_writer_test.cpp b/misc_writer/misc_writer_test.cpp deleted file mode 100644 index e8b207af..00000000 --- a/misc_writer/misc_writer_test.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2016 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 -#include -#include -#include - -#include -#include -#include - -#include "misc_writer/misc_writer.h" - -using namespace std::string_literals; - -namespace android { -namespace hardware { -namespace google { -namespace pixel { - -class MiscWriterTest : public ::testing::Test { - protected: - void TearDown() override { - // Clear the vendor space. - auto zeros = std::string(WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC, 0); - std::string err; - ASSERT_TRUE(MiscWriter::WriteMiscPartitionVendorSpace(zeros.data(), zeros.size(), 0, &err)) - << err; - } - - void CheckMiscPartitionVendorSpaceContent(size_t offset, const std::string& expected); - - std::unique_ptr misc_writer_; -}; - -void MiscWriterTest::CheckMiscPartitionVendorSpaceContent(size_t offset, - const std::string& expected) { - ASSERT_TRUE(MiscWriter::OffsetAndSizeInVendorSpace(offset, expected.size())); - std::string err; - auto misc_blk_device = get_misc_blk_device(&err); - ASSERT_FALSE(misc_blk_device.empty()); - android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY)); - ASSERT_NE(-1, fd); - - std::string content(expected.size(), 0); - ASSERT_TRUE(android::base::ReadFullyAtOffset(fd, content.data(), content.size(), - VENDOR_SPACE_OFFSET_IN_MISC + offset)); - ASSERT_EQ(expected, content); -} - -TEST_F(MiscWriterTest, SetClearDarkTheme) { - misc_writer_ = std::make_unique(MiscWriterActions::kSetDarkThemeFlag); - ASSERT_TRUE(misc_writer_); - ASSERT_TRUE(misc_writer_->PerformAction()); - std::string expected = "theme-dark"; - CheckMiscPartitionVendorSpaceContent(0, expected); - - misc_writer_ = std::make_unique(MiscWriterActions::kClearDarkThemeFlag); - ASSERT_TRUE(misc_writer_->PerformAction()); - std::string zeros(expected.size(), 0); - CheckMiscPartitionVendorSpaceContent(0, zeros); -} - -TEST_F(MiscWriterTest, SetClearDarkTheme_OffsetOverride) { - misc_writer_ = std::make_unique(MiscWriterActions::kSetDarkThemeFlag); - size_t offset = 12360; - ASSERT_TRUE(misc_writer_->PerformAction(offset)); - std::string expected = "theme-dark"; - CheckMiscPartitionVendorSpaceContent(offset, expected); - - misc_writer_ = std::make_unique(MiscWriterActions::kClearDarkThemeFlag); - ASSERT_TRUE(misc_writer_->PerformAction(offset)); - std::string zeros(expected.size(), 0); - CheckMiscPartitionVendorSpaceContent(offset, zeros); -} - -TEST_F(MiscWriterTest, SetClearSota) { - misc_writer_ = std::make_unique(MiscWriterActions::kSetSotaFlag); - ASSERT_TRUE(misc_writer_); - ASSERT_TRUE(misc_writer_->PerformAction()); - std::string expected = "enable-sota"; - CheckMiscPartitionVendorSpaceContent(32, expected); - - // Test we can write to the override offset. - size_t override_offset = 12360; - ASSERT_FALSE(misc_writer_->PerformAction(override_offset)); - CheckMiscPartitionVendorSpaceContent(override_offset, expected); - - misc_writer_ = std::make_unique(MiscWriterActions::kClearSotaFlag); - ASSERT_TRUE(misc_writer_->PerformAction()); - std::string zeros(expected.size(), 0); - CheckMiscPartitionVendorSpaceContent(32, zeros); -} - -TEST_F(MiscWriterTest, WriteMiscPartitionVendorSpace) { - std::string kTestMessage = "kTestMessage"; - std::string err; - ASSERT_TRUE( - MiscWriter::WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), 0, &err)); - - CheckMiscPartitionVendorSpaceContent(0, kTestMessage); - - // Write with an offset. - ASSERT_TRUE(MiscWriter::WriteMiscPartitionVendorSpace("\x00\x00", 2, 5, &err)); - CheckMiscPartitionVendorSpaceContent(0, "kTest\x00\x00ssage"s); - - // Write with the right size. - auto start_offset = - WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC - kTestMessage.size(); - ASSERT_TRUE(MiscWriter::WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), - start_offset, &err)); - - // Out-of-bound write. - ASSERT_FALSE(MiscWriter::WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), - start_offset + 1, &err)); - - // Message won't fit. - std::string long_message(WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC + 1, 'a'); - ASSERT_FALSE( - MiscWriter::WriteMiscPartitionVendorSpace(long_message.data(), long_message.size(), 0, &err)); -} - -} // namespace pixel -} // namespace google -} // namespace hardware -} // namespace android From 88bf6d20ebbf8850a2389ede2e959b10d7ebe09e Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 14 Nov 2019 15:07:25 -0800 Subject: [PATCH 123/171] Clear the warm_reset flag after boot is successful The property is set to inform kernel to do a warm_reset on the next reboot. This is useful to persist the logs to debug device boot failures. After the slot has been marked as boot successful, we can drop the warm_reset flag to avoid the performance overhead on the next reboot. Bug: 143489994 Test: check the property is set to 0 by update_verifier Change-Id: I722fb1906e6efa56dfc4ad7beccd5e2ba7e0ef7c --- update_verifier/update_verifier.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index d04c455d..a042f900 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -371,6 +371,10 @@ int update_verifier(int argc, char** argv) { return reboot_device(); } LOG(INFO) << "Marked slot " << current_slot << " as booted successfully."; + // Clears the warm reset flag for next reboot. + if (!android::base::SetProperty("ota.warm_reset", "0")) { + LOG(WARNING) << "Failed to reset the warm reset flag"; + } } else { LOG(INFO) << "Deferred marking slot " << current_slot << " as booted successfully."; } From 682d2a5db2a61a6f97c09d7c143e023fe2bd4e14 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 20 Nov 2019 15:47:07 -0800 Subject: [PATCH 124/171] bootloader_message: Add a magic header to the Virtual A/B message block. This adds a sanity check in addition to the version number in case misc contains random bits. Bug: 139156011 Test: manual test Change-Id: Ie4f3731d2b1795340881c88e0c4eec9cd4432653 --- boot_control/libboot_control.cpp | 4 +++- .../include/bootloader_message/bootloader_message.h | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/boot_control/libboot_control.cpp b/boot_control/libboot_control.cpp index 70218397..ab9ce971 100644 --- a/boot_control/libboot_control.cpp +++ b/boot_control/libboot_control.cpp @@ -365,13 +365,15 @@ bool InitMiscVirtualAbMessageIfNeeded() { return false; } - if (message.version == MISC_VIRTUAL_AB_MESSAGE_VERSION) { + if (message.version == MISC_VIRTUAL_AB_MESSAGE_VERSION && + message.magic == MISC_VIRTUAL_AB_MAGIC_HEADER) { // Already initialized. return true; } message = {}; message.version = MISC_VIRTUAL_AB_MESSAGE_VERSION; + message.magic = MISC_VIRTUAL_AB_MAGIC_HEADER; if (!WriteMiscVirtualAbMessage(message, &err)) { LOG(ERROR) << "Could not write merge status: " << err; return false; diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index a27e80bc..9a482d42 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -189,12 +189,14 @@ static_assert(sizeof(struct bootloader_control) == // must be added to the end. struct misc_virtual_ab_message { uint8_t version; + uint32_t magic; uint8_t merge_status; // IBootControl 1.1, MergeStatus enum. uint8_t source_slot; // Slot number when merge_status was written. - uint8_t reserved[61]; + uint8_t reserved[57]; } __attribute__((packed)); -#define MISC_VIRTUAL_AB_MESSAGE_VERSION 1 +#define MISC_VIRTUAL_AB_MESSAGE_VERSION 2 +#define MISC_VIRTUAL_AB_MAGIC_HEADER 0x56740AB0 #if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) static_assert(sizeof(struct misc_virtual_ab_message) == 64, From fd6a2c226d8babf1c77dd09efbab5473b9173c51 Mon Sep 17 00:00:00 2001 From: Joshua Lambert Date: Fri, 6 Dec 2019 12:51:06 -0800 Subject: [PATCH 125/171] Add mechanism for device-specific loop images Bug: 144974129 Test: Manual - Testing OTA and Factory Reset 1) Tested with matching device name ({devicename}00000.png) 2) Tested with no matching device name (uses loop00000.png default) 3) Tested with empty string device name (uses loop00000.png default) Change-Id: I4c73af82ab8826d1a43fe193a7616bc219d536e4 --- recovery_ui/screen_ui.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp index 087fc0e8..b3ef113a 100644 --- a/recovery_ui/screen_ui.cpp +++ b/recovery_ui/screen_ui.cpp @@ -941,17 +941,27 @@ void ScreenRecoveryUI::LoadAnimation() { closedir); dirent* de; std::vector intro_frame_names; - std::vector loop_frame_names; + std::vector default_loop_frame_names; + std::vector device_loop_frame_names; + // Create string format for device-specific loop animations. + std::string deviceformat = android::base::GetProperty("ro.product.product.name", ""); + deviceformat += "%d%n.png"; while ((de = readdir(dir.get())) != nullptr) { int value, num_chars; if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) { intro_frame_names.emplace_back(de->d_name, num_chars); } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) { - loop_frame_names.emplace_back(de->d_name, num_chars); + default_loop_frame_names.emplace_back(de->d_name, num_chars); + } else if (sscanf(de->d_name, deviceformat.c_str(), &value, &num_chars) == 1) { + device_loop_frame_names.emplace_back(de->d_name, num_chars); } } + // Favor device-specific loop frames, if they exist. + auto& loop_frame_names = + device_loop_frame_names.empty() ? default_loop_frame_names : device_loop_frame_names; + size_t intro_frames = intro_frame_names.size(); size_t loop_frames = loop_frame_names.size(); From 94a83be42aab908ae7f59af0f107a8036ac8c684 Mon Sep 17 00:00:00 2001 From: Joshua Lambert Date: Mon, 9 Dec 2019 20:24:18 +0000 Subject: [PATCH 126/171] Revert "Add mechanism for device-specific loop images" This reverts commit fd6a2c226d8babf1c77dd09efbab5473b9173c51. Reason for revert: Mechanism already exists: https://source.android.com/devices/tech/ota/nonab/device_code#recovery-ui-images Change-Id: Ic7b640b5d435cfb6f5c2610a2534405fb9415936 --- recovery_ui/screen_ui.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp index b3ef113a..087fc0e8 100644 --- a/recovery_ui/screen_ui.cpp +++ b/recovery_ui/screen_ui.cpp @@ -941,27 +941,17 @@ void ScreenRecoveryUI::LoadAnimation() { closedir); dirent* de; std::vector intro_frame_names; - std::vector default_loop_frame_names; - std::vector device_loop_frame_names; - // Create string format for device-specific loop animations. - std::string deviceformat = android::base::GetProperty("ro.product.product.name", ""); - deviceformat += "%d%n.png"; + std::vector loop_frame_names; while ((de = readdir(dir.get())) != nullptr) { int value, num_chars; if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) { intro_frame_names.emplace_back(de->d_name, num_chars); } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) { - default_loop_frame_names.emplace_back(de->d_name, num_chars); - } else if (sscanf(de->d_name, deviceformat.c_str(), &value, &num_chars) == 1) { - device_loop_frame_names.emplace_back(de->d_name, num_chars); + loop_frame_names.emplace_back(de->d_name, num_chars); } } - // Favor device-specific loop frames, if they exist. - auto& loop_frame_names = - device_loop_frame_names.empty() ? default_loop_frame_names : device_loop_frame_names; - size_t intro_frames = intro_frame_names.size(); size_t loop_frames = loop_frame_names.size(); From e0134fc7159b1b67c50ff843c1359b357b88758a Mon Sep 17 00:00:00 2001 From: Jindong Date: Fri, 9 Aug 2019 15:31:03 +0800 Subject: [PATCH 127/171] updater_sample: add internet access permission Add internet access permission to fix below error: java.net.SocketException: socket failed: EPERM (Operation not permitted) Change-Id: I6fba2c871df77ad7a0b98ed4a2ffa907037e9d6f Signed-off-by: Jindong --- updater_sample/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/updater_sample/AndroidManifest.xml b/updater_sample/AndroidManifest.xml index 0a251161..981cd8eb 100644 --- a/updater_sample/AndroidManifest.xml +++ b/updater_sample/AndroidManifest.xml @@ -20,6 +20,7 @@ + Date: Mon, 9 Dec 2019 19:27:57 +0900 Subject: [PATCH 128/171] Generate linker config from recovery init Generate linker config from recovery init to be used from recovery processes. Bug: 139638519 Test: Tested from crosshatch Change-Id: I777a8baf08254b07375b8039bb252864637e29e7 --- etc/init.rc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/etc/init.rc b/etc/init.rc index 12411ac1..395e627b 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -4,6 +4,10 @@ on early-init # Set the security context of /postinstall if present. restorecon /postinstall + # Generate ld.config.txt for recovery processes + exec u:r:linkerconfig:s0 -- /system/bin/linkerconfig --target /linkerconfig/ld.config.txt + chmod 444 /linkerconfig/ld.config.txt + start ueventd setprop sys.usb.configfs 0 From cc7b7eb7379f08d113133b865a28dd9c3b0f0852 Mon Sep 17 00:00:00 2001 From: Adrian Salido Date: Thu, 12 Dec 2019 20:18:09 -0800 Subject: [PATCH 129/171] minui: add ARGB_8888 format Minui currently really only supports composing in 2 different formats (see gr_color()) with ALPHA always as MSB. However, some devices interpret PixelFormat as either Big Endian (i.e. ARGB has alpha at MSB) or Little Endian (i.e. BGRA has alpha at MSB). This change attempts to give multiple options to specify the same format depending on device interpretation, while keeping just 2 different composition formats supported by minui. * ARGB + BGRA: Pixels have (A)lpha at MSB and (B)lue at LSB * RGBX + ABGR: Pixels have (A)lpha at MSB and (R)ed at LSB With this in mind, limiting the use of png_set_bgr() to happen only for (ARGB/BGRA) combination while leaving (RGBX/ABGR) unchanged. Bug: 143480444 Test: Boot device with TARGET_RECOVERY_PIXEL_FORMAT := <> Change-Id: Ia0f94ccbc564b8def7c9416483712ff1abbbf49a --- minui/graphics.cpp | 4 +++- minui/graphics_drm.cpp | 4 ++++ minui/include/minui/minui.h | 1 + minui/resources.cpp | 6 +++--- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/minui/graphics.cpp b/minui/graphics.cpp index 4d1f9b2d..d34da567 100644 --- a/minui/graphics.cpp +++ b/minui/graphics.cpp @@ -209,7 +209,7 @@ void gr_texticon(int x, int y, const GRSurface* icon) { void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { uint32_t r32 = r, g32 = g, b32 = b, a32 = a; - if (pixel_format == PixelFormat::ABGR || pixel_format == PixelFormat::BGRA) { + if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) { gr_current = (a32 << 24) | (r32 << 16) | (g32 << 8) | b32; } else { gr_current = (a32 << 24) | (b32 << 16) | (g32 << 8) | r32; @@ -348,6 +348,8 @@ int gr_init() { pixel_format = PixelFormat::ABGR; } else if (format == "RGBX_8888") { pixel_format = PixelFormat::RGBX; + } else if (format == "ARGB_8888") { + pixel_format = PixelFormat::ARGB; } else if (format == "BGRA_8888") { pixel_format = PixelFormat::BGRA; } else { diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp index 7b2eed15..95759e38 100644 --- a/minui/graphics_drm.cpp +++ b/minui/graphics_drm.cpp @@ -62,6 +62,8 @@ static int drm_format_to_bpp(uint32_t format) { case DRM_FORMAT_ABGR8888: case DRM_FORMAT_BGRA8888: case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_ARGB8888: case DRM_FORMAT_BGRX8888: case DRM_FORMAT_XBGR8888: case DRM_FORMAT_XRGB8888: @@ -87,6 +89,8 @@ std::unique_ptr GRSurfaceDrm::Create(int drm_fd, int width, int he format = DRM_FORMAT_ARGB8888; } else if (pixel_format == PixelFormat::RGBX) { format = DRM_FORMAT_XBGR8888; + } else if (pixel_format == PixelFormat::ARGB) { + format = DRM_FORMAT_BGRA8888; } else { format = DRM_FORMAT_RGB565; } diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h index 36bdcf10..163e41dc 100644 --- a/minui/include/minui/minui.h +++ b/minui/include/minui/minui.h @@ -101,6 +101,7 @@ enum class PixelFormat : int { ABGR = 1, RGBX = 2, BGRA = 3, + ARGB = 4, }; // Initializes the graphics backend and loads font file. Returns 0 on success, or -1 on error. Note diff --git a/minui/resources.cpp b/minui/resources.cpp index 00d36d5f..f635acd1 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -199,7 +199,7 @@ int res_create_display_surface(const char* name, GRSurface** pSurface) { } PixelFormat pixel_format = gr_pixel_format(); - if (pixel_format == PixelFormat::ABGR || pixel_format == PixelFormat::BGRA) { + if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) { png_set_bgr(png_ptr); } @@ -271,7 +271,7 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps, surface[i] = created_surface.release(); } - if (gr_pixel_format() == PixelFormat::ABGR || gr_pixel_format() == PixelFormat::BGRA) { + if (gr_pixel_format() == PixelFormat::ARGB || gr_pixel_format() == PixelFormat::BGRA) { png_set_bgr(png_ptr); } @@ -317,7 +317,7 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) { } PixelFormat pixel_format = gr_pixel_format(); - if (pixel_format == PixelFormat::ABGR || pixel_format == PixelFormat::BGRA) { + if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) { png_set_bgr(png_ptr); } From a9665ced57ebd3f00f11cd8d9d99b33269687286 Mon Sep 17 00:00:00 2001 From: Alessio Balsini Date: Mon, 9 Dec 2019 09:26:05 +0000 Subject: [PATCH 130/171] Mount snapshotted /system in Virtual A/B devices Mounting /system in Virtual A/B devices may require the creation of the associated snapshot devices. This patch performs all the required initializations prior to attempting the mount of /system. Bug: 139157327 Test: manual /system partition mount on VAB device during OTA Depends-on: I7337bdd38d7016d12d3ee42be1c7893b10e9116d Change-Id: I71a9dfc57e1a1354f1f1edc5d287aca93c0c8924 Signed-off-by: Alessio Balsini --- install/include/install/snapshot_utils.h | 9 +++++++++ install/snapshot_utils.cpp | 25 ++++++++++++++++++++++++ recovery.cpp | 7 +++++++ 3 files changed, 41 insertions(+) diff --git a/install/include/install/snapshot_utils.h b/install/include/install/snapshot_utils.h index 00c3ef7e..f4b978d2 100644 --- a/install/include/install/snapshot_utils.h +++ b/install/include/install/snapshot_utils.h @@ -19,3 +19,12 @@ #include "recovery_ui/device.h" bool FinishPendingSnapshotMerges(Device* device); + +/* + * This function tries to create the snapshotted devices in the case a Virtual + * A/B device is updating. + * The function returns false in case of critical failure that would prevent + * the further mountings of devices, or true in case of success, if either the + * devices were created or there was no need to. + */ +bool CreateSnapshotPartitions(); diff --git a/install/snapshot_utils.cpp b/install/snapshot_utils.cpp index 69da5eea..7235e67c 100644 --- a/install/snapshot_utils.cpp +++ b/install/snapshot_utils.cpp @@ -15,6 +15,7 @@ * limitations under the License. */ +#include #include #include @@ -22,6 +23,7 @@ #include "recovery_ui/ui.h" #include "recovery_utils/roots.h" +using android::snapshot::CreateResult; using android::snapshot::SnapshotManager; bool FinishPendingSnapshotMerges(Device* device) { @@ -47,3 +49,26 @@ bool FinishPendingSnapshotMerges(Device* device) { } return true; } + +bool CreateSnapshotPartitions() { + if (!android::base::GetBoolProperty("ro.virtual_ab.enabled", false)) { + // If the device does not support Virtual A/B, there's no need to create + // snapshot devices. + return true; + } + + auto sm = SnapshotManager::NewForFirstStageMount(); + if (!sm) { + // SnapshotManager could not be created. The device is still in a + // consistent state and can continue with the mounting of the existing + // devices, but cannot initialize snapshot devices. + LOG(WARNING) << "Could not create SnapshotManager"; + return true; + } + + auto ret = sm->RecoveryCreateSnapshotDevices(); + if (ret == CreateResult::ERROR) { + return false; + } + return true; +} diff --git a/recovery.cpp b/recovery.cpp index f59a940f..e4b8e45f 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -50,6 +50,7 @@ #include "install/fuse_install.h" #include "install/install.h" #include "install/package.h" +#include "install/snapshot_utils.h" #include "install/wipe_data.h" #include "install/wipe_device.h" #include "otautil/boot_state.h" @@ -437,7 +438,13 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) screen_ui->CheckBackgroundTextImages(); break; } + case Device::MOUNT_SYSTEM: + // For Virtual A/B, set up the snapshot devices (if exist). + if (!CreateSnapshotPartitions()) { + ui->Print("Virtual A/B: snapshot partitions creation failed.\n"); + break; + } if (ensure_path_mounted_at(android::fs_mgr::GetSystemRoot(), "/mnt/system") != -1) { ui->Print("Mounted /system.\n"); } From ea2b683f6e886a595f59cb07c5f90383c9579035 Mon Sep 17 00:00:00 2001 From: Kiyoung Kim Date: Tue, 17 Dec 2019 15:05:54 +0900 Subject: [PATCH 131/171] Do not execute linkerconfig from recovery Linkerconfig binary itself should be built as static, so size of executable is hard to be reduced. However, this used lots of space from recovery so only small space left in it. To avoid this linker config from recovery should be generated within build time and use prebuilt one. Prebuilt ld.config.txt will be located under /system/etc as before, and init will copy the file into /linkerconfig so we can use same location for both recovery and normal boot. Bug: 146384333 Test: m -j passed && crosshatch bootloader worked with this change Change-Id: I96300f1c8301167234787274820086a4c6ea0e6e --- etc/init.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/init.rc b/etc/init.rc index 395e627b..3ec45db2 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -4,8 +4,8 @@ on early-init # Set the security context of /postinstall if present. restorecon /postinstall - # Generate ld.config.txt for recovery processes - exec u:r:linkerconfig:s0 -- /system/bin/linkerconfig --target /linkerconfig/ld.config.txt + # Copy prebuilt ld.config.txt into linkerconfig directory + copy /system/etc/ld.config.txt /linkerconfig/ld.config.txt chmod 444 /linkerconfig/ld.config.txt start ueventd From 8f2f0d09ea4243bb95d2fffbaceecfeff159a331 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Tue, 17 Dec 2019 17:51:42 -0800 Subject: [PATCH 132/171] Link libvndksupport dynamically instead of statically. Bug: 146456667 Change-Id: I839223d8fbc365fd3271634143b117604f6aa879 --- tests/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Android.bp b/tests/Android.bp index 5b881e36..3366f56d 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -39,6 +39,7 @@ cc_defaults { android: { shared_libs: [ "libutils", + "libvndksupport", ], }, @@ -92,7 +93,6 @@ librecovery_static_libs = [ "libhidlbase", "libbinderthreadstate", "liblp", - "libvndksupport", "libtinyxml2", ] From c77bb70166db61b99b8c1aff59c2b47b4daf3275 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Mon, 12 Aug 2019 16:18:59 -0700 Subject: [PATCH 133/171] Delete VINTF compatibility check during OTA. Test: sideload OTA Bug: 139300422 Change-Id: I3369b69242ccd7a64540a0c2d754a5d6fc50d072 --- install/Android.bp | 1 - install/include/install/install.h | 4 -- install/install.cpp | 81 ------------------------------ tests/Android.bp | 1 - tests/unit/install_test.cpp | 82 ------------------------------- 5 files changed, 169 deletions(-) diff --git a/install/Android.bp b/install/Android.bp index be08506e..bed3bc50 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -42,7 +42,6 @@ cc_defaults { "libsnapshot_nobinder", // external dependencies - "libvintf_recovery", "libvintf", ], } diff --git a/install/include/install/install.h b/install/include/install/install.h index b4b3a914..87d43ab0 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -59,10 +59,6 @@ bool verify_package(Package* package, RecoveryUI* ui); // result to |metadata|. Return true if succeed, otherwise return false. bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map* metadata); -// Verifies the compatibility info in a Treble-compatible package. Returns true directly if the -// entry doesn't exist. -bool verify_package_compatibility(ZipArchiveHandle package_zip); - // Checks if the metadata in the OTA package has expected values. Mandatory checks: ota-type, // pre-device and serial number (if presents). A/B OTA specific checks: pre-build version, // fingerprint, timestamp. diff --git a/install/install.cpp b/install/install.cpp index 9166f9cf..4bb0903c 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #include "install/package.h" #include "install/verifier.h" @@ -505,73 +504,6 @@ static InstallResult TryUpdateBinary(Package* package, bool* wipe_cache, return INSTALL_SUCCESS; } -// Verifies the compatibility info in a Treble-compatible package. Returns true directly if the -// entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA -// package. -bool verify_package_compatibility(ZipArchiveHandle package_zip) { - LOG(INFO) << "Verifying package compatibility..."; - - static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip"; - ZipEntry compatibility_entry; - if (FindEntry(package_zip, COMPATIBILITY_ZIP_ENTRY, &compatibility_entry) != 0) { - LOG(INFO) << "Package doesn't contain " << COMPATIBILITY_ZIP_ENTRY << " entry"; - return true; - } - - std::string zip_content(compatibility_entry.uncompressed_length, '\0'); - int32_t ret; - if ((ret = ExtractToMemory(package_zip, &compatibility_entry, - reinterpret_cast(&zip_content[0]), - compatibility_entry.uncompressed_length)) != 0) { - LOG(ERROR) << "Failed to read " << COMPATIBILITY_ZIP_ENTRY << ": " << ErrorCodeString(ret); - return false; - } - - ZipArchiveHandle zip_handle; - ret = OpenArchiveFromMemory(static_cast(const_cast(zip_content.data())), - zip_content.size(), COMPATIBILITY_ZIP_ENTRY, &zip_handle); - if (ret != 0) { - LOG(ERROR) << "Failed to OpenArchiveFromMemory: " << ErrorCodeString(ret); - return false; - } - - // Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents. - void* cookie; - ret = StartIteration(zip_handle, &cookie); - if (ret != 0) { - LOG(ERROR) << "Failed to start iterating zip entries: " << ErrorCodeString(ret); - CloseArchive(zip_handle); - return false; - } - std::unique_ptr guard(cookie, EndIteration); - - std::vector compatibility_info; - ZipEntry info_entry; - std::string_view info_name; - while (Next(cookie, &info_entry, &info_name) == 0) { - std::string content(info_entry.uncompressed_length, '\0'); - int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast(&content[0]), - info_entry.uncompressed_length); - if (ret != 0) { - LOG(ERROR) << "Failed to read " << info_name << ": " << ErrorCodeString(ret); - CloseArchive(zip_handle); - return false; - } - compatibility_info.emplace_back(std::move(content)); - } - CloseArchive(zip_handle); - - // VintfObjectRecovery::CheckCompatibility returns zero on success. - std::string err; - int result = android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err); - if (result == 0) { - return true; - } - - LOG(ERROR) << "Failed to verify package compatibility (result " << result << "): " << err; - return false; -} - static InstallResult VerifyAndInstallPackage(Package* package, bool* wipe_cache, std::vector* log_buffer, int retry_count, int* max_temperature, RecoveryUI* ui) { @@ -586,19 +518,6 @@ static InstallResult VerifyAndInstallPackage(Package* package, bool* wipe_cache, return INSTALL_CORRUPT; } - // Try to open the package. - ZipArchiveHandle zip = package->GetZipArchiveHandle(); - if (!zip) { - log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure)); - return INSTALL_CORRUPT; - } - - // Additionally verify the compatibility of the package if it's a fresh install. - if (retry_count == 0 && !verify_package_compatibility(zip)) { - log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure)); - return INSTALL_CORRUPT; - } - // Verify and install the contents of the package. ui->Print("Installing update...\n"); if (retry_count > 0) { diff --git a/tests/Android.bp b/tests/Android.bp index 5b881e36..dc662d88 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -81,7 +81,6 @@ librecovery_static_libs = [ "libotautil", "libhealthhalutils", - "libvintf_recovery", "libvintf", "android.hardware.health@2.0", diff --git a/tests/unit/install_test.cpp b/tests/unit/install_test.cpp index 4ec40990..370fbdcc 100644 --- a/tests/unit/install_test.cpp +++ b/tests/unit/install_test.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -50,29 +49,6 @@ static void BuildZipArchive(const std::map& file_map, ASSERT_EQ(0, fclose(zip_file)); } -TEST(InstallTest, verify_package_compatibility_no_entry) { - TemporaryFile temp_file; - // The archive must have something to be opened correctly. - BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); - - // Doesn't contain compatibility zip entry. - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - ASSERT_TRUE(verify_package_compatibility(zip)); - CloseArchive(zip); -} - -TEST(InstallTest, verify_package_compatibility_invalid_entry) { - TemporaryFile temp_file; - BuildZipArchive({ { "compatibility.zip", "" } }, temp_file.release(), kCompressStored); - - // Empty compatibility zip entry. - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - ASSERT_FALSE(verify_package_compatibility(zip)); - CloseArchive(zip); -} - TEST(InstallTest, read_metadata_from_package_smoke) { TemporaryFile temp_file; const std::string content("abc=defg"); @@ -134,64 +110,6 @@ TEST(InstallTest, read_wipe_ab_partition_list) { ASSERT_EQ(expected, read_partition_list); } -TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) { - TemporaryFile compatibility_zip_file; - std::string malformed_xml = "malformed"; - BuildZipArchive({ { "system_manifest.xml", malformed_xml } }, compatibility_zip_file.release(), - kCompressDeflated); - - TemporaryFile temp_file; - std::string compatibility_zip_content; - ASSERT_TRUE( - android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); - BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(), - kCompressStored); - - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - std::vector compatibility_info; - compatibility_info.push_back(malformed_xml); - // Malformed compatibility zip is expected to be rejected by libvintf. But we defer that to - // libvintf. - std::string err; - bool result = - android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err) == 0; - ASSERT_EQ(result, verify_package_compatibility(zip)); - CloseArchive(zip); -} - -TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml) { - static constexpr const char* system_manifest_xml_path = "/system/manifest.xml"; - if (access(system_manifest_xml_path, R_OK) == -1) { - GTEST_LOG_(INFO) << "Test skipped on devices w/o /system/manifest.xml."; - return; - } - std::string system_manifest_xml_content; - ASSERT_TRUE( - android::base::ReadFileToString(system_manifest_xml_path, &system_manifest_xml_content)); - TemporaryFile compatibility_zip_file; - BuildZipArchive({ { "system_manifest.xml", system_manifest_xml_content } }, - compatibility_zip_file.release(), kCompressDeflated); - - TemporaryFile temp_file; - std::string compatibility_zip_content; - ASSERT_TRUE( - android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); - BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(), - kCompressStored); - - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - std::vector compatibility_info; - compatibility_info.push_back(system_manifest_xml_content); - std::string err; - bool result = - android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err) == 0; - // Make sure the result is consistent with libvintf library. - ASSERT_EQ(result, verify_package_compatibility(zip)); - CloseArchive(zip); -} - TEST(InstallTest, SetUpNonAbUpdateCommands) { TemporaryFile temp_file; static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; From cf58738b8981cf053c3e11c0d683556560ac5b08 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Tue, 7 Jan 2020 11:35:06 -0800 Subject: [PATCH 134/171] Add elsk@ to OWNERS Test: none Change-Id: Ibe39304e7ad2e23377853ef5753f7873348ff24b --- OWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/OWNERS b/OWNERS index fe1c33d4..79dd9f77 100644 --- a/OWNERS +++ b/OWNERS @@ -1,3 +1,4 @@ +elsk@google.com enh@google.com nhdo@google.com xunchang@google.com From 7851fab9734cc957f85c893a5b82842933409628 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Fri, 10 Jan 2020 22:41:44 -0800 Subject: [PATCH 135/171] Import translations. DO NOT MERGE Change-Id: Iab26dd7eda26ad0d105a69939091cc7cda35c371 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-ky/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/recovery_l10n/res/values-ky/strings.xml b/tools/recovery_l10n/res/values-ky/strings.xml index 1cd69ea8..837cf7d6 100644 --- a/tools/recovery_l10n/res/values-ky/strings.xml +++ b/tools/recovery_l10n/res/values-ky/strings.xml @@ -6,7 +6,7 @@ "Буйрук берилген жок" "Ката!" "Коопсуздук жаңыртуусу орнотулууда" - "Android тутуму жүктөлбөй жатат. Дайындарыңыз бузук болушу мүмкүн. Бул билдирүү дагы деле келе берсе, түзмөктү кайра башынан жөндөп, анда сакталган бардык колдонуучу дайындарын тазалашыңыз керек." + "Android тутуму жүктөлбөй жатат. Дайын-даректериңиз бузук болушу мүмкүн. Бул билдирүү дагы деле келе берсе, түзмөктү кайра башынан жөндөп, анда сакталган бардык колдонуучу дайындарын тазалашыңыз керек." "Кайталоо" "Кайра башынан жөндөө" "Колдонуучу дайындарынын баары жашырылсынбы?\n\n МУНУ АРТКА КАЙТАРУУ МҮМКҮН ЭМЕС!" From 12952cdddb0465093c3bd869bc28c677dd78a53b Mon Sep 17 00:00:00 2001 From: Stephane Lee Date: Mon, 13 Jan 2020 15:46:36 -0800 Subject: [PATCH 136/171] Fix ioctl FIONREAD call parameters to use int instead of size_t Test: Ensure that calls to inotify_cb succeed; Ensure charger does not crash Change-Id: I9a7ca304057313c74ef02fd97223d0ed570c6206 --- minui/events.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/minui/events.cpp b/minui/events.cpp index f331ed68..87f81122 100644 --- a/minui/events.cpp +++ b/minui/events.cpp @@ -90,9 +90,11 @@ static int inotify_cb(int fd, __unused uint32_t epevents) { // The inotify will put one or several complete events. // Should not read part of one event. - size_t event_len; - int ret = ioctl(fd, FIONREAD, &event_len); + int event_len_int; + int ret = ioctl(fd, FIONREAD, &event_len_int); if (ret != 0) return -1; + if (event_len_int < 0) return -1; + size_t event_len = event_len_int; std::unique_ptr dir(opendir(INPUT_DEV_DIR), closedir); if (!dir) { From ee3dd45c0736733de139585ef73723f9b98d1f03 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Tue, 14 Jan 2020 12:54:14 -0800 Subject: [PATCH 137/171] edify: update for bison 3.5 Getting rid of warning, so using new define for turning on verbose errors: %define parse.error verbose Bug: 31194194 Test: N/A Change-Id: I47c200a1e669b5c5fc53d392f32b4c264a42182d --- edify/parser.yy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edify/parser.yy b/edify/parser.yy index 3a63c37f..37bcdd03 100644 --- a/edify/parser.yy +++ b/edify/parser.yy @@ -72,7 +72,7 @@ static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) { %parse-param {std::unique_ptr* root} %parse-param {int* error_count} -%error-verbose +%define parse.error verbose /* declarations in increasing order of precedence */ %left ';' From 5a6784168f25cf6e0139d48baa516ed510f19595 Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Wed, 15 Jan 2020 09:38:09 -0800 Subject: [PATCH 138/171] Convert update_host_simulator to Android.bp Bug: 130696912 Test: m update_host_simulator Change-Id: I7b3c0217268a3edcf76548a5c83030050b2d17f3 --- updater/Android.bp | 43 +++++++++++++++++++++++++++++++++++++------ updater/Android.mk | 29 ----------------------------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/updater/Android.bp b/updater/Android.bp index 8a60ef76..cbef4309 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -13,11 +13,7 @@ // limitations under the License. cc_defaults { - name: "libupdater_defaults", - - defaults: [ - "recovery_defaults", - ], + name: "libupdater_static_libs", static_libs: [ "libapplypatch", @@ -45,6 +41,15 @@ cc_defaults { "libcutils", "libutils", ], +} + +cc_defaults { + name: "libupdater_defaults", + + defaults: [ + "recovery_defaults", + "libupdater_static_libs", + ], shared_libs: [ "libcrypto", @@ -64,7 +69,7 @@ cc_defaults { "libext2_uuid", "libext2_e2p", "libext2fs", - ] + ], } cc_library_static { @@ -155,3 +160,29 @@ cc_library_host_static { "include", ], } + +cc_binary_host { + name: "update_host_simulator", + defaults: ["libupdater_static_libs"], + + srcs: ["update_simulator_main.cpp"], + + cflags: [ + "-Wall", + "-Werror", + ], + + static_libs: [ + "libupdater_host", + "libupdater_core", + "libcrypto_static", + "libfstab", + "libc++fs", + ], + + target: { + darwin: { + enabled: false, + }, + }, +} diff --git a/updater/Android.mk b/updater/Android.mk index 6f54d89b..8a4cd86d 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -111,32 +111,3 @@ inc := LOCAL_FORCE_STATIC_EXECUTABLE := true include $(BUILD_EXECUTABLE) - -# TODO(xunchang) move to bp file -# update_host_simulator (host executable) -# =============================== -include $(CLEAR_VARS) - -LOCAL_MODULE := update_host_simulator -LOCAL_MODULE_HOST_OS := linux - -LOCAL_SRC_FILES := \ - update_simulator_main.cpp - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include - -LOCAL_CFLAGS := \ - -Wall \ - -Werror - -LOCAL_STATIC_LIBRARIES := \ - libupdater_host \ - libupdater_core \ - $(updater_common_static_libraries) \ - libfstab \ - libc++fs - -LOCAL_MODULE_CLASS := EXECUTABLES - -include $(BUILD_HOST_EXECUTABLE) From eb27bfe1e8e64833cb697fe16913e0713bf9af8f Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 16 Jan 2020 15:17:41 -0800 Subject: [PATCH 139/171] Retire the Tron metrics reporting for non-A/B update This is part of the effort to remove libmetricslogger in platform. Remove the reporting since the status from non-A/B update is less important to us. Plus the gmscore already has a copy of the logic to parse the contents from last_install and report non-A/B metrics to the clearcut log. bug: 147776349 Test: build Change-Id: I4fc5d58fb616edb3eb1edadf4614d3eca15c7ce1 --- Android.bp | 1 - recovery-persist.cpp | 18 ------------------ 2 files changed, 19 deletions(-) diff --git a/Android.bp b/Android.bp index a8e032e2..3a8e9788 100644 --- a/Android.bp +++ b/Android.bp @@ -157,7 +157,6 @@ cc_binary { shared_libs: [ "libbase", "liblog", - "libmetricslogger", ], static_libs: [ diff --git a/recovery-persist.cpp b/recovery-persist.cpp index 6dbf8625..ad101ede 100644 --- a/recovery-persist.cpp +++ b/recovery-persist.cpp @@ -35,12 +35,10 @@ #include #include -#include #include #include #include -#include #include /* private pmsg functions */ #include "recovery_utils/logging.h" @@ -112,20 +110,6 @@ ssize_t logsave( return android::base::WriteStringToFile(buffer, destination.c_str()); } -// Parses the LAST_INSTALL file and reports the update metrics saved under recovery mode. -static void report_metrics_from_last_install(const std::string& file_name) { - auto metrics = ParseLastInstall(file_name); - // TODO(xunchang) report the installation result. - for (const auto& [event, value] : metrics) { - if (value > std::numeric_limits::max()) { - LOG(WARNING) << event << " (" << value << ") exceeds integer max."; - } else { - LOG(INFO) << "Uploading " << value << " to " << event; - android::metricslogger::LogHistogram(event, value); - } - } -} - int main(int argc, char **argv) { /* Is /cache a mount?, we have been delivered where we are not wanted */ @@ -157,7 +141,6 @@ int main(int argc, char **argv) { if (has_cache) { // Collects and reports the non-a/b update metrics from last_install; and removes the file // to avoid duplicate report. - report_metrics_from_last_install(LAST_INSTALL_FILE_IN_CACHE); if (access(LAST_INSTALL_FILE_IN_CACHE, F_OK) && unlink(LAST_INSTALL_FILE_IN_CACHE) == -1) { PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE_IN_CACHE; } @@ -181,7 +164,6 @@ int main(int argc, char **argv) { // For those device without /cache, the last_install file has been copied to // /data/misc/recovery from pmsg. Looks for the sideload history only. if (!has_cache) { - report_metrics_from_last_install(LAST_INSTALL_FILE); if (access(LAST_INSTALL_FILE, F_OK) && unlink(LAST_INSTALL_FILE) == -1) { PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE; } From 23f15ec2f26dc5e3d27f8b44cb1a12167d33033e Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 21 Jan 2020 09:03:54 -0800 Subject: [PATCH 140/171] Import translations. DO NOT MERGE Change-Id: Ie0e80dec4c698c6c6fbf5edd7a434bdcad975951 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-ky/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/recovery_l10n/res/values-ky/strings.xml b/tools/recovery_l10n/res/values-ky/strings.xml index 1cd69ea8..837cf7d6 100644 --- a/tools/recovery_l10n/res/values-ky/strings.xml +++ b/tools/recovery_l10n/res/values-ky/strings.xml @@ -6,7 +6,7 @@ "Буйрук берилген жок" "Ката!" "Коопсуздук жаңыртуусу орнотулууда" - "Android тутуму жүктөлбөй жатат. Дайындарыңыз бузук болушу мүмкүн. Бул билдирүү дагы деле келе берсе, түзмөктү кайра башынан жөндөп, анда сакталган бардык колдонуучу дайындарын тазалашыңыз керек." + "Android тутуму жүктөлбөй жатат. Дайын-даректериңиз бузук болушу мүмкүн. Бул билдирүү дагы деле келе берсе, түзмөктү кайра башынан жөндөп, анда сакталган бардык колдонуучу дайындарын тазалашыңыз керек." "Кайталоо" "Кайра башынан жөндөө" "Колдонуучу дайындарынын баары жашырылсынбы?\n\n МУНУ АРТКА КАЙТАРУУ МҮМКҮН ЭМЕС!" From ba27adbbb6db81babc1ee3ac070ea6b6189dd2e1 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 6 Aug 2019 12:32:05 -0700 Subject: [PATCH 141/171] Force package installation with FUSE unless the package stores on device The non-A/B package installation is subject to TOC/TOU flaw if the attacker can switch the package in the middle of installation. And the most pratical case is to store the package on an external device, e.g. a sdcard, and swap the device in the middle. To prevent that, we can adopt the same protection as used in sideloading a package with FUSE. Specifically, when we install the package with FUSE, we read the entire package to cryptographically verify its signature. The hash for each transfer block is recorded in the memory (TOC), and the subsequent reads (TOU) will be rejected upon dectecting a mismatch. This CL forces the package installation with FUSE when the package stays on a removable media. Bug: 136498130 Test: Run bin/recovery --update_package with various paths; and packages are installed from FUSE as expected Test: recovery_component_test - all passing Merged-In: Ibc9b095036a2fa624e8edf6c347ed4f12aef072f Change-Id: Ibc9b095036a2fa624e8edf6c347ed4f12aef072f --- Android.bp | 2 ++ install/include/install/install.h | 4 +++ install/install.cpp | 47 +++++++++++++++++++++++++++++++ recovery.cpp | 25 +++++++++++++++- tests/Android.bp | 1 + tests/component/install_test.cpp | 28 ++++++++++++++++++ 6 files changed, 106 insertions(+), 1 deletion(-) diff --git a/Android.bp b/Android.bp index f9207825..f367e5e7 100644 --- a/Android.bp +++ b/Android.bp @@ -69,6 +69,7 @@ cc_defaults { ], static_libs: [ + "libc++fs", "libinstall", "librecovery_fastboot", "libminui", @@ -94,6 +95,7 @@ cc_library_static { ], shared_libs: [ + "libfusesideload", "librecovery_ui", ], } diff --git a/install/include/install/install.h b/install/include/install/install.h index c0a8f1f4..ed0f6c7d 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -69,3 +69,7 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip); // 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& metadata, OtaType ota_type); + +// Ensures the path to the update package is mounted. Also set the |should_use_fuse| to true if the +// package stays on a removable media. +bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse); diff --git a/install/install.cpp b/install/install.cpp index e2d47009..9203ef0e 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -736,3 +737,49 @@ bool verify_package(Package* package, RecoveryUI* ui) { } return true; } + +bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse) { + CHECK(should_use_fuse != nullptr); + + if (package_path.empty()) { + return false; + } + + *should_use_fuse = true; + if (package_path[0] == '@') { + auto block_map_path = package_path.substr(1); + if (ensure_path_mounted(block_map_path) != 0) { + LOG(ERROR) << "Failed to mount " << block_map_path; + return false; + } + // uncrypt only produces block map only if the package stays on /data. + *should_use_fuse = false; + return true; + } + + // Package is not a block map file. + if (ensure_path_mounted(package_path) != 0) { + LOG(ERROR) << "Failed to mount " << package_path; + return false; + } + + // Reject the package if the input path doesn't equal the canonicalized path. + // e.g. /cache/../sdcard/update_package. + std::error_code ec; + auto canonical_path = std::filesystem::canonical(package_path, ec); + if (ec) { + LOG(ERROR) << "Failed to get canonical of " << package_path << ", " << ec.message(); + return false; + } + if (canonical_path.string() != package_path) { + LOG(ERROR) << "Installation aborts. The canonical path " << canonical_path.string() + << " doesn't equal the original path " << package_path; + return false; + } + + constexpr const char* CACHE_ROOT = "/cache"; + if (android::base::StartsWith(package_path, CACHE_ROOT)) { + *should_use_fuse = false; + } + return true; +} diff --git a/recovery.cpp b/recovery.cpp index 5fc673ec..e51687a7 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -50,6 +50,7 @@ #include "common.h" #include "fsck_unshare_blocks.h" +#include "fuse_sideload.h" #include "install/adb_install.h" #include "install/fuse_sdcard_install.h" #include "install/install.h" @@ -881,7 +882,29 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector(update_package, 65536); + status = run_fuse_sideload(std::move(file_data_reader)); + } else if (auto memory_package = Package::CreateMemoryPackage( + update_package, + std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); + memory_package != nullptr) { + status = install_package(update_package, should_wipe_cache, true, retry_count, ui); + } else { + // We may fail to memory map the package on 32 bit builds for packages with 2GiB+ size. + // In such cases, we will try to install the package with fuse. This is not the default + // installation method because it introduces a layer of indirection from the kernel space. + LOG(WARNING) << "Failed to memory map package " << update_package + << "; falling back to install with fuse"; + auto file_data_reader = std::make_unique(update_package, 65536); + status = run_fuse_sideload(std::move(file_data_reader)); + } + if (status != INSTALL_SUCCESS) { ui->Print("Installation aborted.\n"); diff --git a/tests/Android.bp b/tests/Android.bp index 09ef716d..a0d82d5f 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -99,6 +99,7 @@ librecovery_static_libs = [ "liblp", "libvndksupport", "libtinyxml2", + "libc++fs", ] cc_test { diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index 38513293..c1f0ca81 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -34,6 +34,7 @@ #include "install/install.h" #include "otautil/paths.h" +#include "otautil/roots.h" #include "private/setup_commands.h" static void BuildZipArchive(const std::map& file_map, int fd, @@ -595,3 +596,30 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { "\n"); test_check_package_metadata(metadata, OtaType::AB, 0); } + +TEST(InstallTest, SetupPackageMount_package_path) { + load_volume_table(); + bool install_with_fuse; + + // Setup should fail if the input path doesn't exist. + ASSERT_FALSE(SetupPackageMount("/does_not_exist", &install_with_fuse)); + + // Package should be installed with fuse if it's not in /cache. + TemporaryDir temp_dir; + TemporaryFile update_package(temp_dir.path); + ASSERT_TRUE(SetupPackageMount(update_package.path, &install_with_fuse)); + ASSERT_TRUE(install_with_fuse); + + // Setup should fail if the input path isn't canonicalized. + std::string uncanonical_package_path = android::base::Join( + std::vector{ + temp_dir.path, + "..", + android::base::Basename(temp_dir.path), + android::base::Basename(update_package.path), + }, + '/'); + + ASSERT_EQ(0, access(uncanonical_package_path.c_str(), R_OK)); + ASSERT_FALSE(SetupPackageMount(uncanonical_package_path, &install_with_fuse)); +} From 5e6c4e9a91674826bf11cab604250b41a9326fd8 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 6 Aug 2019 12:32:05 -0700 Subject: [PATCH 142/171] Force package installation with FUSE unless the package stores on device The non-A/B package installation is subject to TOC/TOU flaw if the attacker can switch the package in the middle of installation. And the most pratical case is to store the package on an external device, e.g. a sdcard, and swap the device in the middle. To prevent that, we can adopt the same protection as used in sideloading a package with FUSE. Specifically, when we install the package with FUSE, we read the entire package to cryptographically verify its signature. The hash for each transfer block is recorded in the memory (TOC), and the subsequent reads (TOU) will be rejected upon dectecting a mismatch. This CL forces the package installation with FUSE when the package stays on a removable media. Bug: 136498130 Test: Run bin/recovery --update_package with various paths; and packages are installed from FUSE as expected Test: recovery_component_test - all passing Change-Id: Ibc9b095036a2fa624e8edf6c347ed4f12aef072f Merged-In: Ibc9b095036a2fa624e8edf6c347ed4f12aef072f --- Android.bp | 2 ++ install/include/install/install.h | 4 +++ install/install.cpp | 47 +++++++++++++++++++++++++++++++ recovery.cpp | 6 +++- tests/Android.bp | 1 + tests/unit/install_test.cpp | 28 ++++++++++++++++++ 6 files changed, 87 insertions(+), 1 deletion(-) diff --git a/Android.bp b/Android.bp index 3a8e9788..bda36c2f 100644 --- a/Android.bp +++ b/Android.bp @@ -72,6 +72,7 @@ cc_defaults { ], static_libs: [ + "libc++fs", "libinstall", "librecovery_fastboot", "libminui", @@ -95,6 +96,7 @@ cc_library_static { ], shared_libs: [ + "libfusesideload", "librecovery_ui", ], } diff --git a/install/include/install/install.h b/install/include/install/install.h index 87d43ab0..bef23e9c 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -63,3 +63,7 @@ bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map& metadata, OtaType ota_type); + +// Ensures the path to the update package is mounted. Also set the |should_use_fuse| to true if the +// package stays on a removable media. +bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse); diff --git a/install/install.cpp b/install/install.cpp index 4bb0903c..1c9bf2fd 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -641,3 +642,49 @@ bool verify_package(Package* package, RecoveryUI* ui) { } return true; } + +bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse) { + CHECK(should_use_fuse != nullptr); + + if (package_path.empty()) { + return false; + } + + *should_use_fuse = true; + if (package_path[0] == '@') { + auto block_map_path = package_path.substr(1); + if (ensure_path_mounted(block_map_path) != 0) { + LOG(ERROR) << "Failed to mount " << block_map_path; + return false; + } + // uncrypt only produces block map only if the package stays on /data. + *should_use_fuse = false; + return true; + } + + // Package is not a block map file. + if (ensure_path_mounted(package_path) != 0) { + LOG(ERROR) << "Failed to mount " << package_path; + return false; + } + + // Reject the package if the input path doesn't equal the canonicalized path. + // e.g. /cache/../sdcard/update_package. + std::error_code ec; + auto canonical_path = std::filesystem::canonical(package_path, ec); + if (ec) { + LOG(ERROR) << "Failed to get canonical of " << package_path << ", " << ec.message(); + return false; + } + if (canonical_path.string() != package_path) { + LOG(ERROR) << "Installation aborts. The canonical path " << canonical_path.string() + << " doesn't equal the original path " << package_path; + return false; + } + + constexpr const char* CACHE_ROOT = "/cache"; + if (android::base::StartsWith(package_path, CACHE_ROOT)) { + *should_use_fuse = false; + } + return true; +} diff --git a/recovery.cpp b/recovery.cpp index e4b8e45f..9747aae8 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -687,7 +687,11 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector& file_map, int fd, @@ -513,3 +514,30 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { "\n"); TestCheckPackageMetadata(metadata, OtaType::AB, true); } + +TEST(InstallTest, SetupPackageMount_package_path) { + load_volume_table(); + bool install_with_fuse; + + // Setup should fail if the input path doesn't exist. + ASSERT_FALSE(SetupPackageMount("/does_not_exist", &install_with_fuse)); + + // Package should be installed with fuse if it's not in /cache. + TemporaryDir temp_dir; + TemporaryFile update_package(temp_dir.path); + ASSERT_TRUE(SetupPackageMount(update_package.path, &install_with_fuse)); + ASSERT_TRUE(install_with_fuse); + + // Setup should fail if the input path isn't canonicalized. + std::string uncanonical_package_path = android::base::Join( + std::vector{ + temp_dir.path, + "..", + android::base::Basename(temp_dir.path), + android::base::Basename(update_package.path), + }, + '/'); + + ASSERT_EQ(0, access(uncanonical_package_path.c_str(), R_OK)); + ASSERT_FALSE(SetupPackageMount(uncanonical_package_path, &install_with_fuse)); +} From cd46c1a860bd16853d097ff4990dabbcaf04723c Mon Sep 17 00:00:00 2001 From: "P.Adarsh Reddy" Date: Thu, 23 Jan 2020 19:23:55 +0530 Subject: [PATCH 143/171] Mark libedify library as recovery_available. Mark libedify library as recovery_available so that this is available in recovery. Bug: 148189099 Test: Tested compilation, library is recovery available. Functionality wise, tested sideloading in recovery. Change-Id: Ie4a7a3af1fd32b7ec1bf2016938550e9312a519b --- edify/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/edify/Android.bp b/edify/Android.bp index 73048d21..0ab53d6d 100644 --- a/edify/Android.bp +++ b/edify/Android.bp @@ -17,6 +17,7 @@ cc_library_static { host_supported: true, vendor_available: true, + recovery_available: true, srcs: [ "expr.cpp", From f25b977740e388b458fcdbb8e3c43b957f0beaf3 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 16 Dec 2019 18:32:00 -0800 Subject: [PATCH 144/171] Set Casefold and PrjQuotas in Factory Reset This sets up devices to have casefolding and project quotas if they have enabled the relevant product properties. For ext4, we must set wide inodes at makefs time, but other things are enabled by fs_mgr with tune2fs at boot time. Bug: 138321217 Bug: 138322712 Test: Enable on a device. Reformat from recovery. Check that /data has the expected values using tune2fs/f2fs.fsck Change-Id: If706a3cad591f311eb904df4deca531d0b3fe0c4 --- recovery_utils/roots.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/recovery_utils/roots.cpp b/recovery_utils/roots.cpp index fe3a07aa..12703987 100644 --- a/recovery_utils/roots.cpp +++ b/recovery_utils/roots.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -152,6 +153,14 @@ int format_volume(const std::string& volume, const std::string& directory) { return -1; } + bool needs_casefold = false; + bool needs_projid = false; + + if (volume == "/data") { + needs_casefold = android::base::GetBoolProperty("ro.emulated_storage.casefold", false); + needs_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false); + } + // If there's a key_loc that looks like a path, it should be a block device for storing encryption // metadata. Wipe it too. if (!v->key_loc.empty() && v->key_loc[0] == '/') { @@ -187,6 +196,12 @@ int format_volume(const std::string& volume, const std::string& directory) { "/system/bin/mke2fs", "-F", "-t", "ext4", "-b", std::to_string(kBlockSize), }; + // Project ID's require wider inodes. The Quotas themselves are enabled by tune2fs on boot. + if (needs_projid) { + mke2fs_args.push_back("-I"); + mke2fs_args.push_back("512"); + } + int raid_stride = v->logical_blk_size / kBlockSize; int raid_stripe_width = v->erase_blk_size / kBlockSize; // stride should be the max of 8KB and logical block size @@ -224,8 +239,18 @@ int format_volume(const std::string& volume, const std::string& directory) { "/system/bin/make_f2fs", "-g", "android", - v->blk_device, }; + if (needs_projid) { + make_f2fs_cmd.push_back("-O"); + make_f2fs_cmd.push_back("project_quota,extra_attr"); + } + if (needs_casefold) { + make_f2fs_cmd.push_back("-O"); + make_f2fs_cmd.push_back("casefold"); + make_f2fs_cmd.push_back("-C"); + make_f2fs_cmd.push_back("utf8"); + } + make_f2fs_cmd.push_back(v->blk_device); if (length >= kSectorSize) { make_f2fs_cmd.push_back(std::to_string(length / kSectorSize)); } From 969787cffdf057bb8b4d3995423d818c15b52e9f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 27 Jan 2020 09:29:02 -0800 Subject: [PATCH 145/171] Remove fsck_unshare_blocks. This code is dead. It was briefly used to support "adb remount" with deduplicated partitions, but was very quickly obsoleted by overlayfs support. There is no reason to include it anymore. Bug: N/A Test: N/A Change-Id: I4cdcbf66bec80092f954826eaae037934ff37765 --- Android.bp | 1 - fsck_unshare_blocks.cpp | 151 ---------------------------------------- fsck_unshare_blocks.h | 22 ------ recovery.cpp | 11 +-- 4 files changed, 1 insertion(+), 184 deletions(-) delete mode 100644 fsck_unshare_blocks.cpp delete mode 100644 fsck_unshare_blocks.h diff --git a/Android.bp b/Android.bp index 3a8e9788..4032bcc1 100644 --- a/Android.bp +++ b/Android.bp @@ -90,7 +90,6 @@ cc_library_static { ], srcs: [ - "fsck_unshare_blocks.cpp", "recovery.cpp", ], diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp deleted file mode 100644 index e0b2d966..00000000 --- a/fsck_unshare_blocks.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fsck_unshare_blocks.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "recovery_utils/roots.h" - -static constexpr const char* SYSTEM_E2FSCK_BIN = "/system/bin/e2fsck_static"; -static constexpr const char* TMP_E2FSCK_BIN = "/tmp/e2fsck.bin"; - -static bool copy_file(const char* source, const char* dest) { - android::base::unique_fd source_fd(open(source, O_RDONLY)); - if (source_fd < 0) { - PLOG(ERROR) << "open %s failed" << source; - return false; - } - - android::base::unique_fd dest_fd(open(dest, O_CREAT | O_WRONLY, S_IRWXU)); - if (dest_fd < 0) { - PLOG(ERROR) << "open %s failed" << dest; - return false; - } - - for (;;) { - char buf[4096]; - ssize_t rv = read(source_fd, buf, sizeof(buf)); - if (rv < 0) { - PLOG(ERROR) << "read failed"; - return false; - } - if (rv == 0) { - break; - } - if (write(dest_fd, buf, rv) != rv) { - PLOG(ERROR) << "write failed"; - return false; - } - } - return true; -} - -static bool run_e2fsck(const std::string& partition) { - Volume* volume = volume_for_mount_point(partition); - if (!volume) { - LOG(INFO) << "No fstab entry for " << partition << ", skipping."; - return true; - } - - LOG(INFO) << "Running e2fsck on device " << volume->blk_device; - - std::vector args = { TMP_E2FSCK_BIN, "-p", "-E", "unshare_blocks", - volume->blk_device }; - std::vector argv(args.size()); - std::transform(args.cbegin(), args.cend(), argv.begin(), - [](const std::string& arg) { return const_cast(arg.c_str()); }); - argv.push_back(nullptr); - - pid_t child; - char* env[] = { nullptr }; - if (posix_spawn(&child, argv[0], nullptr, nullptr, argv.data(), env)) { - PLOG(ERROR) << "posix_spawn failed"; - return false; - } - - int status = 0; - int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0)); - if (ret < 0) { - PLOG(ERROR) << "waitpid failed"; - return false; - } - if (!WIFEXITED(status)) { - LOG(ERROR) << "e2fsck exited abnormally: " << status; - return false; - } - int return_code = WEXITSTATUS(status); - if (return_code >= 8) { - LOG(ERROR) << "e2fsck could not unshare blocks: " << return_code; - return false; - } - - LOG(INFO) << "Successfully unshared blocks on " << partition; - return true; -} - -bool do_fsck_unshare_blocks() { - // List of partitions we will try to e2fsck -E unshare_blocks. - std::vector partitions = { "/odm", "/oem", "/product", "/vendor" }; - - // Temporarily mount system so we can copy e2fsck_static. - auto system_root = android::fs_mgr::GetSystemRoot(); - bool mounted = ensure_path_mounted_at(system_root, "/mnt/system") != -1; - partitions.push_back(system_root); - - if (!mounted) { - LOG(ERROR) << "Failed to mount system image."; - return false; - } - if (!copy_file(SYSTEM_E2FSCK_BIN, TMP_E2FSCK_BIN)) { - LOG(ERROR) << "Could not copy e2fsck to /tmp."; - return false; - } - if (umount("/mnt/system") < 0) { - PLOG(ERROR) << "umount failed"; - return false; - } - - bool ok = true; - for (const auto& partition : partitions) { - ok &= run_e2fsck(partition); - } - - if (ok) { - LOG(INFO) << "Finished running e2fsck."; - } else { - LOG(ERROR) << "Finished running e2fsck, but not all partitions succceeded."; - } - return ok; -} diff --git a/fsck_unshare_blocks.h b/fsck_unshare_blocks.h deleted file mode 100644 index 9de8ef9a..00000000 --- a/fsck_unshare_blocks.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _FILESYSTEM_CMDS_H -#define _FILESYSTEM_CMDS_H - -bool do_fsck_unshare_blocks(); - -#endif // _FILESYSTEM_CMDS_H diff --git a/recovery.cpp b/recovery.cpp index e4b8e45f..cbf29b6c 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -45,7 +45,6 @@ #include #include "bootloader_message/bootloader_message.h" -#include "fsck_unshare_blocks.h" #include "install/adb_install.h" #include "install/fuse_install.h" #include "install/install.h" @@ -524,7 +523,6 @@ static void log_failure_code(ErrorCode code, const std::string& update_package) Device::BuiltinAction start_recovery(Device* device, const std::vector& args) { static constexpr struct option OPTIONS[] = { { "fastboot", no_argument, nullptr, 0 }, - { "fsck_unshare_blocks", no_argument, nullptr, 0 }, { "install_with_fuse", no_argument, nullptr, 0 }, { "just_exit", no_argument, nullptr, 'x' }, { "locale", required_argument, nullptr, 0 }, @@ -557,7 +555,6 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("\nInstall from ADB complete (status: %d).\n", status); - } else if (fsck_unshare_blocks) { - if (!do_fsck_unshare_blocks()) { - status = INSTALL_ERROR; - } } else if (!just_exit) { // If this is an eng or userdebug build, automatically turn on the text display if no command // is specified. Note that this should be called before setting the background to avoid From c9163fdacca67daeeb946e291d76a8437865bf49 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Mon, 27 Jan 2020 17:37:39 -0800 Subject: [PATCH 146/171] Prompt for confirmation before reboot if installation fails. If previous installation fails, menu item 'Reboot system now' and 'Power off' now prompts for confirmation from the user. Known issues: - If the sideload is interrupted, it'll still boot into normal Android in the next cycle. - If 'Enter fastbootd' is chosen, and then 'Enter recovery', such prompt do not show up. Test: manual Fixes: 142892891 Change-Id: I929b80e0520bd3b9f56d88a4b2203fcdd8d7b013 --- recovery.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index cbf29b6c..a9d2458d 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -310,6 +310,62 @@ static void run_graphics_test(RecoveryUI* ui) { ui->ShowText(true); } +static bool AskToReboot(Device* device, Device::BuiltinAction chosen_action, InstallResult status) { + switch (status) { + case INSTALL_SUCCESS: + case INSTALL_NONE: + case INSTALL_SKIPPED: + case INSTALL_RETRY: + case INSTALL_KEY_INTERRUPTED: + // okay to reboot; no need to ask. + return true; + case INSTALL_ERROR: + case INSTALL_CORRUPT: + // need to ask + break; + case INSTALL_REBOOT: + // All the reboots should have been handled prior to entering AskToReboot() or immediately + // after installing a package. + LOG(FATAL) << "Invalid status code of INSTALL_REBOOT"; + break; + } + + bool is_non_ab = android::base::GetProperty("ro.boot.slot_suffix", "").empty(); + bool is_virtual_ab = android::base::GetBoolProperty("ro.virtual_ab.enabled", false); + if (!is_non_ab && !is_virtual_ab) { + // Only prompt for non-A/B or Virtual A/B devices. + return true; + } + + std::string header_text; + std::string item_text; + switch (chosen_action) { + case Device::REBOOT: + header_text = "reboot"; + item_text = " Reboot system now"; + break; + case Device::SHUTDOWN: + header_text = "power off"; + item_text = " Power off"; + break; + default: + LOG(FATAL) << "Invalid chosen action " << chosen_action; + break; + } + + std::vector headers{ "Previous installation has failed.", + " Your device may fail to boot if you " + header_text + + " now.", + " Confirm reboot?" }; + std::vector items{ " Cancel", item_text }; + + size_t chosen_item = device->GetUI()->ShowMenu( + headers, items, 0, true /* menu_only */, + std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); + + return (chosen_item == 1); +} + // Shows the recovery UI and waits for user input. Returns one of the device builtin actions, such // as REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default, which // is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery. @@ -361,14 +417,19 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) case Device::ENTER_FASTBOOT: case Device::ENTER_RECOVERY: - case Device::REBOOT: case Device::REBOOT_BOOTLOADER: case Device::REBOOT_FASTBOOT: case Device::REBOOT_RECOVERY: case Device::REBOOT_RESCUE: - case Device::SHUTDOWN: return chosen_action; + case Device::REBOOT: + case Device::SHUTDOWN: + if (!ui->IsTextVisible() || AskToReboot(device, chosen_action, status)) { + return Device::REBOOT; + } + break; + case Device::WIPE_DATA: save_current_log = true; if (ui->IsTextVisible()) { From 2b9be1f4e385d74de0cc8a0d50ae8d9e9ecd21e4 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 29 Jan 2020 15:36:57 -0800 Subject: [PATCH 147/171] Import translations. DO NOT MERGE Change-Id: Id8dd55dd5f36191402ba7f6b44e107f205fbf484 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-ar/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml index 2af36d64..a9cd2d13 100644 --- a/tools/recovery_l10n/res/values-ar/strings.xml +++ b/tools/recovery_l10n/res/values-ar/strings.xml @@ -6,9 +6,9 @@ "ليس هناك أي أمر" "خطأ!" "جارٍ تثبيت تحديث الأمان" - "‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط بحسب بيانات المصنع ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز." + "‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط على الإعدادات الأصلية ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز." "إعادة المحاولة" - "إعادة الضبط بحسب بيانات المصنع" + "إعادة الضبط على الإعدادات الأصلية" "هل تريد حجب كل بيانات المستخدم؟\n\n لا يمكن التراجع عن هذا الإجراء." "إلغاء" From 13b099696f01aa5a2b80c3208d040dcbd176279d Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Wed, 29 Jan 2020 16:51:33 -0800 Subject: [PATCH 148/171] Show warning message if sideload failed or is interrupted. For non A/B and Virtual A/B devices where sideloading may affect the existing OS, - If sideload has failed, show a warning message in recovery menu header. - If sideload has interrupted, automatically reboot back into recovery and show the warning message in recovery menu header. Test: the above Fixes: 140749209 Change-Id: Ifdfc28b45975cdc31b6fce2ecb99acc31bc61fa8 --- recovery.cpp | 57 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index a9d2458d..9ea616e1 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -310,26 +310,14 @@ static void run_graphics_test(RecoveryUI* ui) { ui->ShowText(true); } -static bool AskToReboot(Device* device, Device::BuiltinAction chosen_action, InstallResult status) { - switch (status) { - case INSTALL_SUCCESS: - case INSTALL_NONE: - case INSTALL_SKIPPED: - case INSTALL_RETRY: - case INSTALL_KEY_INTERRUPTED: - // okay to reboot; no need to ask. - return true; - case INSTALL_ERROR: - case INSTALL_CORRUPT: - // need to ask - break; - case INSTALL_REBOOT: - // All the reboots should have been handled prior to entering AskToReboot() or immediately - // after installing a package. - LOG(FATAL) << "Invalid status code of INSTALL_REBOOT"; - break; +static void WriteUpdateInProgress() { + std::string err; + if (!update_bootloader_message({ "--reason=update_in_progress" }, &err)) { + LOG(ERROR) << "Failed to WriteUpdateInProgress: " << err; } +} +static bool AskToReboot(Device* device, Device::BuiltinAction chosen_action) { bool is_non_ab = android::base::GetProperty("ro.boot.slot_suffix", "").empty(); bool is_virtual_ab = android::base::GetBoolProperty("ro.virtual_ab.enabled", false); if (!is_non_ab && !is_virtual_ab) { @@ -353,7 +341,7 @@ static bool AskToReboot(Device* device, Device::BuiltinAction chosen_action, Ins break; } - std::vector headers{ "Previous installation has failed.", + std::vector headers{ "WARNING: Previous installation has failed.", " Your device may fail to boot if you " + header_text + " now.", " Confirm reboot?" }; @@ -371,6 +359,7 @@ static bool AskToReboot(Device* device, Device::BuiltinAction chosen_action, Ins // is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery. static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) { auto ui = device->GetUI(); + bool update_in_progress = (device->GetReason().value_or("") == "update_in_progress"); for (;;) { FinishRecovery(ui); switch (status) { @@ -395,8 +384,14 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) } ui->SetProgressType(RecoveryUI::EMPTY); + std::vector headers; + if (update_in_progress) { + headers = { "WARNING: Previous installation has failed.", + " Your device may fail to boot if you reboot or power off now." }; + } + size_t chosen_item = ui->ShowMenu( - {}, device->GetMenuItems(), 0, false, + headers, device->GetMenuItems(), 0, false, std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); // Handle Interrupt key if (chosen_item == static_cast(RecoveryUI::KeyError::INTERRUPTED)) { @@ -425,7 +420,15 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) case Device::REBOOT: case Device::SHUTDOWN: - if (!ui->IsTextVisible() || AskToReboot(device, chosen_action, status)) { + if (!ui->IsTextVisible()) { + return Device::REBOOT; + } + // okay to reboot; no need to ask. + if (!update_in_progress) { + return Device::REBOOT; + } + // An update might have been failed. Ask if user really wants to reboot. + if (AskToReboot(device, chosen_action)) { return Device::REBOOT; } break; @@ -457,6 +460,9 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) case Device::ENTER_RESCUE: { save_current_log = true; + update_in_progress = true; + WriteUpdateInProgress(); + bool adb = true; Device::BuiltinAction reboot_action; if (chosen_action == Device::ENTER_RESCUE) { @@ -475,12 +481,15 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) return reboot_action; } - if (status != INSTALL_SUCCESS) { + if (status == INSTALL_SUCCESS) { + update_in_progress = false; + if (!ui->IsTextVisible()) { + return Device::NO_ACTION; // reboot if logs aren't visible + } + } else { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); copy_logs(save_current_log); - } else if (!ui->IsTextVisible()) { - return Device::NO_ACTION; // reboot if logs aren't visible } break; } From 3147b682cf43e3d1edd30d2f7d32a4430f594593 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 1 Feb 2020 07:19:31 -0800 Subject: [PATCH 149/171] Import translations. DO NOT MERGE Change-Id: I81fcbdc2d6c21f6ffdefcf3c10792ebe5ade6e18 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-ar/strings.xml | 4 ++-- tools/recovery_l10n/res/values-gl/strings.xml | 4 ++-- tools/recovery_l10n/res/values-in/strings.xml | 2 +- tools/recovery_l10n/res/values-ja/strings.xml | 2 +- tools/recovery_l10n/res/values-ky/strings.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml index 2af36d64..a9cd2d13 100644 --- a/tools/recovery_l10n/res/values-ar/strings.xml +++ b/tools/recovery_l10n/res/values-ar/strings.xml @@ -6,9 +6,9 @@ "ليس هناك أي أمر" "خطأ!" "جارٍ تثبيت تحديث الأمان" - "‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط بحسب بيانات المصنع ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز." + "‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط على الإعدادات الأصلية ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز." "إعادة المحاولة" - "إعادة الضبط بحسب بيانات المصنع" + "إعادة الضبط على الإعدادات الأصلية" "هل تريد حجب كل بيانات المستخدم؟\n\n لا يمكن التراجع عن هذا الإجراء." "إلغاء" diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml index e51b36df..e6f2ffd8 100644 --- a/tools/recovery_l10n/res/values-gl/strings.xml +++ b/tools/recovery_l10n/res/values-gl/strings.xml @@ -6,9 +6,9 @@ "Non hai ningún comando" "Erro" "Instalando actualización de seguranza" - "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." + "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." "Tentar de novo" "Restablecemento dos datos de fábrica" - "Queres borrar todos os datos de usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER." + "Queres borrar todos os datos do usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER." "Cancelar" diff --git a/tools/recovery_l10n/res/values-in/strings.xml b/tools/recovery_l10n/res/values-in/strings.xml index 15a78ec4..43c9deb9 100644 --- a/tools/recovery_l10n/res/values-in/strings.xml +++ b/tools/recovery_l10n/res/values-in/strings.xml @@ -9,6 +9,6 @@ "Tidak dapat memuat sistem Android. Data Anda mungkin rusak. Jika terus mendapatkan pesan ini, Anda mungkin perlu melakukan reset ke setelan pabrik dan menghapus semua data pengguna yang disimpan di perangkat ini." "Coba lagi" "Reset ke setelan pabrik" - "Wipe semua data pengguna?\n\n TINDAKAN INI TIDAK DAPAT DIURUNGKAN!" + "Hapus total semua data pengguna?\n\n TINDAKAN INI TIDAK DAPAT DIURUNGKAN!" "Batal" diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml index 3d663727..2d6c0abc 100644 --- a/tools/recovery_l10n/res/values-ja/strings.xml +++ b/tools/recovery_l10n/res/values-ja/strings.xml @@ -6,7 +6,7 @@ "コマンドが指定されていません" "エラーが発生しました。" "セキュリティ アップデートをインストールしています" - "Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、この端末に保存されているすべてのユーザー データを消去することが必要な場合があります。" + "Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、このデバイスに保存されているすべてのユーザー データを消去することが必要な場合があります。" "再試行" "データの初期化" "すべてのユーザー データをワイプしますか?\n\nこの操作は元に戻せません。" diff --git a/tools/recovery_l10n/res/values-ky/strings.xml b/tools/recovery_l10n/res/values-ky/strings.xml index 1cd69ea8..837cf7d6 100644 --- a/tools/recovery_l10n/res/values-ky/strings.xml +++ b/tools/recovery_l10n/res/values-ky/strings.xml @@ -6,7 +6,7 @@ "Буйрук берилген жок" "Ката!" "Коопсуздук жаңыртуусу орнотулууда" - "Android тутуму жүктөлбөй жатат. Дайындарыңыз бузук болушу мүмкүн. Бул билдирүү дагы деле келе берсе, түзмөктү кайра башынан жөндөп, анда сакталган бардык колдонуучу дайындарын тазалашыңыз керек." + "Android тутуму жүктөлбөй жатат. Дайын-даректериңиз бузук болушу мүмкүн. Бул билдирүү дагы деле келе берсе, түзмөктү кайра башынан жөндөп, анда сакталган бардык колдонуучу дайындарын тазалашыңыз керек." "Кайталоо" "Кайра башынан жөндөө" "Колдонуучу дайындарынын баары жашырылсынбы?\n\n МУНУ АРТКА КАЙТАРУУ МҮМКҮН ЭМЕС!" From c7647926f4209c1aef5b643b247e737c444fef44 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Thu, 6 Feb 2020 13:28:52 -0800 Subject: [PATCH 150/171] rm libbinderthreadstate This library is empty, and its functionality has moved into libbinder/libhwbinder. Bug: 148692216 Test: N/A Change-Id: Ie50d9130a8e43de7d5b222883169c26ab958e6d7 --- tests/Android.bp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Android.bp b/tests/Android.bp index e49d966c..bde1bc5f 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -90,7 +90,6 @@ librecovery_static_libs = [ "libfs_mgr", "libhidl-gen-utils", "libhidlbase", - "libbinderthreadstate", "liblp", "libtinyxml2", ] From a8d36e1ea913ddc88a3bd5ddc630188168e9b009 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 11 Feb 2020 15:24:54 -0800 Subject: [PATCH 151/171] recovery: add a way to enable metadata_csum for ext4 Requires to add "metadata_csum" in fsmgr_flag of fstab. Bug: 149039306 Change-Id: I2b95dcaaf2ba224135ad51f117b7b01bbf342b7e Signed-off-by: Jaegeuk Kim --- recovery_utils/roots.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/recovery_utils/roots.cpp b/recovery_utils/roots.cpp index 12703987..58a31397 100644 --- a/recovery_utils/roots.cpp +++ b/recovery_utils/roots.cpp @@ -202,6 +202,15 @@ int format_volume(const std::string& volume, const std::string& directory) { mke2fs_args.push_back("512"); } + if (v->fs_mgr_flags.ext_meta_csum) { + mke2fs_args.push_back("-O"); + mke2fs_args.push_back("metadata_csum"); + mke2fs_args.push_back("-O"); + mke2fs_args.push_back("64bit"); + mke2fs_args.push_back("-O"); + mke2fs_args.push_back("extent"); + } + int raid_stride = v->logical_blk_size / kBlockSize; int raid_stripe_width = v->erase_blk_size / kBlockSize; // stride should be the max of 8KB and logical block size From daaacea96ebfc55b47f44240e2082c820c8eb234 Mon Sep 17 00:00:00 2001 From: Raman Tenneti Date: Thu, 13 Feb 2020 02:50:42 +0000 Subject: [PATCH 152/171] Revert "Force package installation with FUSE unless the package stores on device" This reverts commit 5e6c4e9a91674826bf11cab604250b41a9326fd8. Reason for revert: BUG: 149432069 - build failure on git_qt-qpr1-dev-plus-aosp on docs. 'otautil/roots.h' file not found is the error. Forrest run: https://android-build.googleplex.com/builds/forrest/run/L85900000460577420 Change-Id: I35119c2334895aa0ef4ed71b3ddd08f280c0c031 Merged-In: I35119c2334895aa0ef4ed71b3ddd08f280c0c031 --- Android.bp | 2 -- install/include/install/install.h | 4 --- install/install.cpp | 47 ------------------------------- recovery.cpp | 6 +--- tests/Android.bp | 1 - tests/unit/install_test.cpp | 28 ------------------ 6 files changed, 1 insertion(+), 87 deletions(-) diff --git a/Android.bp b/Android.bp index bda36c2f..3a8e9788 100644 --- a/Android.bp +++ b/Android.bp @@ -72,7 +72,6 @@ cc_defaults { ], static_libs: [ - "libc++fs", "libinstall", "librecovery_fastboot", "libminui", @@ -96,7 +95,6 @@ cc_library_static { ], shared_libs: [ - "libfusesideload", "librecovery_ui", ], } diff --git a/install/include/install/install.h b/install/include/install/install.h index bef23e9c..87d43ab0 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -63,7 +63,3 @@ bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map& metadata, OtaType ota_type); - -// Ensures the path to the update package is mounted. Also set the |should_use_fuse| to true if the -// package stays on a removable media. -bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse); diff --git a/install/install.cpp b/install/install.cpp index 1c9bf2fd..4bb0903c 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -642,49 +641,3 @@ bool verify_package(Package* package, RecoveryUI* ui) { } return true; } - -bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse) { - CHECK(should_use_fuse != nullptr); - - if (package_path.empty()) { - return false; - } - - *should_use_fuse = true; - if (package_path[0] == '@') { - auto block_map_path = package_path.substr(1); - if (ensure_path_mounted(block_map_path) != 0) { - LOG(ERROR) << "Failed to mount " << block_map_path; - return false; - } - // uncrypt only produces block map only if the package stays on /data. - *should_use_fuse = false; - return true; - } - - // Package is not a block map file. - if (ensure_path_mounted(package_path) != 0) { - LOG(ERROR) << "Failed to mount " << package_path; - return false; - } - - // Reject the package if the input path doesn't equal the canonicalized path. - // e.g. /cache/../sdcard/update_package. - std::error_code ec; - auto canonical_path = std::filesystem::canonical(package_path, ec); - if (ec) { - LOG(ERROR) << "Failed to get canonical of " << package_path << ", " << ec.message(); - return false; - } - if (canonical_path.string() != package_path) { - LOG(ERROR) << "Installation aborts. The canonical path " << canonical_path.string() - << " doesn't equal the original path " << package_path; - return false; - } - - constexpr const char* CACHE_ROOT = "/cache"; - if (android::base::StartsWith(package_path, CACHE_ROOT)) { - *should_use_fuse = false; - } - return true; -} diff --git a/recovery.cpp b/recovery.cpp index 9747aae8..e4b8e45f 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -687,11 +687,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector& file_map, int fd, @@ -514,30 +513,3 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { "\n"); TestCheckPackageMetadata(metadata, OtaType::AB, true); } - -TEST(InstallTest, SetupPackageMount_package_path) { - load_volume_table(); - bool install_with_fuse; - - // Setup should fail if the input path doesn't exist. - ASSERT_FALSE(SetupPackageMount("/does_not_exist", &install_with_fuse)); - - // Package should be installed with fuse if it's not in /cache. - TemporaryDir temp_dir; - TemporaryFile update_package(temp_dir.path); - ASSERT_TRUE(SetupPackageMount(update_package.path, &install_with_fuse)); - ASSERT_TRUE(install_with_fuse); - - // Setup should fail if the input path isn't canonicalized. - std::string uncanonical_package_path = android::base::Join( - std::vector{ - temp_dir.path, - "..", - android::base::Basename(temp_dir.path), - android::base::Basename(update_package.path), - }, - '/'); - - ASSERT_EQ(0, access(uncanonical_package_path.c_str(), R_OK)); - ASSERT_FALSE(SetupPackageMount(uncanonical_package_path, &install_with_fuse)); -} From cd8faf7eeead6fd6ee5912ddd26dab5ab6c7dda7 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 6 Aug 2019 12:32:05 -0700 Subject: [PATCH 153/171] Force off-device package installation with FUSE The non-A/B package installation is subject to TOC/TOU flaw if the attacker can switch the package in the middle of installation. And the most pratical case is to store the package on an external device, e.g. a sdcard, and swap the device in the middle. To prevent that, we can adopt the same protection as used in sideloading a package with FUSE. Specifically, when we install the package with FUSE, we read the entire package to cryptographically verify its signature. The hash for each transfer block is recorded in the memory (TOC), and the subsequent reads (TOU) will be rejected upon dectecting a mismatch. This CL forces the package installation with FUSE when the package stays on a removable media. Bug: 136498130 Test: Run bin/recovery --update_package with various paths; and packages are installed from FUSE as expected Test: recovery_unit_test - no new failures Change-Id: Ia5afd19854c3737110339fd59491b96708926ae5 Merged-In: I35119c2334895aa0ef4ed71b3ddd08f280c0c031 --- Android.bp | 2 ++ install/include/install/install.h | 4 +++ install/install.cpp | 47 +++++++++++++++++++++++++++++++ recovery.cpp | 6 +++- tests/Android.bp | 1 + tests/unit/install_test.cpp | 28 ++++++++++++++++++ 6 files changed, 87 insertions(+), 1 deletion(-) diff --git a/Android.bp b/Android.bp index 4032bcc1..e7e1938b 100644 --- a/Android.bp +++ b/Android.bp @@ -72,6 +72,7 @@ cc_defaults { ], static_libs: [ + "libc++fs", "libinstall", "librecovery_fastboot", "libminui", @@ -94,6 +95,7 @@ cc_library_static { ], shared_libs: [ + "libfusesideload", "librecovery_ui", ], } diff --git a/install/include/install/install.h b/install/include/install/install.h index 87d43ab0..bef23e9c 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -63,3 +63,7 @@ bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map& metadata, OtaType ota_type); + +// Ensures the path to the update package is mounted. Also set the |should_use_fuse| to true if the +// package stays on a removable media. +bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse); diff --git a/install/install.cpp b/install/install.cpp index 4bb0903c..1c9bf2fd 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -641,3 +642,49 @@ bool verify_package(Package* package, RecoveryUI* ui) { } return true; } + +bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse) { + CHECK(should_use_fuse != nullptr); + + if (package_path.empty()) { + return false; + } + + *should_use_fuse = true; + if (package_path[0] == '@') { + auto block_map_path = package_path.substr(1); + if (ensure_path_mounted(block_map_path) != 0) { + LOG(ERROR) << "Failed to mount " << block_map_path; + return false; + } + // uncrypt only produces block map only if the package stays on /data. + *should_use_fuse = false; + return true; + } + + // Package is not a block map file. + if (ensure_path_mounted(package_path) != 0) { + LOG(ERROR) << "Failed to mount " << package_path; + return false; + } + + // Reject the package if the input path doesn't equal the canonicalized path. + // e.g. /cache/../sdcard/update_package. + std::error_code ec; + auto canonical_path = std::filesystem::canonical(package_path, ec); + if (ec) { + LOG(ERROR) << "Failed to get canonical of " << package_path << ", " << ec.message(); + return false; + } + if (canonical_path.string() != package_path) { + LOG(ERROR) << "Installation aborts. The canonical path " << canonical_path.string() + << " doesn't equal the original path " << package_path; + return false; + } + + constexpr const char* CACHE_ROOT = "/cache"; + if (android::base::StartsWith(package_path, CACHE_ROOT)) { + *should_use_fuse = false; + } + return true; +} diff --git a/recovery.cpp b/recovery.cpp index 9ea616e1..526e1a55 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -752,7 +752,11 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector& file_map, int fd, int compression_type) { @@ -513,3 +514,30 @@ TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { "\n"); TestCheckPackageMetadata(metadata, OtaType::AB, true); } + +TEST(InstallTest, SetupPackageMount_package_path) { + load_volume_table(); + bool install_with_fuse; + + // Setup should fail if the input path doesn't exist. + ASSERT_FALSE(SetupPackageMount("/does_not_exist", &install_with_fuse)); + + // Package should be installed with fuse if it's not in /cache. + TemporaryDir temp_dir; + TemporaryFile update_package(temp_dir.path); + ASSERT_TRUE(SetupPackageMount(update_package.path, &install_with_fuse)); + ASSERT_TRUE(install_with_fuse); + + // Setup should fail if the input path isn't canonicalized. + std::string uncanonical_package_path = android::base::Join( + std::vector{ + temp_dir.path, + "..", + android::base::Basename(temp_dir.path), + android::base::Basename(update_package.path), + }, + '/'); + + ASSERT_EQ(0, access(uncanonical_package_path.c_str(), R_OK)); + ASSERT_FALSE(SetupPackageMount(uncanonical_package_path, &install_with_fuse)); +} From f5fab11115e07c72e3e7213f5b2bf558f40dbc76 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 18 Feb 2020 06:57:16 -0800 Subject: [PATCH 154/171] Import translations. DO NOT MERGE Change-Id: I6b85744c59986965ca016e613974661568b0d182 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-ar/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml index a9cd2d13..69191287 100644 --- a/tools/recovery_l10n/res/values-ar/strings.xml +++ b/tools/recovery_l10n/res/values-ar/strings.xml @@ -1,11 +1,11 @@ - "جارٍ تثبيت تحديث النظام" + "جارٍ تثبيت إعادة تحميل النظام" "جارٍ محو البيانات" "ليس هناك أي أمر" "خطأ!" - "جارٍ تثبيت تحديث الأمان" + "جارٍ تثبيت إعادة تحميل الأمان" "‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط على الإعدادات الأصلية ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز." "إعادة المحاولة" "إعادة الضبط على الإعدادات الأصلية" From 9c8d0f61037307b5f3478fc73a1538d2a0d5885e Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 16 Dec 2019 16:07:27 -0800 Subject: [PATCH 155/171] Move libboot_control to boot_control 1.1 It should belong to the default /misc implementation of boot control 1.1. Right now, it's only used by cuttlefish. So move it over to reduce confusion in bootloader_message. Bug: 131775112 Test: build Change-Id: If09bc6f4cc8adf74c8798048c8e54ec94566abaa (cherry picked from commit dc4d2a70db40a302cb7001ddbc41816e7187d5c5) --- boot_control/Android.bp | 61 --- .../include/libboot_control/libboot_control.h | 89 ---- boot_control/legacy_boot_control.cpp | 115 ----- boot_control/libboot_control.cpp | 424 ------------------ .../bootloader_message/bootloader_message.h | 102 ----- 5 files changed, 791 deletions(-) delete mode 100644 boot_control/Android.bp delete mode 100644 boot_control/include/libboot_control/libboot_control.h delete mode 100644 boot_control/legacy_boot_control.cpp delete mode 100644 boot_control/libboot_control.cpp diff --git a/boot_control/Android.bp b/boot_control/Android.bp deleted file mode 100644 index b2e68dfd..00000000 --- a/boot_control/Android.bp +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -cc_defaults { - name: "libboot_control_defaults", - vendor: true, - recovery_available: true, - relative_install_path: "hw", - - cflags: [ - "-D_FILE_OFFSET_BITS=64", - "-Werror", - "-Wall", - "-Wextra", - ], - - shared_libs: [ - "android.hardware.boot@1.1", - "libbase", - "liblog", - ], - static_libs: [ - "libbootloader_message_vendor", - "libfstab", - ], -} - -cc_library_static { - name: "libboot_control", - defaults: ["libboot_control_defaults"], - export_include_dirs: ["include"], - - srcs: ["libboot_control.cpp"], -} - -cc_library_shared { - name: "bootctrl.default", - defaults: ["libboot_control_defaults"], - - srcs: ["legacy_boot_control.cpp"], - - static_libs: [ - "libboot_control", - ], - shared_libs: [ - "libhardware", - ], -} diff --git a/boot_control/include/libboot_control/libboot_control.h b/boot_control/include/libboot_control/libboot_control.h deleted file mode 100644 index 54686588..00000000 --- a/boot_control/include/libboot_control/libboot_control.h +++ /dev/null @@ -1,89 +0,0 @@ -// -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#pragma once - -#include - -#include - -namespace android { -namespace bootable { - -// Helper library to implement the IBootControl HAL using the misc partition. -class BootControl { - using MergeStatus = ::android::hardware::boot::V1_1::MergeStatus; - - public: - bool Init(); - unsigned int GetNumberSlots(); - unsigned int GetCurrentSlot(); - bool MarkBootSuccessful(); - bool SetActiveBootSlot(unsigned int slot); - bool SetSlotAsUnbootable(unsigned int slot); - bool SetSlotBootable(unsigned int slot); - bool IsSlotBootable(unsigned int slot); - const char* GetSuffix(unsigned int slot); - bool IsSlotMarkedSuccessful(unsigned int slot); - bool SetSnapshotMergeStatus(MergeStatus status); - MergeStatus GetSnapshotMergeStatus(); - - bool IsValidSlot(unsigned int slot); - - const std::string& misc_device() const { - return misc_device_; - } - - private: - // Whether this object was initialized with data from the bootloader message - // that doesn't change until next reboot. - bool initialized_ = false; - - // The path to the misc_device as reported in the fstab. - std::string misc_device_; - - // The number of slots present on the device. - unsigned int num_slots_ = 0; - - // The slot where we are running from. - unsigned int current_slot_ = 0; -}; - -// Helper functions to write the Virtual A/B merge status message. These are -// separate because BootControl uses bootloader_control_ab in vendor space, -// whereas the Virtual A/B merge status is in system space. A HAL might not -// use bootloader_control_ab, but may want to use the AOSP method of maintaining -// the merge status. - -// If the Virtual A/B message has not yet been initialized, then initialize it. -// This should be called when the BootControl HAL first loads. -// -// If the Virtual A/B message in misc was already initialized, true is returned. -// If initialization was attempted, but failed, false is returned, and the HAL -// should fail to load. -bool InitMiscVirtualAbMessageIfNeeded(); - -// Save the current merge status as well as the current slot. -bool SetMiscVirtualAbMergeStatus(unsigned int current_slot, - android::hardware::boot::V1_1::MergeStatus status); - -// Return the current merge status. If the saved status is SNAPSHOTTED but the -// slot hasn't changed, the status returned will be NONE. -bool GetMiscVirtualAbMergeStatus(unsigned int current_slot, - android::hardware::boot::V1_1::MergeStatus* status); - -} // namespace bootable -} // namespace android diff --git a/boot_control/legacy_boot_control.cpp b/boot_control/legacy_boot_control.cpp deleted file mode 100644 index 73d3a584..00000000 --- a/boot_control/legacy_boot_control.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include - -#include - -using android::bootable::BootControl; - -struct boot_control_private_t { - // The base struct needs to be first in the list. - boot_control_module_t base; - - BootControl impl; -}; - -namespace { - -void BootControl_init(boot_control_module_t* module) { - auto& impl = reinterpret_cast(module)->impl; - impl.Init(); -} - -unsigned int BootControl_getNumberSlots(boot_control_module_t* module) { - auto& impl = reinterpret_cast(module)->impl; - return impl.GetNumberSlots(); -} - -unsigned int BootControl_getCurrentSlot(boot_control_module_t* module) { - auto& impl = reinterpret_cast(module)->impl; - return impl.GetCurrentSlot(); -} - -int BootControl_markBootSuccessful(boot_control_module_t* module) { - auto& impl = reinterpret_cast(module)->impl; - return impl.MarkBootSuccessful() ? 0 : -1; -} - -int BootControl_setActiveBootSlot(boot_control_module_t* module, unsigned int slot) { - auto& impl = reinterpret_cast(module)->impl; - return impl.SetActiveBootSlot(slot) ? 0 : -1; -} - -int BootControl_setSlotAsUnbootable(struct boot_control_module* module, unsigned int slot) { - auto& impl = reinterpret_cast(module)->impl; - return impl.SetSlotAsUnbootable(slot) ? 0 : -1; -} - -int BootControl_isSlotBootable(struct boot_control_module* module, unsigned int slot) { - auto& impl = reinterpret_cast(module)->impl; - return impl.IsSlotBootable(slot) ? 0 : -1; -} - -int BootControl_isSlotMarkedSuccessful(struct boot_control_module* module, unsigned int slot) { - auto& impl = reinterpret_cast(module)->impl; - return impl.IsSlotMarkedSuccessful(slot) ? 0 : -1; -} - -const char* BootControl_getSuffix(boot_control_module_t* module, unsigned int slot) { - auto& impl = reinterpret_cast(module)->impl; - return impl.GetSuffix(slot); -} - -static int BootControl_open(const hw_module_t* module __unused, const char* id __unused, - hw_device_t** device __unused) { - /* Nothing to do currently. */ - return 0; -} - -struct hw_module_methods_t BootControl_methods = { - .open = BootControl_open, -}; - -} // namespace - -boot_control_private_t HAL_MODULE_INFO_SYM = { - .base = - { - .common = - { - .tag = HARDWARE_MODULE_TAG, - .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1, - .hal_api_version = HARDWARE_HAL_API_VERSION, - .id = BOOT_CONTROL_HARDWARE_MODULE_ID, - .name = "AOSP reference bootctrl HAL", - .author = "The Android Open Source Project", - .methods = &BootControl_methods, - }, - .init = BootControl_init, - .getNumberSlots = BootControl_getNumberSlots, - .getCurrentSlot = BootControl_getCurrentSlot, - .markBootSuccessful = BootControl_markBootSuccessful, - .setActiveBootSlot = BootControl_setActiveBootSlot, - .setSlotAsUnbootable = BootControl_setSlotAsUnbootable, - .isSlotBootable = BootControl_isSlotBootable, - .getSuffix = BootControl_getSuffix, - .isSlotMarkedSuccessful = BootControl_isSlotMarkedSuccessful, - }, -}; diff --git a/boot_control/libboot_control.cpp b/boot_control/libboot_control.cpp deleted file mode 100644 index ab9ce971..00000000 --- a/boot_control/libboot_control.cpp +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include - -namespace android { -namespace bootable { - -using ::android::hardware::boot::V1_1::MergeStatus; - -// The number of boot attempts that should be made from a new slot before -// rolling back to the previous slot. -constexpr unsigned int kDefaultBootAttempts = 7; -static_assert(kDefaultBootAttempts < 8, "tries_remaining field only has 3 bits"); - -constexpr unsigned int kMaxNumSlots = - sizeof(bootloader_control::slot_info) / sizeof(bootloader_control::slot_info[0]); -constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" }; -constexpr off_t kBootloaderControlOffset = offsetof(bootloader_message_ab, slot_suffix); - -static uint32_t CRC32(const uint8_t* buf, size_t size) { - static uint32_t crc_table[256]; - - // Compute the CRC-32 table only once. - if (!crc_table[1]) { - for (uint32_t i = 0; i < 256; ++i) { - uint32_t crc = i; - for (uint32_t j = 0; j < 8; ++j) { - uint32_t mask = -(crc & 1); - crc = (crc >> 1) ^ (0xEDB88320 & mask); - } - crc_table[i] = crc; - } - } - - uint32_t ret = -1; - for (size_t i = 0; i < size; ++i) { - ret = (ret >> 8) ^ crc_table[(ret ^ buf[i]) & 0xFF]; - } - - return ~ret; -} - -// Return the little-endian representation of the CRC-32 of the first fields -// in |boot_ctrl| up to the crc32_le field. -uint32_t BootloaderControlLECRC(const bootloader_control* boot_ctrl) { - return htole32( - CRC32(reinterpret_cast(boot_ctrl), offsetof(bootloader_control, crc32_le))); -} - -bool LoadBootloaderControl(const std::string& misc_device, bootloader_control* buffer) { - android::base::unique_fd fd(open(misc_device.c_str(), O_RDONLY)); - if (fd.get() == -1) { - PLOG(ERROR) << "failed to open " << misc_device; - return false; - } - if (lseek(fd, kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) { - PLOG(ERROR) << "failed to lseek " << misc_device; - return false; - } - if (!android::base::ReadFully(fd.get(), buffer, sizeof(bootloader_control))) { - PLOG(ERROR) << "failed to read " << misc_device; - return false; - } - return true; -} - -bool UpdateAndSaveBootloaderControl(const std::string& misc_device, bootloader_control* buffer) { - buffer->crc32_le = BootloaderControlLECRC(buffer); - android::base::unique_fd fd(open(misc_device.c_str(), O_WRONLY | O_SYNC)); - if (fd.get() == -1) { - PLOG(ERROR) << "failed to open " << misc_device; - return false; - } - if (lseek(fd.get(), kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) { - PLOG(ERROR) << "failed to lseek " << misc_device; - return false; - } - if (!android::base::WriteFully(fd.get(), buffer, sizeof(bootloader_control))) { - PLOG(ERROR) << "failed to write " << misc_device; - return false; - } - return true; -} - -void InitDefaultBootloaderControl(BootControl* control, bootloader_control* boot_ctrl) { - memset(boot_ctrl, 0, sizeof(*boot_ctrl)); - - unsigned int current_slot = control->GetCurrentSlot(); - if (current_slot < kMaxNumSlots) { - strlcpy(boot_ctrl->slot_suffix, kSlotSuffixes[current_slot], sizeof(boot_ctrl->slot_suffix)); - } - boot_ctrl->magic = BOOT_CTRL_MAGIC; - boot_ctrl->version = BOOT_CTRL_VERSION; - - // Figure out the number of slots by checking if the partitions exist, - // otherwise assume the maximum supported by the header. - boot_ctrl->nb_slot = kMaxNumSlots; - std::string base_path = control->misc_device(); - size_t last_path_sep = base_path.rfind('/'); - if (last_path_sep != std::string::npos) { - // We test the existence of the "boot" partition on each possible slot, - // which is a partition required by Android Bootloader Requirements. - base_path = base_path.substr(0, last_path_sep + 1) + "boot"; - int last_existing_slot = -1; - int first_missing_slot = -1; - for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) { - std::string partition_path = base_path + kSlotSuffixes[slot]; - struct stat part_stat; - int err = stat(partition_path.c_str(), &part_stat); - if (!err) { - last_existing_slot = slot; - LOG(INFO) << "Found slot: " << kSlotSuffixes[slot]; - } else if (err < 0 && errno == ENOENT && first_missing_slot == -1) { - first_missing_slot = slot; - } - } - // We only declare that we found the actual number of slots if we found all - // the boot partitions up to the number of slots, and no boot partition - // after that. Not finding any of the boot partitions implies a problem so - // we just leave the number of slots in the maximum value. - if ((last_existing_slot != -1 && last_existing_slot + 1 == first_missing_slot) || - (first_missing_slot == -1 && last_existing_slot + 1 == kMaxNumSlots)) { - boot_ctrl->nb_slot = last_existing_slot + 1; - LOG(INFO) << "Found a system with " << last_existing_slot + 1 << " slots."; - } - } - - for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) { - slot_metadata entry = {}; - - if (slot < boot_ctrl->nb_slot) { - entry.priority = 7; - entry.tries_remaining = kDefaultBootAttempts; - entry.successful_boot = 0; - } else { - entry.priority = 0; // Unbootable - } - - // When the boot_control stored on disk is invalid, we assume that the - // current slot is successful. The bootloader should repair this situation - // before booting and write a valid boot_control slot, so if we reach this - // stage it means that the misc partition was corrupted since boot. - if (current_slot == slot) { - entry.successful_boot = 1; - } - - boot_ctrl->slot_info[slot] = entry; - } - boot_ctrl->recovery_tries_remaining = 0; - - boot_ctrl->crc32_le = BootloaderControlLECRC(boot_ctrl); -} - -// Return the index of the slot suffix passed or -1 if not a valid slot suffix. -int SlotSuffixToIndex(const char* suffix) { - for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) { - if (!strcmp(kSlotSuffixes[slot], suffix)) return slot; - } - return -1; -} - -// Initialize the boot_control_private struct with the information from -// the bootloader_message buffer stored in |boot_ctrl|. Returns whether the -// initialization succeeded. -bool BootControl::Init() { - if (initialized_) return true; - - // Initialize the current_slot from the read-only property. If the property - // was not set (from either the command line or the device tree), we can later - // initialize it from the bootloader_control struct. - std::string suffix_prop = android::base::GetProperty("ro.boot.slot_suffix", ""); - if (suffix_prop.empty()) { - LOG(ERROR) << "Slot suffix property is not set"; - return false; - } - current_slot_ = SlotSuffixToIndex(suffix_prop.c_str()); - - std::string err; - std::string device = get_bootloader_message_blk_device(&err); - if (device.empty()) { - LOG(ERROR) << "Could not find bootloader message block device: " << err; - return false; - } - - bootloader_control boot_ctrl; - if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) { - LOG(ERROR) << "Failed to load bootloader control block"; - return false; - } - - // Note that since there isn't a module unload function this memory is leaked. - // We use `device` below sometimes, so it's not moved out of here. - misc_device_ = device; - initialized_ = true; - - // Validate the loaded data, otherwise we will destroy it and re-initialize it - // with the current information. - uint32_t computed_crc32 = BootloaderControlLECRC(&boot_ctrl); - if (boot_ctrl.crc32_le != computed_crc32) { - LOG(WARNING) << "Invalid boot control found, expected CRC-32 0x" << std::hex << computed_crc32 - << " but found 0x" << std::hex << boot_ctrl.crc32_le << ". Re-initializing."; - InitDefaultBootloaderControl(this, &boot_ctrl); - UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl); - } - - if (!InitMiscVirtualAbMessageIfNeeded()) { - return false; - } - - num_slots_ = boot_ctrl.nb_slot; - return true; -} - -unsigned int BootControl::GetNumberSlots() { - return num_slots_; -} - -unsigned int BootControl::GetCurrentSlot() { - return current_slot_; -} - -bool BootControl::MarkBootSuccessful() { - bootloader_control bootctrl; - if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; - - bootctrl.slot_info[current_slot_].successful_boot = 1; - // tries_remaining == 0 means that the slot is not bootable anymore, make - // sure we mark the current slot as bootable if it succeeds in the last - // attempt. - bootctrl.slot_info[current_slot_].tries_remaining = 1; - return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl); -} - -bool BootControl::SetActiveBootSlot(unsigned int slot) { - if (slot >= kMaxNumSlots || slot >= num_slots_) { - // Invalid slot number. - return false; - } - - bootloader_control bootctrl; - if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; - - // Set every other slot with a lower priority than the new "active" slot. - const unsigned int kActivePriority = 15; - const unsigned int kActiveTries = 6; - for (unsigned int i = 0; i < num_slots_; ++i) { - if (i != slot) { - if (bootctrl.slot_info[i].priority >= kActivePriority) - bootctrl.slot_info[i].priority = kActivePriority - 1; - } - } - - // Note that setting a slot as active doesn't change the successful bit. - // The successful bit will only be changed by setSlotAsUnbootable(). - bootctrl.slot_info[slot].priority = kActivePriority; - bootctrl.slot_info[slot].tries_remaining = kActiveTries; - - // Setting the current slot as active is a way to revert the operation that - // set *another* slot as active at the end of an updater. This is commonly - // used to cancel the pending update. We should only reset the verity_corrpted - // bit when attempting a new slot, otherwise the verity bit on the current - // slot would be flip. - if (slot != current_slot_) bootctrl.slot_info[slot].verity_corrupted = 0; - - return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl); -} - -bool BootControl::SetSlotAsUnbootable(unsigned int slot) { - if (slot >= kMaxNumSlots || slot >= num_slots_) { - // Invalid slot number. - return false; - } - - bootloader_control bootctrl; - if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; - - // The only way to mark a slot as unbootable, regardless of the priority is to - // set the tries_remaining to 0. - bootctrl.slot_info[slot].successful_boot = 0; - bootctrl.slot_info[slot].tries_remaining = 0; - return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl); -} - -bool BootControl::IsSlotBootable(unsigned int slot) { - if (slot >= kMaxNumSlots || slot >= num_slots_) { - // Invalid slot number. - return false; - } - - bootloader_control bootctrl; - if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; - - return bootctrl.slot_info[slot].tries_remaining != 0; -} - -bool BootControl::IsSlotMarkedSuccessful(unsigned int slot) { - if (slot >= kMaxNumSlots || slot >= num_slots_) { - // Invalid slot number. - return false; - } - - bootloader_control bootctrl; - if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false; - - return bootctrl.slot_info[slot].successful_boot && bootctrl.slot_info[slot].tries_remaining; -} - -bool BootControl::IsValidSlot(unsigned int slot) { - return slot < kMaxNumSlots && slot < num_slots_; -} - -bool BootControl::SetSnapshotMergeStatus(MergeStatus status) { - return SetMiscVirtualAbMergeStatus(current_slot_, status); -} - -MergeStatus BootControl::GetSnapshotMergeStatus() { - MergeStatus status; - if (!GetMiscVirtualAbMergeStatus(current_slot_, &status)) { - return MergeStatus::UNKNOWN; - } - return status; -} - -const char* BootControl::GetSuffix(unsigned int slot) { - if (slot >= kMaxNumSlots || slot >= num_slots_) { - return nullptr; - } - return kSlotSuffixes[slot]; -} - -bool InitMiscVirtualAbMessageIfNeeded() { - std::string err; - misc_virtual_ab_message message; - if (!ReadMiscVirtualAbMessage(&message, &err)) { - LOG(ERROR) << "Could not read merge status: " << err; - return false; - } - - if (message.version == MISC_VIRTUAL_AB_MESSAGE_VERSION && - message.magic == MISC_VIRTUAL_AB_MAGIC_HEADER) { - // Already initialized. - return true; - } - - message = {}; - message.version = MISC_VIRTUAL_AB_MESSAGE_VERSION; - message.magic = MISC_VIRTUAL_AB_MAGIC_HEADER; - if (!WriteMiscVirtualAbMessage(message, &err)) { - LOG(ERROR) << "Could not write merge status: " << err; - return false; - } - return true; -} - -bool SetMiscVirtualAbMergeStatus(unsigned int current_slot, - android::hardware::boot::V1_1::MergeStatus status) { - std::string err; - misc_virtual_ab_message message; - - if (!ReadMiscVirtualAbMessage(&message, &err)) { - LOG(ERROR) << "Could not read merge status: " << err; - return false; - } - - message.merge_status = static_cast(status); - message.source_slot = current_slot; - if (!WriteMiscVirtualAbMessage(message, &err)) { - LOG(ERROR) << "Could not write merge status: " << err; - return false; - } - return true; -} - -bool GetMiscVirtualAbMergeStatus(unsigned int current_slot, - android::hardware::boot::V1_1::MergeStatus* status) { - std::string err; - misc_virtual_ab_message message; - - if (!ReadMiscVirtualAbMessage(&message, &err)) { - LOG(ERROR) << "Could not read merge status: " << err; - return false; - } - - // If the slot reverted after having created a snapshot, then the snapshot will - // be thrown away at boot. Thus we don't count this as being in a snapshotted - // state. - *status = static_cast(message.merge_status); - if (*status == MergeStatus::SNAPSHOTTED && current_slot == message.source_slot) { - *status = MergeStatus::NONE; - } - return true; -} - -} // namespace bootable -} // namespace android diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 9a482d42..e4cf09b2 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -83,108 +83,6 @@ struct bootloader_message { char reserved[1184]; }; -/** - * We must be cautious when changing the bootloader_message struct size, - * because A/B-specific fields may end up with different offsets. - */ -#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) -static_assert(sizeof(struct bootloader_message) == 2048, - "struct bootloader_message size changes, which may break A/B devices"); -#endif - -/** - * The A/B-specific bootloader message structure (4-KiB). - * - * We separate A/B boot control metadata from the regular bootloader - * message struct and keep it here. Everything that's A/B-specific - * stays after struct bootloader_message, which should be managed by - * the A/B-bootloader or boot control HAL. - * - * The slot_suffix field is used for A/B implementations where the - * bootloader does not set the androidboot.ro.boot.slot_suffix kernel - * commandline parameter. This is used by fs_mgr to mount /system and - * other partitions with the slotselect flag set in fstab. A/B - * implementations are free to use all 32 bytes and may store private - * data past the first NUL-byte in this field. It is encouraged, but - * not mandatory, to use 'struct bootloader_control' described below. - * - * The update_channel field is used to store the Omaha update channel - * if update_engine is compiled with Omaha support. - */ -struct bootloader_message_ab { - struct bootloader_message message; - char slot_suffix[32]; - char update_channel[128]; - - // Round up the entire struct to 4096-byte. - char reserved[1888]; -}; - -/** - * Be cautious about the struct size change, in case we put anything post - * bootloader_message_ab struct (b/29159185). - */ -#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) -static_assert(sizeof(struct bootloader_message_ab) == 4096, - "struct bootloader_message_ab size changes"); -#endif - -#define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */ -#define BOOT_CTRL_VERSION 1 - -struct slot_metadata { - // Slot priority with 15 meaning highest priority, 1 lowest - // priority and 0 the slot is unbootable. - uint8_t priority : 4; - // Number of times left attempting to boot this slot. - uint8_t tries_remaining : 3; - // 1 if this slot has booted successfully, 0 otherwise. - uint8_t successful_boot : 1; - // 1 if this slot is corrupted from a dm-verity corruption, 0 - // otherwise. - uint8_t verity_corrupted : 1; - // Reserved for further use. - uint8_t reserved : 7; -} __attribute__((packed)); - -/* Bootloader Control AB - * - * This struct can be used to manage A/B metadata. It is designed to - * be put in the 'slot_suffix' field of the 'bootloader_message' - * structure described above. It is encouraged to use the - * 'bootloader_control' structure to store the A/B metadata, but not - * mandatory. - */ -struct bootloader_control { - // NUL terminated active slot suffix. - char slot_suffix[4]; - // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC). - uint32_t magic; - // Version of struct being used (see BOOT_CTRL_VERSION). - uint8_t version; - // Number of slots being managed. - uint8_t nb_slot : 3; - // Number of times left attempting to boot recovery. - uint8_t recovery_tries_remaining : 3; - // Status of any pending snapshot merge of dynamic partitions. - uint8_t merge_status : 3; - // Ensure 4-bytes alignment for slot_info field. - uint8_t reserved0[1]; - // Per-slot information. Up to 4 slots. - struct slot_metadata slot_info[4]; - // Reserved for further use. - uint8_t reserved1[8]; - // CRC32 of all 28 bytes preceding this field (little endian - // format). - uint32_t crc32_le; -} __attribute__((packed)); - -#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) -static_assert(sizeof(struct bootloader_control) == - sizeof(((struct bootloader_message_ab *)0)->slot_suffix), - "struct bootloader_control has wrong size"); -#endif - // Holds Virtual A/B merge status information. Current version is 1. New fields // must be added to the end. struct misc_virtual_ab_message { From cbb0b54b1253bcf5d29fb0b29441f8f821a061b6 Mon Sep 17 00:00:00 2001 From: Marek Z Jeziorek Date: Sat, 22 Feb 2020 21:43:12 +0000 Subject: [PATCH 156/171] New priority scripts + critical updates to fonts. Author: Marek Z. Jeziorek Needs to be submitted with https://googleplex-android-review.git.corp.google.com/c/platform/frameworks/base/+/10280447 BUGANIZER BUGS Addressed ======================== BUG: 149257053 CHANGES ======= Remove font files no longer required deleted: other/NotoSansAdlam-Regular.ttf deleted: other/NotoSansGeorgian-Bold.otf deleted: other/NotoSansGeorgian-Medium.otf deleted: other/NotoSansGeorgian-Regular.otf deleted: other/NotoSansJavanese-Regular.ttf deleted: other/NotoSansTifinagh-Regular.ttf deleted: other/NotoSerifGeorgian-Bold.otf deleted: other/NotoSerifGeorgian-Regular.otf modified: fonts.mk modified: other/Android.bp Replacements for the above deleted files: new file: other/NotoSansAdlam-VF.ttf new file: other/NotoSansGeorgian-VF.ttf new file: other/NotoSansJavanese-Regular.otf new file: other/NotoSansTifinagh-Regular.otf new file: other/NotoSerifGeorgian-VF.ttf New additions to Android fonts (living/revival languages): new file: other/NotoSansGunjalaGondi-Regular.otf new file: other/NotoSansHanifiRohingya-Regular.otf new file: other/NotoSansKhojki-Regular.otf new file: other/NotoSansMasaramGondi-Regular.otf new file: other/NotoSansWancho-Regular.otf new file: other/NotoSansWarangCiti-Regular.otf Test: =================================================== Test: on Android (click on a link within a browser): Test: Adlam https://en.wikipedia.org/wiki/Adlam_(Unicode_block) Test: Georgian https://en.wikipedia.org/wiki/Georgian_(Unicode_block) Test: Georgian https://en.wikipedia.org/wiki/Georgian_Extended Test: Gunjala Gondi https://en.wikipedia.org/wiki/Gunjala_Gondi_(Unicode_block) Test: Hanifi Rohingya https://en.wikipedia.org/wiki/Hanifi_Rohingya_(Unicode_block) Test: Javanese https://en.wikipedia.org/wiki/Javanese_(Unicode_block) Test: Khojki https://en.wikipedia.org/wiki/Khojki_(Unicode_block) Test: Masaram Gondi https://en.wikipedia.org/wiki/Masaram_Gondi_(Unicode_block) Test: Tifinagh https://en.wikipedia.org/wiki/Tifinagh_(Unicode_block) Test: Wancho https://en.wikipedia.org/wiki/Wancho_(Unicode_block) Test: Warang Citi https://en.wikipedia.org/wiki/Warang_Citi_(Unicode_block) Test: =================================================== Test: on Android (click on a link within a browser): Test: udhr Adlam https://unicode.org/udhr/d/udhr_fuf_adlm.html Test: udhr Georgian https://unicode.org/udhr/d/udhr_kat.html Test: udhr Gunjala Gondi NA (Not Available) Test: udhr Hanifi Rohingya NA (Not Available) Test: udhr Javanese https://unicode.org/udhr/d/udhr_jav_java.html Test: udhr Khojki NA (Not Available) Test: udhr Masaram Gondi NA (Not Available) Test: udhr Tifinagh https://unicode.org/udhr/d/udhr_tzm_tfng.html Test: udhr Wancho NA (Not Available) Test: udhr Warang Citi NA (Not Available) Test: =================================== Test: on Android (comparing two systems - before/after) Test: Georgian: Systems settings menus compared side by side Test: =================================== Test: on Mac (comparing the existing Android fonts with the new font Test: using diffenator - https://github.com/googlefonts/fontdiffenator) Test: =================================== Test: SansAdlam-VF: VALIDATED Test: SansGeorgian-VF: VALIDATED Test: SansGunjalaGondi: NEW (No Applicable) Test: SansHanifiRohingya: NEW (No Applicable) Test: SansJavanese: VALIDATED Test: SansKhojki: NEW (No Applicable) Test: SansMasaramGondi: NEW (No Applicable) Test: SansTifinagh: VALIDATED Test: SansWancho: NEW (No Applicable) Test: SansWarangCiti: NEW (No Applicable) Test: SerifGeorgian-VF: VALIDATED Change-Id: I41e030b63f83ec243ae6f174c061faa5298bd6dc Signed-off-by: Marek Z Jeziorek (cherry picked from commit f165b9c886476889d99edad8d3e43b2980b855a1) --- tools/image_generator/ImageGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/image_generator/ImageGenerator.java b/tools/image_generator/ImageGenerator.java index fd8e5429..1da43e5c 100644 --- a/tools/image_generator/ImageGenerator.java +++ b/tools/image_generator/ImageGenerator.java @@ -123,7 +123,7 @@ public class ImageGenerator { put("hy", "NotoSansArmenian-Regular"); put("iw", "NotoSansHebrew-Regular"); put("ja", "NotoSansCJK-Regular"); - put("ka", "NotoSansGeorgian-Regular"); + put("ka", "NotoSansGeorgian-VF"); put("ko", "NotoSansCJK-Regular"); put("km", "NotoSansKhmerUI-Regular"); put("kn", "NotoSansKannadaUI-Regular"); From 2f6ed0524eabdad3daaa1c4ff08a4f7889696f1f Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 16 Mar 2020 19:53:58 -0700 Subject: [PATCH 157/171] Import translations. DO NOT MERGE Auto-generated-cl: translation import Change-Id: I05a2df29e459400e5af878922f6e73a8b94f042b --- tools/recovery_l10n/res/values-ar/strings.xml | 4 ++-- tools/recovery_l10n/res/values-hy/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml index 2af36d64..a9cd2d13 100644 --- a/tools/recovery_l10n/res/values-ar/strings.xml +++ b/tools/recovery_l10n/res/values-ar/strings.xml @@ -6,9 +6,9 @@ "ليس هناك أي أمر" "خطأ!" "جارٍ تثبيت تحديث الأمان" - "‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط بحسب بيانات المصنع ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز." + "‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط على الإعدادات الأصلية ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز." "إعادة المحاولة" - "إعادة الضبط بحسب بيانات المصنع" + "إعادة الضبط على الإعدادات الأصلية" "هل تريد حجب كل بيانات المستخدم؟\n\n لا يمكن التراجع عن هذا الإجراء." "إلغاء" diff --git a/tools/recovery_l10n/res/values-hy/strings.xml b/tools/recovery_l10n/res/values-hy/strings.xml index 35a0ab11..76c28a70 100644 --- a/tools/recovery_l10n/res/values-hy/strings.xml +++ b/tools/recovery_l10n/res/values-hy/strings.xml @@ -9,6 +9,6 @@ "Չհաջողվեց բեռնել Android համակարգը։ Հնարավոր է՝ ձեր տվյալները վնասված են։ Եթե նորից տեսնեք այս հաղորդագրությունը, փորձեք վերակայել սարքի կարգավորումները և ջնջել օգտատիրոջ բոլոր տվյալները։" "Նորից փորձել" "Վերակայել բոլոր տվյալները" - "Մաքրե՞լ օգտատիրոջ բոլոր տվյալները։\n\n ԱՅՍ ԳՈՐԾՈՂՈՒԹՅՈՒՆԸ ՀՆԱՐԱՎՈՐ ՉԻ ԼԻՆԻ ՀԵՏԱՐԿԵԼ" + "Ջնջե՞լ օգտատիրոջ բոլոր տվյալները։\n\n ԱՅՍ ԳՈՐԾՈՂՈՒԹՅՈՒՆԸ ՀՆԱՐԱՎՈՐ ՉԻ ԼԻՆԻ ՀԵՏԱՐԿԵԼ" "Չեղարկել" From b37d794351e3718683cd13238972cf79da7f6cdd Mon Sep 17 00:00:00 2001 From: Hongguang Chen Date: Sun, 15 Mar 2020 21:09:21 -0700 Subject: [PATCH 158/171] Add more mounting options to updater mount function. If enabling the oem partition, it will be mounted by updater before reading product properties from it. To be safety, we want to enable AVB to this oem partition. But this means the oem partition can never be mounted to writable. Otherwise, that partition will be corrupted to AVB verifying. This change follows fs_mgr to allow to pass more mounting options to the updater. BUG: 150156957 Test: make ota package which mounts AVB oem partition to read only and run OTA. Change-Id: I2ebbe3c8ac53c70112f3fed2703fcba9170405a6 --- updater/updater_runtime.cpp | 60 +++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/updater/updater_runtime.cpp b/updater/updater_runtime.cpp index c4222a56..b1b8863f 100644 --- a/updater/updater_runtime.cpp +++ b/updater/updater_runtime.cpp @@ -43,10 +43,62 @@ std::string UpdaterRuntime::FindBlockDeviceName(const std::string_view name) con return std::string(name); } +static struct { + const char* name; + unsigned flag; +} mount_flags_list[] = { + { "noatime", MS_NOATIME }, + { "noexec", MS_NOEXEC }, + { "nosuid", MS_NOSUID }, + { "nodev", MS_NODEV }, + { "nodiratime", MS_NODIRATIME }, + { "ro", MS_RDONLY }, + { "rw", 0 }, + { "remount", MS_REMOUNT }, + { "bind", MS_BIND }, + { "rec", MS_REC }, + { "unbindable", MS_UNBINDABLE }, + { "private", MS_PRIVATE }, + { "slave", MS_SLAVE }, + { "shared", MS_SHARED }, + { "defaults", 0 }, + { 0, 0 }, +}; + +static bool setMountFlag(const std::string& flag, unsigned* mount_flags) { + for (const auto& [name, value] : mount_flags_list) { + if (flag == name) { + *mount_flags |= value; + return true; + } + } + return false; +} + +static bool parseMountFlags(const std::string& flags, unsigned* mount_flags, + std::string* fs_options) { + bool is_flag_set = false; + std::vector flag_list; + for (const auto& flag : android::base::Split(flags, ",")) { + if (!setMountFlag(flag, mount_flags)) { + // Unknown flag, so it must be a filesystem specific option. + flag_list.push_back(flag); + } else { + is_flag_set = true; + } + } + *fs_options = android::base::Join(flag_list, ','); + return is_flag_set; +} + int UpdaterRuntime::Mount(const std::string_view location, const std::string_view mount_point, const std::string_view fs_type, const std::string_view mount_options) { std::string mount_point_string(mount_point); + std::string mount_options_string(mount_options); char* secontext = nullptr; + unsigned mount_flags = 0; + std::string fs_options; + if (sehandle_) { selabel_lookup(sehandle_, &secontext, mount_point_string.c_str(), 0755); setfscreatecon(secontext); @@ -59,9 +111,13 @@ int UpdaterRuntime::Mount(const std::string_view location, const std::string_vie setfscreatecon(nullptr); } + if (!parseMountFlags(mount_options_string, &mount_flags, &fs_options)) { + // Fall back to default + mount_flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME; + } + return mount(std::string(location).c_str(), mount_point_string.c_str(), - std::string(fs_type).c_str(), MS_NOATIME | MS_NODEV | MS_NODIRATIME, - std::string(mount_options).c_str()); + std::string(fs_type).c_str(), mount_flags, fs_options.c_str()); } bool UpdaterRuntime::IsMounted(const std::string_view mount_point) const { From e8ca1b86347992d3c92db8e7a25ee77a6280787b Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 13 Mar 2020 14:25:02 -0700 Subject: [PATCH 159/171] Consolidate the wait in recovery's reboot After a reboot function call, we should always wait for it to finish without executing other instructions. Bug: 151110322 Test: build Change-Id: I1dda291a0835ff96df7eaf42eba1a38267a3beeb (cherry picked from commit 00c4aba9bf428717fc00e26a03e97401eca76ee8) --- otautil/include/otautil/sysutil.h | 2 +- otautil/sysutil.cpp | 8 ++++++-- recovery.cpp | 8 +------- recovery_ui/ui.cpp | 3 --- updater/install.cpp | 1 - 5 files changed, 8 insertions(+), 14 deletions(-) diff --git a/otautil/include/otautil/sysutil.h b/otautil/include/otautil/sysutil.h index 326db864..d0d2e67d 100644 --- a/otautil/include/otautil/sysutil.h +++ b/otautil/include/otautil/sysutil.h @@ -103,7 +103,7 @@ class MemMapping { // Reboots the device into the specified target, by additionally handling quiescent reboot mode. // All unknown targets reboot into Android. -bool Reboot(std::string_view target); +[[noreturn]] void Reboot(std::string_view target); // Triggers a shutdown. bool Shutdown(std::string_view target); diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp index 6cd46c6a..b3ead973 100644 --- a/otautil/sysutil.cpp +++ b/otautil/sysutil.cpp @@ -219,14 +219,18 @@ MemMapping::~MemMapping() { ranges_.clear(); } -bool Reboot(std::string_view target) { +void Reboot(std::string_view target) { std::string cmd = "reboot," + std::string(target); // Honor the quiescent mode if applicable. if (target != "bootloader" && target != "fastboot" && android::base::GetBoolProperty("ro.boot.quiescent", false)) { cmd += ",quiescent"; } - return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd); + if (!android::base::SetProperty(ANDROID_RB_PROPERTY, cmd)) { + LOG(FATAL) << "Reboot failed"; + } + + while (true) pause(); } bool Shutdown(std::string_view target) { diff --git a/recovery.cpp b/recovery.cpp index 0382697a..7675121d 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -781,13 +781,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Retry attempt %d\n", retry_count); // Reboot back into recovery to retry the update. - if (!Reboot("recovery")) { - ui->Print("Reboot failed\n"); - } else { - while (true) { - pause(); - } - } + Reboot("recovery"); } // If this is an eng or userdebug build, then automatically // turn the text display on if the script fails so the error diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index 6f5cbbca..33072177 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -375,9 +375,6 @@ void RecoveryUI::ProcessKey(int key_code, int updown) { case RecoveryUI::REBOOT: if (reboot_enabled) { Reboot("userrequested,recovery,ui"); - while (true) { - pause(); - } } break; diff --git a/updater/install.cpp b/updater/install.cpp index 62ff87e7..7608dc3c 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -733,7 +733,6 @@ Value* RebootNowFn(const char* name, State* state, const std::vector Date: Fri, 22 Nov 2019 13:46:19 -0800 Subject: [PATCH 160/171] Add libavb as a dependency It's needed by libfec Bug: 144388532 Test: mma Change-Id: Ic82671f6506e7718afa965a511261b1b329ea38f (cherry picked from commit 2bb374a86d0161700f01b68433063b0b13adc86a) --- updater/Android.bp | 1 + updater/Android.mk | 1 + 2 files changed, 2 insertions(+) diff --git a/updater/Android.bp b/updater/Android.bp index cbef4309..f00a192b 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -25,6 +25,7 @@ cc_defaults { "libdm", "libfec", "libfec_rs", + "libavb", "libverity_tree", "libgtest_prod", "liblog", diff --git a/updater/Android.mk b/updater/Android.mk index 8a4cd86d..46300d97 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -32,6 +32,7 @@ updater_common_static_libraries := \ libdm \ libfec \ libfec_rs \ + libavb \ libverity_tree \ libgtest_prod \ liblog \ From c054f231ae94ff14fc1cebe632d98597a153c884 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 25 Mar 2020 10:29:41 -0700 Subject: [PATCH 161/171] Import translations. DO NOT MERGE Auto-generated-cl: translation import Change-Id: Ia3393b1e62f0af1661cbc7b2c0b19e0a52631c4c --- tools/recovery_l10n/res/values-ar/strings.xml | 4 ++-- tools/recovery_l10n/res/values-hy/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml index 69191287..a9cd2d13 100644 --- a/tools/recovery_l10n/res/values-ar/strings.xml +++ b/tools/recovery_l10n/res/values-ar/strings.xml @@ -1,11 +1,11 @@ - "جارٍ تثبيت إعادة تحميل النظام" + "جارٍ تثبيت تحديث النظام" "جارٍ محو البيانات" "ليس هناك أي أمر" "خطأ!" - "جارٍ تثبيت إعادة تحميل الأمان" + "جارٍ تثبيت تحديث الأمان" "‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط على الإعدادات الأصلية ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز." "إعادة المحاولة" "إعادة الضبط على الإعدادات الأصلية" diff --git a/tools/recovery_l10n/res/values-hy/strings.xml b/tools/recovery_l10n/res/values-hy/strings.xml index 35a0ab11..76c28a70 100644 --- a/tools/recovery_l10n/res/values-hy/strings.xml +++ b/tools/recovery_l10n/res/values-hy/strings.xml @@ -9,6 +9,6 @@ "Չհաջողվեց բեռնել Android համակարգը։ Հնարավոր է՝ ձեր տվյալները վնասված են։ Եթե նորից տեսնեք այս հաղորդագրությունը, փորձեք վերակայել սարքի կարգավորումները և ջնջել օգտատիրոջ բոլոր տվյալները։" "Նորից փորձել" "Վերակայել բոլոր տվյալները" - "Մաքրե՞լ օգտատիրոջ բոլոր տվյալները։\n\n ԱՅՍ ԳՈՐԾՈՂՈՒԹՅՈՒՆԸ ՀՆԱՐԱՎՈՐ ՉԻ ԼԻՆԻ ՀԵՏԱՐԿԵԼ" + "Ջնջե՞լ օգտատիրոջ բոլոր տվյալները։\n\n ԱՅՍ ԳՈՐԾՈՂՈՒԹՅՈՒՆԸ ՀՆԱՐԱՎՈՐ ՉԻ ԼԻՆԻ ՀԵՏԱՐԿԵԼ" "Չեղարկել" From 22beef1b282301d69987e7bc9144c82f397586b5 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Fri, 27 Mar 2020 20:37:07 -0700 Subject: [PATCH 162/171] minadbd: statically link libadbd. Reduce the amount of wasted space on the recovery image by statically linking all of the adb libraries into minadbd. Bug: http://b/150317254 Test: booted aosp_walleye-eng into recovery Change-Id: I5233b1a23c14f9d478bb77c25ebdbb0ce45bcc5a Merged-In: I5233b1a23c14f9d478bb77c25ebdbb0ce45bcc5a (cherry picked from commit 5ee3eba34f0ddf478f53ac4d0774c1ee423a5182) --- minadbd/Android.bp | 12 +++++++++--- minadbd/minadbd_services.cpp | 4 ++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/minadbd/Android.bp b/minadbd/Android.bp index c39c7344..793680f3 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -34,7 +34,7 @@ cc_defaults { // `libminadbd_services` is analogous to the `libadbd_services` for regular `adbd`, but providing // the sideload service only. -cc_library { +cc_library_static { name: "libminadbd_services", recovery_available: true, @@ -79,6 +79,8 @@ cc_binary { defaults: [ "minadbd_defaults", + "libadbd_binary_dependencies", + "librecovery_utils_defaults", ], srcs: [ @@ -86,10 +88,14 @@ cc_binary { ], shared_libs: [ - "libadbd", "libbase", "libcrypto", + ], + + static_libs: [ "libminadbd_services", + "libfusesideload", + "librecovery_utils", ], required: [ @@ -104,6 +110,7 @@ cc_test { defaults: [ "minadbd_defaults", "librecovery_utils_defaults", + "libadbd_binary_dependencies", ], srcs: [ @@ -116,7 +123,6 @@ cc_test { "libfusesideload", "librecovery_utils", "libotautil", - "libadbd", ], shared_libs: [ diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index eb91fb3e..ff91ba93 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -266,6 +266,10 @@ static void WipeDeviceService(unique_fd fd, const std::string& args) { } } +asocket* daemon_service_to_socket(std::string_view) { + return nullptr; +} + unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { // Common services that are supported both in sideload and rescue modes. if (android::base::ConsumePrefix(&name, "reboot:")) { From 500ed46d9effc1f5269cf7672f3f60f85195c418 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Fri, 10 Apr 2020 08:41:19 -0700 Subject: [PATCH 163/171] Import translations. DO NOT MERGE Auto-generated-cl: translation import Change-Id: I7e9b0f4f7433c275413541e048644e3fbe2a4a61 --- tools/recovery_l10n/res/values-ar/strings.xml | 4 ++-- tools/recovery_l10n/res/values-hy/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml index 69191287..a9cd2d13 100644 --- a/tools/recovery_l10n/res/values-ar/strings.xml +++ b/tools/recovery_l10n/res/values-ar/strings.xml @@ -1,11 +1,11 @@ - "جارٍ تثبيت إعادة تحميل النظام" + "جارٍ تثبيت تحديث النظام" "جارٍ محو البيانات" "ليس هناك أي أمر" "خطأ!" - "جارٍ تثبيت إعادة تحميل الأمان" + "جارٍ تثبيت تحديث الأمان" "‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط على الإعدادات الأصلية ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز." "إعادة المحاولة" "إعادة الضبط على الإعدادات الأصلية" diff --git a/tools/recovery_l10n/res/values-hy/strings.xml b/tools/recovery_l10n/res/values-hy/strings.xml index 35a0ab11..76c28a70 100644 --- a/tools/recovery_l10n/res/values-hy/strings.xml +++ b/tools/recovery_l10n/res/values-hy/strings.xml @@ -9,6 +9,6 @@ "Չհաջողվեց բեռնել Android համակարգը։ Հնարավոր է՝ ձեր տվյալները վնասված են։ Եթե նորից տեսնեք այս հաղորդագրությունը, փորձեք վերակայել սարքի կարգավորումները և ջնջել օգտատիրոջ բոլոր տվյալները։" "Նորից փորձել" "Վերակայել բոլոր տվյալները" - "Մաքրե՞լ օգտատիրոջ բոլոր տվյալները։\n\n ԱՅՍ ԳՈՐԾՈՂՈՒԹՅՈՒՆԸ ՀՆԱՐԱՎՈՐ ՉԻ ԼԻՆԻ ՀԵՏԱՐԿԵԼ" + "Ջնջե՞լ օգտատիրոջ բոլոր տվյալները։\n\n ԱՅՍ ԳՈՐԾՈՂՈՒԹՅՈՒՆԸ ՀՆԱՐԱՎՈՐ ՉԻ ԼԻՆԻ ՀԵՏԱՐԿԵԼ" "Չեղարկել" From 94abf26ba2ae26a37b44a4de4cc949bbf26f762e Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Wed, 15 Apr 2020 11:52:21 +0200 Subject: [PATCH 164/171] Rename external storage properties. According to property naming guidelines. Bug: 152170470 Bug: 153525566 Test: N/A Change-Id: I64c6d8ca49fbd72f923a8794df0929bd500dd8e9 Merged-In: I64c6d8ca49fbd72f923a8794df0929bd500dd8e9 --- recovery_utils/roots.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/recovery_utils/roots.cpp b/recovery_utils/roots.cpp index 58a31397..99f3c5dc 100644 --- a/recovery_utils/roots.cpp +++ b/recovery_utils/roots.cpp @@ -157,8 +157,8 @@ int format_volume(const std::string& volume, const std::string& directory) { bool needs_projid = false; if (volume == "/data") { - needs_casefold = android::base::GetBoolProperty("ro.emulated_storage.casefold", false); - needs_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false); + needs_casefold = android::base::GetBoolProperty("external_storage.casefold.enabled", false); + needs_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false); } // If there's a key_loc that looks like a path, it should be a block device for storing encryption From 2a4afd29a15522ccf3d8ca902214e68445bcac81 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Mon, 27 Apr 2020 20:16:18 -0700 Subject: [PATCH 165/171] Detect non-A/B vs. A/B packages correctly. Check the package metadata to determine whether this is an A/B or non-A/B update package. This is more accurate. Also checks ro.virtual_ab.allow_non_ab flag. This is useful for continuously supporting (and testing) non-A/B. Bug: 153581609 Test: apply non-A/B update on cuttlefish Change-Id: I629a533a67966d46d9cd87a59c6b9af26daf1667 --- install/install.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/install/install.cpp b/install/install.cpp index 1c9bf2fd..d404997d 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -331,15 +331,25 @@ static InstallResult TryUpdateBinary(Package* package, bool* wipe_cache, return INSTALL_CORRUPT; } - bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false); - if (is_ab) { + bool package_is_ab = get_value(metadata, "ota-type") == OtaTypeToString(OtaType::AB); + bool device_supports_ab = android::base::GetBoolProperty("ro.build.ab_update", false); + bool ab_device_supports_nonab = + android::base::GetBoolProperty("ro.virtual_ab.allow_non_ab", false); + bool device_only_supports_ab = device_supports_ab && !ab_device_supports_nonab; + + if (package_is_ab) { CHECK(package->GetType() == PackageType::kFile); } - // Verify against the metadata in the package first. - if (is_ab && !CheckPackageMetadata(metadata, OtaType::AB)) { - log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); - return INSTALL_ERROR; + // Verify against the metadata in the package first. Expects A/B metadata if: + // Package declares itself as an A/B package + // Package does not declare itself as an A/B package, but device only supports A/B; + // still calls CheckPackageMetadata to get a meaningful error message. + if (package_is_ab || device_only_supports_ab) { + if (!CheckPackageMetadata(metadata, OtaType::AB)) { + log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); + return INSTALL_ERROR; + } } ReadSourceTargetBuild(metadata, log_buffer); @@ -389,8 +399,9 @@ static InstallResult TryUpdateBinary(Package* package, bool* wipe_cache, std::vector args; if (auto setup_result = - is_ab ? SetUpAbUpdateCommands(package_path, zip, pipe_write.get(), &args) - : SetUpNonAbUpdateCommands(package_path, zip, retry_count, pipe_write.get(), &args); + package_is_ab + ? SetUpAbUpdateCommands(package_path, zip, pipe_write.get(), &args) + : SetUpNonAbUpdateCommands(package_path, zip, retry_count, pipe_write.get(), &args); !setup_result) { log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); return INSTALL_CORRUPT; From bc7e1db2118feba14d2efe19651d40df7cf202be Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Tue, 28 Apr 2020 13:26:33 -0700 Subject: [PATCH 166/171] Add slot suffix to DAP ops If device supports both A/B and non-A/B, when applying a non-A/B package, add current slot suffix and apply the update to the partition at current slot. This includes: - (un)map_partition in edify script. For example, map_partition("system") will automatically append slot suffix to "system" before calling CreateLogicalPartition. - All operations in dynamic_partitions_op_list. For example, add foo group_foo will automatically append slot suffix to foo and group_foo before editing the super partition metadata. Test: apply update Bug: 153581609 Change-Id: Idbd0bfea142529a33dddb4d2debfc74513290730 --- .../updater_runtime_dynamic_partitions.cpp | 127 ++++++++++-------- 1 file changed, 70 insertions(+), 57 deletions(-) diff --git a/updater/updater_runtime_dynamic_partitions.cpp b/updater/updater_runtime_dynamic_partitions.cpp index be9250a8..6570cfff 100644 --- a/updater/updater_runtime_dynamic_partitions.cpp +++ b/updater/updater_runtime_dynamic_partitions.cpp @@ -41,6 +41,7 @@ using android::fs_mgr::LpMetadata; using android::fs_mgr::MetadataBuilder; using android::fs_mgr::Partition; using android::fs_mgr::PartitionOpener; +using android::fs_mgr::SlotNumberForSlotSuffix; static constexpr std::chrono::milliseconds kMapTimeout{ 1000 }; @@ -48,13 +49,17 @@ static std::string GetSuperDevice() { return "/dev/block/by-name/" + fs_mgr_get_super_partition_name(); } -static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) { - auto state = DeviceMapper::Instance().GetState(partition_name); +static std::string AddSlotSuffix(const std::string& partition_name) { + return partition_name + fs_mgr_get_slot_suffix(); +} + +static bool UnmapPartitionWithSuffixOnDeviceMapper(const std::string& partition_name_suffix) { + auto state = DeviceMapper::Instance().GetState(partition_name_suffix); if (state == DmDeviceState::INVALID) { return true; } if (state == DmDeviceState::ACTIVE) { - return DestroyLogicalPartition(partition_name); + return DestroyLogicalPartition(partition_name_suffix); } LOG(ERROR) << "Unknown device mapper state: " << static_cast>(state); @@ -63,12 +68,17 @@ static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) { bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) { - auto state = DeviceMapper::Instance().GetState(partition_name); + auto partition_name_suffix = AddSlotSuffix(partition_name); + auto state = DeviceMapper::Instance().GetState(partition_name_suffix); if (state == DmDeviceState::INVALID) { CreateLogicalPartitionParams params = { .block_device = GetSuperDevice(), - .metadata_slot = 0, - .partition_name = partition_name, + // If device supports A/B, apply non-A/B update to the partition at current slot. Otherwise, + // SlotNumberForSlotSuffix("") returns 0. + .metadata_slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix()), + // If device supports A/B, apply non-A/B update to the partition at current slot. Otherwise, + // fs_mgr_get_slot_suffix() returns empty string. + .partition_name = partition_name_suffix, .force_writable = true, .timeout_ms = kMapTimeout, }; @@ -76,7 +86,7 @@ bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_nam } if (state == DmDeviceState::ACTIVE) { - return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path); + return DeviceMapper::Instance().GetDmDevicePathByName(partition_name_suffix, path); } LOG(ERROR) << "Unknown device mapper state: " << static_cast>(state); @@ -84,7 +94,7 @@ bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_nam } bool UpdaterRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) { - return ::UnmapPartitionOnDeviceMapper(partition_name); + return ::UnmapPartitionWithSuffixOnDeviceMapper(AddSlotSuffix(partition_name)); } namespace { // Ops @@ -126,22 +136,23 @@ using OpMap = std::map; bool PerformOpResize(const OpParameters& params) { if (!params.ExpectArgSize(2)) return false; - const auto& partition_name = params.arg(0); + const auto& partition_name_suffix = AddSlotSuffix(params.arg(0)); auto size = params.uint_arg(1, "size"); if (!size.has_value()) return false; - auto partition = params.builder->FindPartition(partition_name); + auto partition = params.builder->FindPartition(partition_name_suffix); if (partition == nullptr) { - LOG(ERROR) << "Failed to find partition " << partition_name + LOG(ERROR) << "Failed to find partition " << partition_name_suffix << " in dynamic partition metadata."; return false; } - if (!UnmapPartitionOnDeviceMapper(partition_name)) { - LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing."; + if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) { + LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before resizing."; return false; } if (!params.builder->ResizePartition(partition, size.value())) { - LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << "."; + LOG(ERROR) << "Failed to resize partition " << partition_name_suffix << " to size " << *size + << "."; return false; } return true; @@ -149,24 +160,25 @@ bool PerformOpResize(const OpParameters& params) { bool PerformOpRemove(const OpParameters& params) { if (!params.ExpectArgSize(1)) return false; - const auto& partition_name = params.arg(0); + const auto& partition_name_suffix = AddSlotSuffix(params.arg(0)); - if (!UnmapPartitionOnDeviceMapper(partition_name)) { - LOG(ERROR) << "Cannot unmap " << partition_name << " before removing."; + if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) { + LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before removing."; return false; } - params.builder->RemovePartition(partition_name); + params.builder->RemovePartition(partition_name_suffix); return true; } bool PerformOpAdd(const OpParameters& params) { if (!params.ExpectArgSize(2)) return false; - const auto& partition_name = params.arg(0); - const auto& group_name = params.arg(1); + const auto& partition_name_suffix = AddSlotSuffix(params.arg(0)); + const auto& group_name_suffix = AddSlotSuffix(params.arg(1)); - if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) == - nullptr) { - LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << "."; + if (params.builder->AddPartition(partition_name_suffix, group_name_suffix, + LP_PARTITION_ATTR_READONLY) == nullptr) { + LOG(ERROR) << "Failed to add partition " << partition_name_suffix << " to group " + << group_name_suffix << "."; return false; } return true; @@ -174,21 +186,21 @@ bool PerformOpAdd(const OpParameters& params) { bool PerformOpMove(const OpParameters& params) { if (!params.ExpectArgSize(2)) return false; - const auto& partition_name = params.arg(0); - const auto& new_group = params.arg(1); + const auto& partition_name_suffix = AddSlotSuffix(params.arg(0)); + const auto& new_group_name_suffix = AddSlotSuffix(params.arg(1)); - auto partition = params.builder->FindPartition(partition_name); + auto partition = params.builder->FindPartition(partition_name_suffix); if (partition == nullptr) { - LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group - << " because it is not found."; + LOG(ERROR) << "Cannot move partition " << partition_name_suffix << " to group " + << new_group_name_suffix << " because it is not found."; return false; } - auto old_group = partition->group_name(); - if (old_group != new_group) { - if (!params.builder->ChangePartitionGroup(partition, new_group)) { - LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group - << " to group " << new_group << "."; + auto old_group_name_suffix = partition->group_name(); + if (old_group_name_suffix != new_group_name_suffix) { + if (!params.builder->ChangePartitionGroup(partition, new_group_name_suffix)) { + LOG(ERROR) << "Cannot move partition " << partition_name_suffix << " from group " + << old_group_name_suffix << " to group " << new_group_name_suffix << "."; return false; } } @@ -197,22 +209,22 @@ bool PerformOpMove(const OpParameters& params) { bool PerformOpAddGroup(const OpParameters& params) { if (!params.ExpectArgSize(2)) return false; - const auto& group_name = params.arg(0); + const auto& group_name_suffix = AddSlotSuffix(params.arg(0)); auto maximum_size = params.uint_arg(1, "maximum_size"); if (!maximum_size.has_value()) return false; - auto group = params.builder->FindGroup(group_name); + auto group = params.builder->FindGroup(group_name_suffix); if (group != nullptr) { - LOG(ERROR) << "Cannot add group " << group_name << " because it already exists."; + LOG(ERROR) << "Cannot add group " << group_name_suffix << " because it already exists."; return false; } if (maximum_size.value() == 0) { - LOG(WARNING) << "Adding group " << group_name << " with no size limits."; + LOG(WARNING) << "Adding group " << group_name_suffix << " with no size limits."; } - if (!params.builder->AddGroup(group_name, maximum_size.value())) { - LOG(ERROR) << "Failed to add group " << group_name << " with maximum size " + if (!params.builder->AddGroup(group_name_suffix, maximum_size.value())) { + LOG(ERROR) << "Failed to add group " << group_name_suffix << " with maximum size " << maximum_size.value() << "."; return false; } @@ -221,20 +233,20 @@ bool PerformOpAddGroup(const OpParameters& params) { bool PerformOpResizeGroup(const OpParameters& params) { if (!params.ExpectArgSize(2)) return false; - const auto& group_name = params.arg(0); + const auto& group_name_suffix = AddSlotSuffix(params.arg(0)); auto new_size = params.uint_arg(1, "maximum_size"); if (!new_size.has_value()) return false; - auto group = params.builder->FindGroup(group_name); + auto group = params.builder->FindGroup(group_name_suffix); if (group == nullptr) { - LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found."; + LOG(ERROR) << "Cannot resize group " << group_name_suffix << " because it is not found."; return false; } auto old_size = group->maximum_size(); if (old_size != new_size.value()) { - if (!params.builder->ChangeGroupSize(group_name, new_size.value())) { - LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to " + if (!params.builder->ChangeGroupSize(group_name_suffix, new_size.value())) { + LOG(ERROR) << "Cannot resize group " << group_name_suffix << " from " << old_size << " to " << new_size.value() << "."; return false; } @@ -243,8 +255,8 @@ bool PerformOpResizeGroup(const OpParameters& params) { } std::vector ListPartitionNamesInGroup(MetadataBuilder* builder, - const std::string& group_name) { - auto partitions = builder->ListPartitionsInGroup(group_name); + const std::string& group_name_suffix) { + auto partitions = builder->ListPartitionsInGroup(group_name_suffix); std::vector partition_names; std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names), [](Partition* partition) { return partition->name(); }); @@ -253,15 +265,16 @@ std::vector ListPartitionNamesInGroup(MetadataBuilder* builder, bool PerformOpRemoveGroup(const OpParameters& params) { if (!params.ExpectArgSize(1)) return false; - const auto& group_name = params.arg(0); + const auto& group_name_suffix = AddSlotSuffix(params.arg(0)); - auto partition_names = ListPartitionNamesInGroup(params.builder, group_name); + auto partition_names = ListPartitionNamesInGroup(params.builder, group_name_suffix); if (!partition_names.empty()) { - LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions [" + LOG(ERROR) << "Cannot remove group " << group_name_suffix + << " because it still contains partitions [" << android::base::Join(partition_names, ", ") << "]"; return false; } - params.builder->RemoveGroupAndPartitions(group_name); + params.builder->RemoveGroupAndPartitions(group_name_suffix); return true; } @@ -269,16 +282,16 @@ bool PerformOpRemoveAllGroups(const OpParameters& params) { if (!params.ExpectArgSize(0)) return false; auto group_names = params.builder->ListGroups(); - for (const auto& group_name : group_names) { - auto partition_names = ListPartitionNamesInGroup(params.builder, group_name); - for (const auto& partition_name : partition_names) { - if (!UnmapPartitionOnDeviceMapper(partition_name)) { - LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name - << "."; + for (const auto& group_name_suffix : group_names) { + auto partition_names = ListPartitionNamesInGroup(params.builder, group_name_suffix); + for (const auto& partition_name_suffix : partition_names) { + if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) { + LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before removing group " + << group_name_suffix << "."; return false; } } - params.builder->RemoveGroupAndPartitions(group_name); + params.builder->RemoveGroupAndPartitions(group_name_suffix); } return true; } From dff80042750992ed635056cd9719481a14f93007 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Tue, 28 Apr 2020 13:31:11 -0700 Subject: [PATCH 167/171] Add add_slot_suffix function. This function appends androidboot.slot_suffix to the value of the argument. Test: apply update Bug: 153581609 Change-Id: I28a4047b5f2051acc039084f65a71deb492d9dcb --- .clang-format | 1 + edify/include/edify/updater_runtime_interface.h | 3 +++ updater/include/updater/simulator_runtime.h | 1 + updater/include/updater/updater_runtime.h | 1 + updater/install.cpp | 16 ++++++++++++++++ updater/simulator_runtime.cpp | 5 +++++ updater/updater_runtime.cpp | 5 +++++ 7 files changed, 32 insertions(+) diff --git a/.clang-format b/.clang-format index 4a3bd2fc..6fa717cf 100644 --- a/.clang-format +++ b/.clang-format @@ -36,6 +36,7 @@ AllowShortIfStatementsOnASingleLine: true ColumnLimit: 100 CommentPragmas: NOLINT:.* DerivePointerAlignment: false +IncludeBlocks: Preserve IndentWidth: 2 PointerAlignment: Left TabWidth: 2 diff --git a/edify/include/edify/updater_runtime_interface.h b/edify/include/edify/updater_runtime_interface.h index d3d26da6..bdd6aecc 100644 --- a/edify/include/edify/updater_runtime_interface.h +++ b/edify/include/edify/updater_runtime_interface.h @@ -71,4 +71,7 @@ class UpdaterRuntimeInterface { virtual bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) = 0; virtual bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) = 0; virtual bool UpdateDynamicPartitions(const std::string_view op_list_value) = 0; + + // On devices supports A/B, add current slot suffix to arg. Otherwise, return |arg| as is. + virtual std::string AddSlotSuffix(const std::string_view arg) const = 0; }; diff --git a/updater/include/updater/simulator_runtime.h b/updater/include/updater/simulator_runtime.h index 9f7847b4..fa878db3 100644 --- a/updater/include/updater/simulator_runtime.h +++ b/updater/include/updater/simulator_runtime.h @@ -53,6 +53,7 @@ class SimulatorRuntime : public UpdaterRuntimeInterface { bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override; bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override; bool UpdateDynamicPartitions(const std::string_view op_list_value) override; + std::string AddSlotSuffix(const std::string_view arg) const override; private: std::string FindBlockDeviceName(const std::string_view name) const override; diff --git a/updater/include/updater/updater_runtime.h b/updater/include/updater/updater_runtime.h index 8fc066f6..b943dfcf 100644 --- a/updater/include/updater/updater_runtime.h +++ b/updater/include/updater/updater_runtime.h @@ -56,6 +56,7 @@ class UpdaterRuntime : public UpdaterRuntimeInterface { bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override; bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override; bool UpdateDynamicPartitions(const std::string_view op_list_value) override; + std::string AddSlotSuffix(const std::string_view arg) const override; private: struct selabel_handle* sehandle_{ nullptr }; diff --git a/updater/install.cpp b/updater/install.cpp index 7608dc3c..afa5195d 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -852,6 +852,20 @@ Value* Tune2FsFn(const char* name, State* state, const std::vector>& argv) { + if (argv.size() != 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size()); + } + std::vector args; + if (!ReadArgs(state, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& arg = args[0]; + auto updater_runtime = state->updater->GetRuntime(); + return StringValue(updater_runtime->AddSlotSuffix(arg)); +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); RegisterFunction("is_mounted", IsMountedFn); @@ -885,4 +899,6 @@ void RegisterInstallFunctions() { RegisterFunction("enable_reboot", EnableRebootFn); RegisterFunction("tune2fs", Tune2FsFn); + + RegisterFunction("add_slot_suffix", AddSlotSuffixFn); } diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp index 3ed7bf33..57dfb32d 100644 --- a/updater/simulator_runtime.cpp +++ b/updater/simulator_runtime.cpp @@ -130,3 +130,8 @@ bool SimulatorRuntime::UpdateDynamicPartitions(const std::string_view op_list_va } return true; } + +std::string SimulatorRuntime::AddSlotSuffix(const std::string_view arg) const { + LOG(INFO) << "Skip adding slot suffix to " << arg; + return std::string(arg); +} diff --git a/updater/updater_runtime.cpp b/updater/updater_runtime.cpp index b1b8863f..e9383050 100644 --- a/updater/updater_runtime.cpp +++ b/updater/updater_runtime.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -186,3 +187,7 @@ int UpdaterRuntime::Tune2Fs(const std::vector& args) const { // tune2fs changes the filesystem parameters on an ext2 filesystem; it returns 0 on success. return tune2fs_main(tune2fs_args.size() - 1, tune2fs_args.data()); } + +std::string UpdaterRuntime::AddSlotSuffix(const std::string_view arg) const { + return std::string(arg) + fs_mgr_get_slot_suffix(); +} From 4d0df88d88db381422ee912f2609258d72eca4cf Mon Sep 17 00:00:00 2001 From: Hongguang Chen Date: Tue, 21 Apr 2020 20:58:04 -0700 Subject: [PATCH 168/171] Add EthernetDevice to manage ethernet connection. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current fastbootd only supports USB protocol. But some Android TV devices are built without USB port. The fastbootd cannot be used on those ATV devices due to it. aosp/1295566 enables fastbootd over ethernet. This change adds an EthernetDevice to manage ethernet connection and set fastbootd protcol property fastbootd.protocol to enable fastbootd over ethernet in recovery mode. It uses IPv6 link-local address to not expose the devices out of the network segment. The devices who want to use this EthernetDevice should add “TARGET_RECOVERY_UI_LIB := librecovery_ui_ethernet” into BoardConfig.mk. BUG: 152544169 BUG: 155198345 Test: Enter and exit fastboot mode and check eth0 IPv6 link-local address on screen UI. Change-Id: I73fe44be3790bdba5a6059fbba3f7264b21eed99 Merged-In: I73fe44be3790bdba5a6059fbba3f7264b21eed99 --- Android.mk | 6 +- fastboot/fastboot.cpp | 1 + recovery_main.cpp | 5 + recovery_ui/Android.bp | 21 +++ recovery_ui/ethernet_device.cpp | 136 ++++++++++++++++++ recovery_ui/ethernet_ui.cpp | 32 +++++ recovery_ui/include/recovery_ui/device.h | 12 ++ recovery_ui/include/recovery_ui/ethernet_ui.h | 34 +++++ 8 files changed, 244 insertions(+), 3 deletions(-) create mode 100644 recovery_ui/ethernet_device.cpp create mode 100644 recovery_ui/ethernet_ui.cpp create mode 100644 recovery_ui/include/recovery_ui/ethernet_ui.h diff --git a/Android.mk b/Android.mk index 9806d109..9f691531 100644 --- a/Android.mk +++ b/Android.mk @@ -18,9 +18,9 @@ LOCAL_PATH := $(call my-dir) RECOVERY_API_VERSION := 3 RECOVERY_FSTAB_VERSION := 2 -# TARGET_RECOVERY_UI_LIB should be one of librecovery_ui_{default,wear,vr} or a device-specific -# module that defines make_device() and the exact RecoveryUI class for the target. It defaults to -# librecovery_ui_default, which uses ScreenRecoveryUI. +# TARGET_RECOVERY_UI_LIB should be one of librecovery_ui_{default,wear,vr,ethernet} or a +# device-specific module that defines make_device() and the exact RecoveryUI class for the +# target. It defaults to librecovery_ui_default, which uses ScreenRecoveryUI. TARGET_RECOVERY_UI_LIB ?= librecovery_ui_default # librecovery_ui_ext (shared library) diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 20233499..a0930087 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -52,6 +52,7 @@ Device::BuiltinAction StartFastboot(Device* device, const std::vectorResetKeyInterruptStatus(); ui->SetTitle(title_lines); ui->ShowText(true); + device->StartFastboot(); // Reset to normal system boot so recovery won't cycle indefinitely. // TODO(b/112277594) Clear only if 'recovery' field of BCB is empty. If not, diff --git a/recovery_main.cpp b/recovery_main.cpp index 30a1fc0a..80cba61d 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -471,6 +471,11 @@ int main(int argc, char** argv) { std::string usb_config = fastboot ? "fastboot" : IsRoDebuggable() || IsDeviceUnlocked() ? "adb" : "none"; std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); + if (fastboot) { + device->PreFastboot(); + } else { + device->PreRecovery(); + } if (usb_config != usb_state) { if (!SetUsbConfig("none")) { LOG(ERROR) << "Failed to clear USB config"; diff --git a/recovery_ui/Android.bp b/recovery_ui/Android.bp index 149ef8ac..9dfee5fd 100644 --- a/recovery_ui/Android.bp +++ b/recovery_ui/Android.bp @@ -22,6 +22,7 @@ cc_library { srcs: [ "device.cpp", + "ethernet_ui.cpp", "screen_ui.cpp", "stub_ui.cpp", "ui.cpp", @@ -90,3 +91,23 @@ cc_library_static { export_include_dirs: ["include"], } + +// The default device that uses EthernetRecoveryUI. +cc_library_static { + name: "librecovery_ui_ethernet", + recovery_available: true, + + defaults: [ + "recovery_defaults", + ], + + srcs: [ + "ethernet_device.cpp", + ], + + shared_libs: [ + "libbase", + ], + + export_include_dirs: ["include"], +} diff --git a/recovery_ui/ethernet_device.cpp b/recovery_ui/ethernet_device.cpp new file mode 100644 index 00000000..39ec65dc --- /dev/null +++ b/recovery_ui/ethernet_device.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "recovery_ui/device.h" +#include "recovery_ui/ethernet_ui.h" + +class EthernetDevice : public Device { + public: + explicit EthernetDevice(EthernetRecoveryUI* ui); + + void PreRecovery() override; + void PreFastboot() override; + + private: + int SetInterfaceFlags(const unsigned set, const unsigned clr); + void SetTitleIPv6LinkLocalAddress(const bool interface_up); + + android::base::unique_fd ctl_sock_; + static const std::string interface; +}; + +const std::string EthernetDevice::interface = "eth0"; + +EthernetDevice::EthernetDevice(EthernetRecoveryUI* ui) + : Device(ui), ctl_sock_(socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) { + if (ctl_sock_ < 0) { + PLOG(ERROR) << "Failed to open socket"; + } +} + +void EthernetDevice::PreRecovery() { + SetInterfaceFlags(0, IFF_UP); + SetTitleIPv6LinkLocalAddress(false); +} + +void EthernetDevice::PreFastboot() { + android::base::SetProperty("fastbootd.protocol", "tcp"); + + if (SetInterfaceFlags(IFF_UP, 0) < 0) { + LOG(ERROR) << "Failed to bring up interface"; + return; + } + + SetTitleIPv6LinkLocalAddress(true); +} + +int EthernetDevice::SetInterfaceFlags(const unsigned set, const unsigned clr) { + struct ifreq ifr; + + if (ctl_sock_ < 0) { + return -1; + } + + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ - 1] = 0; + + if (ioctl(ctl_sock_, SIOCGIFFLAGS, &ifr) < 0) { + PLOG(ERROR) << "Failed to get interface active flags"; + return -1; + } + ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set; + + if (ioctl(ctl_sock_, SIOCSIFFLAGS, &ifr) < 0) { + PLOG(ERROR) << "Failed to set interface active flags"; + return -1; + } + + return 0; +} + +void EthernetDevice::SetTitleIPv6LinkLocalAddress(const bool interface_up) { + auto recovery_ui = reinterpret_cast(GetUI()); + if (!interface_up) { + recovery_ui->SetIPv6LinkLocalAddress(); + return; + } + + struct ifaddrs* ifaddr; + if (getifaddrs(&ifaddr) == -1) { + PLOG(ERROR) << "Failed to get interface addresses"; + recovery_ui->SetIPv6LinkLocalAddress(); + return; + } + + std::unique_ptr guard{ ifaddr, freeifaddrs }; + for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_INET6 || interface != ifa->ifa_name) { + continue; + } + + auto current_addr = reinterpret_cast(ifa->ifa_addr); + if (!IN6_IS_ADDR_LINKLOCAL(&(current_addr->sin6_addr))) { + continue; + } + + char addrstr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, reinterpret_cast(¤t_addr->sin6_addr), addrstr, + INET6_ADDRSTRLEN); + LOG(INFO) << "Our IPv6 link-local address is " << addrstr; + recovery_ui->SetIPv6LinkLocalAddress(addrstr); + return; + } + + recovery_ui->SetIPv6LinkLocalAddress(); +} + +// ----------------------------------------------------------------------------------------- +Device* make_device() { + return new EthernetDevice(new EthernetRecoveryUI); +} diff --git a/recovery_ui/ethernet_ui.cpp b/recovery_ui/ethernet_ui.cpp new file mode 100644 index 00000000..535d407f --- /dev/null +++ b/recovery_ui/ethernet_ui.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "recovery_ui/ethernet_ui.h" + +#include + +void EthernetRecoveryUI::SetTitle(const std::vector& lines) { + ScreenRecoveryUI::SetTitle(lines); + + // Append IP address, if any + if (!address_.empty()) { + title_lines_.push_back("IPv6 link-local address - " + address_); + } +} + +void EthernetRecoveryUI::SetIPv6LinkLocalAddress(const std::string& address) { + address_ = address; +} diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index f4f99363..76166f09 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -79,10 +79,22 @@ class Device { ui_.reset(ui); } + // Called before recovery mode started up, to perform whatever device-specific recovery mode + // preparation as needed. + virtual void PreRecovery() {} + // 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() {} + // Called before fastboot mode is started up, to perform whatever device-specific fastboot mode + // preparation as needed. + virtual void PreFastboot() {} + + // Called when fastboot starts up (after the UI has been obtained and initialized and after the + // arguments have been parsed, but before anything else). + virtual void StartFastboot() {} + // 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; // recovery will be at the main menu with it invisible after an unsuccessful operation, such as diff --git a/recovery_ui/include/recovery_ui/ethernet_ui.h b/recovery_ui/include/recovery_ui/ethernet_ui.h new file mode 100644 index 00000000..f40c73f6 --- /dev/null +++ b/recovery_ui/include/recovery_ui/ethernet_ui.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 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_ETHERNET_UI_H +#define RECOVERY_ETHERNET_UI_H + +#include "screen_ui.h" + +class EthernetRecoveryUI : public ScreenRecoveryUI { + public: + EthernetRecoveryUI() {} + void SetTitle(const std::vector& lines) override; + + // For EthernetDevice + void SetIPv6LinkLocalAddress(const std::string& address = ""); + + private: + std::string address_; +}; + +#endif // RECOVERY_ETHERNET_UI_H From 4473e1689fc7bb788749b4d5e143eafa82b6f473 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Mon, 1 Jun 2020 18:58:57 -0700 Subject: [PATCH 169/171] Track rename of adbd_system_binaries. Bug: http://b/157709367 Test: treehugger Change-Id: Ic205acc56a763c38ba689b2b895609b5e878d813 (cherry picked from commit d4a964f121ec03124a599eeed13a334677e942e9) --- minadbd/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minadbd/Android.bp b/minadbd/Android.bp index 793680f3..4cdcac6d 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -99,7 +99,7 @@ cc_binary { ], required: [ - "adbd_system_binaries_recovery", + "adbd_system_api_recovery", ] } From fd33b4992dd454b6512bb92c97550c9822acfc4c Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Fri, 17 Jul 2020 17:40:48 -0700 Subject: [PATCH 170/171] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Id59b183068ff2cdf8c58f42b9b7c8406cc04b8f2 --- tools/recovery_l10n/res/values-ur/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/recovery_l10n/res/values-ur/strings.xml b/tools/recovery_l10n/res/values-ur/strings.xml index da03f197..13dc6b37 100644 --- a/tools/recovery_l10n/res/values-ur/strings.xml +++ b/tools/recovery_l10n/res/values-ur/strings.xml @@ -6,9 +6,9 @@ "کوئی کمانڈ نہیں ہے" "خرابی!" "سیکیورٹی اپ ڈیٹ انسٹال ہو رہی ہے" - "‏Android سسٹم لوڈ نہیں کیا جا سکتا۔ آپ کا ڈیٹا خراب ہو سکتا ہے۔ اگر آپ کو مستقل یہ پیغام موصول ہوتا ہے تو آپ کو فیکٹری ڈیٹا کی دوبارہ ترتیب انجام دینے اور اس آلہ پر اسٹور کردہ سبھی صارف ڈیٹا کو مٹانے کی ضرورت پڑ سکتی ہے۔" + "‏Android سسٹم لوڈ نہیں کیا جا سکتا۔ آپ کا ڈیٹا خراب ہو سکتا ہے۔ اگر آپ کو مستقل یہ پیغام موصول ہوتا ہے تو آپ کو فیکٹری ڈیٹا ری سیٹ انجام دینے اور اس آلہ پر اسٹور کردہ سبھی صارف ڈیٹا کو مٹانے کی ضرورت پڑ سکتی ہے۔" "دوبارہ کوشش کریں" - "فیکٹری ڈیٹا کی دوبارہ ترتیب" + "فیکٹری ڈیٹا ری سیٹ" "سبھی صارف ڈیٹا صاف کریں؟\n\n اسے کالعدم نہیں کیا جا سکتا!" "منسوخ کریں" From f81364cd8c9bd2e7d39e0a794e00c09746c32e43 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 2 Aug 2020 17:10:14 +0000 Subject: [PATCH 171/171] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: If8fe8b3e5842518a2c61da1ecf1aaddcfc9c24e3 --- tools/recovery_l10n/res/values-ur/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/recovery_l10n/res/values-ur/strings.xml b/tools/recovery_l10n/res/values-ur/strings.xml index da03f197..13dc6b37 100644 --- a/tools/recovery_l10n/res/values-ur/strings.xml +++ b/tools/recovery_l10n/res/values-ur/strings.xml @@ -6,9 +6,9 @@ "کوئی کمانڈ نہیں ہے" "خرابی!" "سیکیورٹی اپ ڈیٹ انسٹال ہو رہی ہے" - "‏Android سسٹم لوڈ نہیں کیا جا سکتا۔ آپ کا ڈیٹا خراب ہو سکتا ہے۔ اگر آپ کو مستقل یہ پیغام موصول ہوتا ہے تو آپ کو فیکٹری ڈیٹا کی دوبارہ ترتیب انجام دینے اور اس آلہ پر اسٹور کردہ سبھی صارف ڈیٹا کو مٹانے کی ضرورت پڑ سکتی ہے۔" + "‏Android سسٹم لوڈ نہیں کیا جا سکتا۔ آپ کا ڈیٹا خراب ہو سکتا ہے۔ اگر آپ کو مستقل یہ پیغام موصول ہوتا ہے تو آپ کو فیکٹری ڈیٹا ری سیٹ انجام دینے اور اس آلہ پر اسٹور کردہ سبھی صارف ڈیٹا کو مٹانے کی ضرورت پڑ سکتی ہے۔" "دوبارہ کوشش کریں" - "فیکٹری ڈیٹا کی دوبارہ ترتیب" + "فیکٹری ڈیٹا ری سیٹ" "سبھی صارف ڈیٹا صاف کریں؟\n\n اسے کالعدم نہیں کیا جا سکتا!" "منسوخ کریں"