diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2019-04-30 05:15:13 +0200 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2019-04-30 05:15:13 +0200 |
commit | e1299d94095e5fc018472b27cc8d980234f2ffb2 (patch) | |
tree | b0e3acf9305aec92ab4f388b6948caeccb05a8f7 | |
parent | Snap for 5480912 from d0052c30cef1202fff6bb0f87d64eddb98cfbc99 to qt-release (diff) | |
parent | Parse BCB command to enter rescue mode. (diff) | |
download | android_bootable_recovery-e1299d94095e5fc018472b27cc8d980234f2ffb2.tar android_bootable_recovery-e1299d94095e5fc018472b27cc8d980234f2ffb2.tar.gz android_bootable_recovery-e1299d94095e5fc018472b27cc8d980234f2ffb2.tar.bz2 android_bootable_recovery-e1299d94095e5fc018472b27cc8d980234f2ffb2.tar.lz android_bootable_recovery-e1299d94095e5fc018472b27cc8d980234f2ffb2.tar.xz android_bootable_recovery-e1299d94095e5fc018472b27cc8d980234f2ffb2.tar.zst android_bootable_recovery-e1299d94095e5fc018472b27cc8d980234f2ffb2.zip |
-rw-r--r-- | fuse_sideload/include/fuse_provider.h | 23 | ||||
-rw-r--r-- | install/adb_install.cpp | 137 | ||||
-rw-r--r-- | install/include/install/adb_install.h | 6 | ||||
-rw-r--r-- | install/include/install/install.h | 3 | ||||
-rw-r--r-- | minadbd/Android.bp | 3 | ||||
-rw-r--r-- | minadbd/fuse_adb_provider.cpp | 4 | ||||
-rw-r--r-- | minadbd/fuse_adb_provider.h | 15 | ||||
-rw-r--r-- | minadbd/minadbd.cpp | 12 | ||||
-rw-r--r-- | minadbd/minadbd_services.cpp | 153 | ||||
-rw-r--r-- | minadbd/minadbd_services.h | 6 | ||||
-rw-r--r-- | minadbd/minadbd_services_test.cpp | 213 | ||||
-rw-r--r-- | minadbd/minadbd_types.h | 14 | ||||
-rw-r--r-- | recovery.cpp | 84 | ||||
-rw-r--r-- | recovery_main.cpp | 35 | ||||
-rw-r--r-- | recovery_ui/device.cpp | 1 | ||||
-rw-r--r-- | recovery_ui/include/recovery_ui/device.h | 8 | ||||
-rw-r--r-- | tests/component/sideload_test.cpp | 22 |
17 files changed, 616 insertions, 123 deletions
diff --git a/fuse_sideload/include/fuse_provider.h b/fuse_sideload/include/fuse_provider.h index 499d57aa0..59059cf9b 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/install/adb_install.cpp b/install/adb_install.cpp index 548b6e5b9..9dfe0407f 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -31,6 +31,8 @@ #include <atomic> #include <functional> #include <map> +#include <utility> +#include <vector> #include <android-base/file.h> #include <android-base/logging.h> @@ -42,30 +44,35 @@ #include "fuse_sideload.h" #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<bool()>; +// 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<std::pair<bool, bool>()>; static bool SetUsbConfig(const std::string& state) { android::base::SetProperty("sys.usb.config", state); return android::base::WaitForProperty("sys.usb.state", state); } -// Parses the minadbd command in |message|; returns MinadbdCommands::kError upon errors. -static MinadbdCommands ParseMinadbdCommands(const std::string& message) { +// 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<uint32_t>(cmd_code_string.c_str()); - if (cmd_code >= static_cast<uint32_t>(MinadbdCommands::kError)) { + if (cmd_code >= static_cast<uint32_t>(MinadbdCommand::kError)) { LOG(ERROR) << "Unsupported command code: " << cmd_code; - return MinadbdCommands::kError; + return MinadbdCommand::kError; } - return static_cast<MinadbdCommands>(cmd_code); + return static_cast<MinadbdCommand>(cmd_code); } static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { @@ -80,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; @@ -95,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; } @@ -106,13 +116,40 @@ 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<MinadbdCommands, CommandFunction>& command_map) { +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::REBOOT_FASTBOOT; + break; + case MinadbdCommand::kRebootRecovery: + *reboot_action = Device::REBOOT_RECOVERY; + break; + case MinadbdCommand::kRebootRescue: + *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<MinadbdCommand, CommandFunction>& command_map) { char buffer[kMinadbdMessageSize]; if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) { PLOG(ERROR) << "Failed to read message from minadbd"; @@ -120,8 +157,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()) { @@ -133,17 +170,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<unsigned int>(command_type); - return WriteStatusToFd(MinadbdCommandStatus::kFailure, socket_fd); + const auto [result, should_continue] = command_func(); + LOG(INFO) << "Command " << static_cast<uint32_t>(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<MinadbdCommands, CommandFunction>& command_map) { + RecoveryUI* ui, pid_t minadbd_pid, android::base::unique_fd&& socket_fd, + const std::map<MinadbdCommand, CommandFunction>& command_map) { android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC)); if (epoll_fd == -1) { PLOG(ERROR) << "Failed to create epoll"; @@ -165,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 = @@ -228,7 +271,8 @@ static void ListenAndExecuteMinadbdCommands( // b11. exit the listening loop // static void CreateMinadbdServiceAndExecuteCommands( - const std::map<MinadbdCommands, CommandFunction>& command_map) { + RecoveryUI* ui, const std::map<MinadbdCommand, CommandFunction>& command_map, + bool rescue_mode) { signal(SIGPIPE, SIG_IGN); android::base::unique_fd recovery_socket; @@ -245,9 +289,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<std::string> 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); } @@ -260,9 +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(); } @@ -280,7 +330,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int apply_from_adb(RecoveryUI* ui) { +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. @@ -289,16 +339,31 @@ 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 <filename>\"...\n"); + if (!rescue_mode) { + ui->Print( + "\n\nNow send the package you want to apply\n" + "to the device with \"adb sideload <filename>\"...\n"); + } else { + ui->Print("\n\nWaiting for rescue commands...\n"); + } int install_result = INSTALL_ERROR; - std::map<MinadbdCommands, CommandFunction> command_map{ - { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, + std::map<MinadbdCommand, CommandFunction> 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); + 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/install/include/install/adb_install.h b/install/include/install/adb_install.h index f7b065b64..49b32b54f 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -16,6 +16,10 @@ #pragma once +#include <recovery_ui/device.h> #include <recovery_ui/ui.h> -int apply_from_adb(RecoveryUI* ui); +// 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 1e41b4843..c0a8f1f4c 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/Android.bp b/minadbd/Android.bp index e4f7712e5..007e5057b 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -76,7 +76,6 @@ cc_binary { "libadbd", "libbase", "libcrypto", - "libfusesideload", "libminadbd_services", ], } @@ -91,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/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp index 9d19a1ec3..47719b07a 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 3fb689bd4..c5561e57d 100644 --- a/minadbd/fuse_adb_provider.h +++ b/minadbd/fuse_adb_provider.h @@ -14,25 +14,22 @@ * limitations under the License. */ -#ifndef __FUSE_ADB_PROVIDER_H -#define __FUSE_ADB_PROVIDER_H +#pragma once #include <stdint.h> -#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; + private: + // The underlying source to read data from (i.e. the one that talks to the host). + int fd_; }; - -#endif diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp index 57158ad57..c80d5490a 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 79e6fc4e0..9b1999d90 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -28,15 +28,19 @@ #include <string> #include <string_view> #include <thread> +#include <unordered_set> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/memory.h> +#include <android-base/parseint.h> +#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include "adb.h" #include "adb_unique_fd.h" +#include "adb_utils.h" #include "fdevent.h" #include "fuse_adb_provider.h" #include "fuse_sideload.h" @@ -45,11 +49,22 @@ #include "sysdeps.h" 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; } -static bool WriteCommandToFd(MinadbdCommands cmd, int fd) { +void SetMinadbdRescueMode(bool rescue) { + rescue_mode = rescue; +} + +void SetSideloadMountPoint(const std::string& path) { + sideload_mount_point = path; +} + +static bool WriteCommandToFd(MinadbdCommand cmd, int fd) { char message[kMinadbdMessageSize]; memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix)); android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), cmd); @@ -81,47 +96,149 @@ 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); + if (!WriteCommandToFd(MinadbdCommand::kInstall, minadbd_socket)) { + return kMinadbdSocketIOError; } - auto adb_data_reader = - std::make_unique<FuseAdbDataProvider>(std::move(sfd), file_size, block_size); - if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) { + auto adb_data_reader = std::make_unique<FuseAdbDataProvider>(sfd, file_size, block_size); + if (int result = run_fuse_sideload(std::move(adb_data_reader), sideload_mount_point.c_str()); + 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; + exit(RunAdbFuseSideload(sfd.get(), args, &status)); +} + +// 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<std::string> 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); + } +} + +// 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); } - LOG(INFO) << "Got command status: " << static_cast<unsigned int>(status); - - LOG(INFO) << "sideload_host finished"; - exit(kMinadbdSuccess); } unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { + // Common services that are supported both in sideload and rescue modes. + if (ConsumePrefix(&name, "reboot:")) { + // "reboot:<target>", where target must be one of the following. + std::string args(name); + if (args.empty() || args == "bootloader" || args == "rescue" || args == "recovery" || + 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:<file-size>:<block-size> + std::string args(name); + return create_service_thread( + "rescue-install", std::bind(RescueInstallHostService, std::placeholders::_1, args)); + } else if (ConsumePrefix(&name, "rescue-getprop:")) { + // rescue-getprop:<prop> + std::string args(name); + return create_service_thread( + "rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args)); + } + 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). 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:<file-size>:<block-size> + 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_services.h b/minadbd/minadbd_services.h index 6835bd770..5575c6b8e 100644 --- a/minadbd/minadbd_services.h +++ b/minadbd/minadbd_services.h @@ -16,4 +16,10 @@ #pragma once +#include <string> + 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 000000000..593180bb3 --- /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 <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <strings.h> +#include <sys/mount.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/unique_fd.h> +#include <gtest/gtest.h> + +#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, MinadbdCommand expected_command) { + std::vector<uint8_t> received(kMinadbdMessageSize, '\0'); + ASSERT_TRUE(android::base::ReadFully(fd, received.data(), kMinadbdMessageSize)); + + std::vector<uint8_t> 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_, MinadbdCommand::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<uint8_t> 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<uint8_t> 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_, MinadbdCommand::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), ""); +} diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h index 7bd69096a..b370b7952 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 { @@ -42,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 0e6e4976d..f9b3bfc0b 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -509,11 +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::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: @@ -537,24 +540,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::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; + Device::BuiltinAction reboot_action; + if (chosen_action == Device::ENTER_RESCUE) { + // Switch to graphics screen. + ui->ShowText(false); + status = ApplyFromAdb(ui, true /* rescue_mode */, &reboot_action); + } else if (chosen_action == Device::APPLY_ADB_SIDELOAD) { + 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; } @@ -715,6 +730,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri { "locale", required_argument, nullptr, 0 }, { "prompt_and_wipe_data", no_argument, nullptr, 0 }, { "reason", required_argument, nullptr, 0 }, + { "rescue", no_argument, nullptr, 0 }, { "retry_count", required_argument, nullptr, 0 }, { "security", no_argument, nullptr, 0 }, { "show_text", no_argument, nullptr, 't' }, @@ -737,6 +753,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri size_t wipe_package_size = 0; bool sideload = false; bool sideload_auto_reboot = false; + bool rescue = false; bool just_exit = false; bool shutdown_after = false; bool fsck_unshare_blocks = false; @@ -770,6 +787,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri should_prompt_and_wipe_data = true; } else if (option == "reason") { reason = optarg; + } else if (option == "rescue") { + rescue = true; } else if (option == "retry_count") { android::base::ParseInt(optarg, &retry_count, 0); } else if (option == "security") { @@ -833,6 +852,9 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri ui->Print("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 @@ -916,21 +938,24 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri status = INSTALL_ERROR; } } else if (sideload) { - // 'adb reboot sideload' acts the same as user presses key combinations - // to enter the sideload mode. When 'sideload-auto-reboot' is used, text - // display will NOT be turned on by default. And it will reboot after - // sideload finishes even if there are errors. Unless one turns on the - // text display during the installation. This is to enable automated + // 'adb reboot sideload' acts the same as user presses key combinations to enter the sideload + // mode. When 'sideload-auto-reboot' is used, text display will NOT be turned on by default. And + // it will reboot after sideload finishes even if there are errors. This is to enable automated // testing. save_current_log = true; if (!sideload_auto_reboot) { ui->ShowText(true); } - status = apply_from_adb(ui); + 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 (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; @@ -953,23 +978,26 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri } } - Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; - // 1. If the recovery menu is visible, prompt and wait for commands. - // 2. If the state is INSTALL_NONE, wait for commands. (i.e. In user build, manually reboot into - // recovery to sideload a package.) - // 3. sideload_auto_reboot is an option only available in user-debug build, reboot the device - // without waiting. - // 4. In all other cases, reboot the device. Therefore, normal users will observe the device - // reboot after it shows the "error" screen for 5s. - if ((status == INSTALL_NONE && !sideload_auto_reboot) || ui->IsTextVisible()) { - 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 5f3ab76dd..de8ac1f42 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -155,9 +155,11 @@ static std::vector<std::string> 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; @@ -423,6 +425,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); @@ -466,6 +472,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; @@ -474,6 +481,32 @@ int main(int argc, char** argv) { android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); break; + 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/device.cpp b/recovery_ui/device.cpp index ddb0118db..e7ae1a3e1 100644 --- a/recovery_ui/device.cpp +++ b/recovery_ui/device.cpp @@ -37,6 +37,7 @@ static std::vector<std::pair<std::string, Device::BuiltinAction>> 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 3c44510c8..7c76cdb0a 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,6 +54,10 @@ class Device { KEY_INTERRUPTED = 13, ENTER_FASTBOOT = 14, ENTER_RECOVERY = 15, + ENTER_RESCUE = 16, + REBOOT_FASTBOOT = 17, + REBOOT_RECOVERY = 18, + REBOOT_RESCUE = 19, }; explicit Device(RecoveryUI* ui); diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp index f5981acbd..6add99f41 100644 --- a/tests/component/sideload_test.cpp +++ b/tests/component/sideload_test.cpp @@ -22,7 +22,6 @@ #include <android-base/file.h> #include <android-base/strings.h> -#include <android-base/unique_fd.h> #include <gtest/gtest.h> #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<FuseFileDataProvider>(android::base::unique_fd(), 4096, 4095); + auto provider_small_block = std::make_unique<FuseTestDataProvider>(4096, 4095); ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_small_block))); - auto provider_large_block = - std::make_unique<FuseFileDataProvider>(android::base::unique_fd(), 4096, (1 << 22) + 1); + auto provider_large_block = std::make_unique<FuseTestDataProvider>(4096, (1 << 22) + 1); ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_large_block))); - auto provider_too_many_blocks = std::make_unique<FuseFileDataProvider>( - android::base::unique_fd(), ((1 << 18) + 1) * 4096, 4096); + auto provider_too_many_blocks = + std::make_unique<FuseTestDataProvider>(((1 << 18) + 1) * 4096, 4096); ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_too_many_blocks))); } |