Loading Android.bp +1 −1 Original line number Original line Diff line number Diff line Loading @@ -112,7 +112,6 @@ cc_binary { ], ], shared_libs: [ shared_libs: [ "libminadbd_services", "librecovery_ui", "librecovery_ui", ], ], Loading @@ -124,6 +123,7 @@ cc_binary { required: [ required: [ "e2fsdroid.recovery", "e2fsdroid.recovery", "librecovery_ui_ext", "librecovery_ui_ext", "minadbd", "mke2fs.conf.recovery", "mke2fs.conf.recovery", "mke2fs.recovery", "mke2fs.recovery", "recovery_deps", "recovery_deps", Loading install/Android.bp +4 −0 Original line number Original line Diff line number Diff line Loading @@ -19,6 +19,10 @@ cc_defaults { "recovery_defaults", "recovery_defaults", ], ], header_libs: [ "libminadbd_headers", ], shared_libs: [ shared_libs: [ "libbase", "libbase", "libbootloader_message", "libbootloader_message", Loading install/adb_install.cpp +237 −48 Original line number Original line Diff line number Diff line Loading @@ -21,97 +21,286 @@ #include <signal.h> #include <signal.h> #include <stdlib.h> #include <stdlib.h> #include <string.h> #include <string.h> #include <sys/epoll.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/wait.h> #include <unistd.h> #include <unistd.h> #include <atomic> #include <functional> #include <map> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/logging.h> #include <android-base/memory.h> #include <android-base/properties.h> #include <android-base/properties.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include "fuse_sideload.h" #include "fuse_sideload.h" #include "install/install.h" #include "install/install.h" #include "minadbd_types.h" #include "recovery_ui/ui.h" #include "recovery_ui/ui.h" using CommandFunction = std::function<bool()>; static bool SetUsbConfig(const std::string& state) { static bool SetUsbConfig(const std::string& state) { android::base::SetProperty("sys.usb.config", state); android::base::SetProperty("sys.usb.config", state); return android::base::WaitForProperty("sys.usb.state", state); return android::base::WaitForProperty("sys.usb.state", state); } } int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { // Parses the minadbd command in |message|; returns MinadbdCommands::kError upon errors. // Save the usb state to restore after the sideload operation. static MinadbdCommands ParseMinadbdCommands(const std::string& message) { std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) { // Clean up state and stop adbd. LOG(ERROR) << "Failed to parse command in message " << message; if (usb_state != "none" && !SetUsbConfig("none")) { return MinadbdCommands::kError; LOG(ERROR) << "Failed to clear USB config"; return INSTALL_ERROR; } } ui->Print( auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix)); "\n\nNow send the package you want to apply\n" auto cmd_code = android::base::get_unaligned<uint32_t>(cmd_code_string.c_str()); "to the device with \"adb sideload <filename>\"...\n"); if (cmd_code >= static_cast<uint32_t>(MinadbdCommands::kError)) { LOG(ERROR) << "Unsupported command code: " << cmd_code; pid_t child; return MinadbdCommands::kError; if ((child = fork()) == 0) { execl("/system/bin/recovery", "recovery", "--adbd", nullptr); _exit(EXIT_FAILURE); } } if (!SetUsbConfig("sideload")) { return static_cast<MinadbdCommands>(cmd_code); 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 bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { static constexpr int ADB_INSTALL_TIMEOUT = 300; char message[kMinadbdMessageSize]; memcpy(message, kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix)); android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), status); // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host connects and starts serving a if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) { // package. Poll for its appearance. (Note that inotify doesn't work with FUSE.) PLOG(ERROR) << "Failed to write message " << message; int result = INSTALL_ERROR; return false; int status; } bool waited = false; return true; for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { if (waitpid(child, &status, WNOHANG) != 0) { result = INSTALL_ERROR; waited = true; break; } } // 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; struct stat st; if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) { if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) { sleep(1); sleep(1); continue; continue; } else { } else { ui->Print("\nTimed out waiting for package.\n\n"); ui->Print("\nTimed out waiting for fuse to be ready.\n\n"); result = INSTALL_ERROR; kill(child, SIGKILL); break; break; } } } } result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0, ui); *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0, ui); break; break; } } if (!waited) { // Calling stat() on this magic filename signals the FUSE to exit. // Calling stat() on this magic filename signals the minadbd subprocess to shut down. struct stat st; struct stat st; stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); return *result == INSTALL_SUCCESS; } // TODO: there should be a way to cancel waiting for a package (by pushing some button combo on // Parses and executes the command from minadbd. Returns false if we enter an invalid state so that // the device). For now you just have to 'adb sideload' a file that's not a valid package, like // the caller can kill the minadbd service properly. // "/dev/null". static bool HandleMessageFromMinadbd( waitpid(child, &status, 0); int socket_fd, const std::map<MinadbdCommands, CommandFunction>& command_map) { char buffer[kMinadbdMessageSize]; if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) { PLOG(ERROR) << "Failed to read message from minadbd"; return false; } std::string message(buffer, buffer + kMinadbdMessageSize); auto command_type = ParseMinadbdCommands(message); if (command_type == MinadbdCommands::kError) { return false; } if (command_map.find(command_type) == command_map.end()) { LOG(ERROR) << "Unsupported command: " << android::base::get_unaligned<unsigned int>( message.substr(strlen(kMinadbdCommandPrefix)).c_str()); return false; } // We have received a valid command, execute the corresponding function. const auto& command_func = command_map.at(command_type); if (!command_func()) { LOG(ERROR) << "Failed to execute command " << static_cast<unsigned int>(command_type); return WriteStatusToFd(MinadbdCommandStatus::kFailure, socket_fd); } return WriteStatusToFd(MinadbdCommandStatus::kSuccess, socket_fd); } // TODO(xunchang) add a wrapper function and kill the minadbd service there. static void ListenAndExecuteMinadbdCommands( pid_t minadbd_pid, android::base::unique_fd&& socket_fd, const std::map<MinadbdCommands, CommandFunction>& command_map) { android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC)); if (epoll_fd == -1) { PLOG(ERROR) << "Failed to create epoll"; kill(minadbd_pid, SIGKILL); return; } constexpr int EPOLL_MAX_EVENTS = 10; struct epoll_event ev = {}; ev.events = EPOLLIN | EPOLLHUP; ev.data.fd = socket_fd.get(); struct epoll_event events[EPOLL_MAX_EVENTS]; if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, socket_fd.get(), &ev) == -1) { PLOG(ERROR) << "Failed to add socket fd to epoll"; kill(minadbd_pid, SIGKILL); return; } // Set the timeout to be 300s when waiting for minadbd commands. constexpr int TIMEOUT_MILLIS = 300 * 1000; while (true) { // Poll for the status change of the socket_fd, and handle the message if the fd is ready to // read. int event_count = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, TIMEOUT_MILLIS)); if (event_count == -1) { PLOG(ERROR) << "Failed to wait for epoll events"; kill(minadbd_pid, SIGKILL); return; } if (event_count == 0) { LOG(ERROR) << "Timeout waiting for messages from minadbd"; kill(minadbd_pid, SIGKILL); return; } for (int n = 0; n < event_count; n++) { if (events[n].events & EPOLLHUP) { LOG(INFO) << "Socket has been closed"; kill(minadbd_pid, SIGKILL); return; } if (!HandleMessageFromMinadbd(socket_fd.get(), command_map)) { kill(minadbd_pid, SIGKILL); return; } } } } // Recovery starts minadbd service as a child process, and spawns another thread to listen for the // message from minadbd through a socket pair. Here is an example to execute one command from adb // host. // a. recovery b. listener thread c. minadbd service // // a1. create socket pair // a2. fork minadbd service // c3. wait for the adb commands // from host // c4. after receiving host commands: // 1) set up pre-condition (i.e. // start fuse for adb sideload) // 2) issue command through // socket. // 3) wait for result // a5. start listener thread // b6. listen for message from // minadbd in a loop. // b7. After receiving a minadbd // command from socket // 1) execute the command function // 2) send the result back to // minadbd // ...... // c8. exit upon receiving the // result // a9. wait for listener thread // to exit. // // a10. wait for minadbd to // exit // b11. exit the listening loop // static void CreateMinadbdServiceAndExecuteCommands( const std::map<MinadbdCommands, CommandFunction>& command_map) { signal(SIGPIPE, SIG_IGN); android::base::unique_fd recovery_socket; android::base::unique_fd minadbd_socket; if (!android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &recovery_socket, &minadbd_socket)) { PLOG(ERROR) << "Failed to create socket"; return; } pid_t child = fork(); if (child == -1) { PLOG(ERROR) << "Failed to fork child process"; return; } if (child == 0) { recovery_socket.reset(); execl("/system/bin/minadbd", "minadbd", "--socket_fd", std::to_string(minadbd_socket.release()).c_str(), nullptr); _exit(EXIT_FAILURE); } minadbd_socket.reset(); // We need to call SetUsbConfig() after forking minadbd service. Because the function waits for // the usb state to be updated, which depends on sys.usb.ffs.ready=1 set in the adb daemon. if (!SetUsbConfig("sideload")) { LOG(ERROR) << "Failed to set usb config to sideload"; return; } std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket), std::ref(command_map)); if (listener_thread.joinable()) { listener_thread.join(); } } int status; waitpid(child, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { if (WEXITSTATUS(status) == 3) { if (WEXITSTATUS(status) == MinadbdErrorCode::kMinadbdAdbVersionError) { ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n"); LOG(ERROR) << "\nYou need adb 1.0.32 or newer to sideload\nto this device.\n"; } else if (!WIFSIGNALED(status)) { } else if (!WIFSIGNALED(status)) { ui->Print("\n(adbd status %d)\n", WEXITSTATUS(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"); // Clean up state and stop adbd. if (usb_state != "none" && !SetUsbConfig("none")) { LOG(ERROR) << "Failed to clear USB config"; return INSTALL_ERROR; } ui->Print( "\n\nNow send the package you want to apply\n" "to the device with \"adb sideload <filename>\"...\n"); int install_result = INSTALL_ERROR; std::map<MinadbdCommands, CommandFunction> command_map{ { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, wipe_cache, ui, &install_result) }, }; CreateMinadbdServiceAndExecuteCommands(command_map); // Clean up before switching to the older state, for example setting the state // Clean up before switching to the older state, for example setting the state // to none sets sys/class/android_usb/android0/enable to 0. // to none sets sys/class/android_usb/android0/enable to 0. if (!SetUsbConfig("none")) { if (!SetUsbConfig("none")) { Loading @@ -124,5 +313,5 @@ int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { } } } } return result; return install_result; } } minadbd/Android.bp +30 −1 Original line number Original line Diff line number Diff line Loading @@ -40,7 +40,6 @@ cc_library { srcs: [ srcs: [ "fuse_adb_provider.cpp", "fuse_adb_provider.cpp", "minadbd.cpp", "minadbd_services.cpp", "minadbd_services.cpp", ], ], Loading @@ -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 { cc_test { name: "minadbd_test", name: "minadbd_test", isolated: true, isolated: true, Loading minadbd/minadbd.cpp +36 −12 Original line number Original line Diff line number Diff line Loading @@ -14,18 +14,42 @@ * limitations under the License. * limitations under the License. */ */ #include "minadbd.h" #include <errno.h> #include <errno.h> #include <fcntl.h> #include <signal.h> #include <signal.h> #include <stdio.h> #include <stdio.h> #include <stdlib.h> #include <stdlib.h> #include <strings.h> #include <android-base/logging.h> #include <android-base/parseint.h> #include "adb.h" #include "adb.h" #include "adb_auth.h" #include "adb_auth.h" #include "transport.h" #include "transport.h" int minadbd_main() { #include "minadbd_services.h" #include "minadbd_types.h" 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); } 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); adb_device_banner = "sideload"; adb_device_banner = "sideload"; signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN); Loading Loading
Android.bp +1 −1 Original line number Original line Diff line number Diff line Loading @@ -112,7 +112,6 @@ cc_binary { ], ], shared_libs: [ shared_libs: [ "libminadbd_services", "librecovery_ui", "librecovery_ui", ], ], Loading @@ -124,6 +123,7 @@ cc_binary { required: [ required: [ "e2fsdroid.recovery", "e2fsdroid.recovery", "librecovery_ui_ext", "librecovery_ui_ext", "minadbd", "mke2fs.conf.recovery", "mke2fs.conf.recovery", "mke2fs.recovery", "mke2fs.recovery", "recovery_deps", "recovery_deps", Loading
install/Android.bp +4 −0 Original line number Original line Diff line number Diff line Loading @@ -19,6 +19,10 @@ cc_defaults { "recovery_defaults", "recovery_defaults", ], ], header_libs: [ "libminadbd_headers", ], shared_libs: [ shared_libs: [ "libbase", "libbase", "libbootloader_message", "libbootloader_message", Loading
install/adb_install.cpp +237 −48 Original line number Original line Diff line number Diff line Loading @@ -21,97 +21,286 @@ #include <signal.h> #include <signal.h> #include <stdlib.h> #include <stdlib.h> #include <string.h> #include <string.h> #include <sys/epoll.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/wait.h> #include <unistd.h> #include <unistd.h> #include <atomic> #include <functional> #include <map> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/logging.h> #include <android-base/memory.h> #include <android-base/properties.h> #include <android-base/properties.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include "fuse_sideload.h" #include "fuse_sideload.h" #include "install/install.h" #include "install/install.h" #include "minadbd_types.h" #include "recovery_ui/ui.h" #include "recovery_ui/ui.h" using CommandFunction = std::function<bool()>; static bool SetUsbConfig(const std::string& state) { static bool SetUsbConfig(const std::string& state) { android::base::SetProperty("sys.usb.config", state); android::base::SetProperty("sys.usb.config", state); return android::base::WaitForProperty("sys.usb.state", state); return android::base::WaitForProperty("sys.usb.state", state); } } int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { // Parses the minadbd command in |message|; returns MinadbdCommands::kError upon errors. // Save the usb state to restore after the sideload operation. static MinadbdCommands ParseMinadbdCommands(const std::string& message) { std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) { // Clean up state and stop adbd. LOG(ERROR) << "Failed to parse command in message " << message; if (usb_state != "none" && !SetUsbConfig("none")) { return MinadbdCommands::kError; LOG(ERROR) << "Failed to clear USB config"; return INSTALL_ERROR; } } ui->Print( auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix)); "\n\nNow send the package you want to apply\n" auto cmd_code = android::base::get_unaligned<uint32_t>(cmd_code_string.c_str()); "to the device with \"adb sideload <filename>\"...\n"); if (cmd_code >= static_cast<uint32_t>(MinadbdCommands::kError)) { LOG(ERROR) << "Unsupported command code: " << cmd_code; pid_t child; return MinadbdCommands::kError; if ((child = fork()) == 0) { execl("/system/bin/recovery", "recovery", "--adbd", nullptr); _exit(EXIT_FAILURE); } } if (!SetUsbConfig("sideload")) { return static_cast<MinadbdCommands>(cmd_code); 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 bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { static constexpr int ADB_INSTALL_TIMEOUT = 300; char message[kMinadbdMessageSize]; memcpy(message, kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix)); android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), status); // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host connects and starts serving a if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) { // package. Poll for its appearance. (Note that inotify doesn't work with FUSE.) PLOG(ERROR) << "Failed to write message " << message; int result = INSTALL_ERROR; return false; int status; } bool waited = false; return true; for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { if (waitpid(child, &status, WNOHANG) != 0) { result = INSTALL_ERROR; waited = true; break; } } // 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; struct stat st; if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) { if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) { sleep(1); sleep(1); continue; continue; } else { } else { ui->Print("\nTimed out waiting for package.\n\n"); ui->Print("\nTimed out waiting for fuse to be ready.\n\n"); result = INSTALL_ERROR; kill(child, SIGKILL); break; break; } } } } result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0, ui); *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0, ui); break; break; } } if (!waited) { // Calling stat() on this magic filename signals the FUSE to exit. // Calling stat() on this magic filename signals the minadbd subprocess to shut down. struct stat st; struct stat st; stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); return *result == INSTALL_SUCCESS; } // TODO: there should be a way to cancel waiting for a package (by pushing some button combo on // Parses and executes the command from minadbd. Returns false if we enter an invalid state so that // the device). For now you just have to 'adb sideload' a file that's not a valid package, like // the caller can kill the minadbd service properly. // "/dev/null". static bool HandleMessageFromMinadbd( waitpid(child, &status, 0); int socket_fd, const std::map<MinadbdCommands, CommandFunction>& command_map) { char buffer[kMinadbdMessageSize]; if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) { PLOG(ERROR) << "Failed to read message from minadbd"; return false; } std::string message(buffer, buffer + kMinadbdMessageSize); auto command_type = ParseMinadbdCommands(message); if (command_type == MinadbdCommands::kError) { return false; } if (command_map.find(command_type) == command_map.end()) { LOG(ERROR) << "Unsupported command: " << android::base::get_unaligned<unsigned int>( message.substr(strlen(kMinadbdCommandPrefix)).c_str()); return false; } // We have received a valid command, execute the corresponding function. const auto& command_func = command_map.at(command_type); if (!command_func()) { LOG(ERROR) << "Failed to execute command " << static_cast<unsigned int>(command_type); return WriteStatusToFd(MinadbdCommandStatus::kFailure, socket_fd); } return WriteStatusToFd(MinadbdCommandStatus::kSuccess, socket_fd); } // TODO(xunchang) add a wrapper function and kill the minadbd service there. static void ListenAndExecuteMinadbdCommands( pid_t minadbd_pid, android::base::unique_fd&& socket_fd, const std::map<MinadbdCommands, CommandFunction>& command_map) { android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC)); if (epoll_fd == -1) { PLOG(ERROR) << "Failed to create epoll"; kill(minadbd_pid, SIGKILL); return; } constexpr int EPOLL_MAX_EVENTS = 10; struct epoll_event ev = {}; ev.events = EPOLLIN | EPOLLHUP; ev.data.fd = socket_fd.get(); struct epoll_event events[EPOLL_MAX_EVENTS]; if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, socket_fd.get(), &ev) == -1) { PLOG(ERROR) << "Failed to add socket fd to epoll"; kill(minadbd_pid, SIGKILL); return; } // Set the timeout to be 300s when waiting for minadbd commands. constexpr int TIMEOUT_MILLIS = 300 * 1000; while (true) { // Poll for the status change of the socket_fd, and handle the message if the fd is ready to // read. int event_count = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, TIMEOUT_MILLIS)); if (event_count == -1) { PLOG(ERROR) << "Failed to wait for epoll events"; kill(minadbd_pid, SIGKILL); return; } if (event_count == 0) { LOG(ERROR) << "Timeout waiting for messages from minadbd"; kill(minadbd_pid, SIGKILL); return; } for (int n = 0; n < event_count; n++) { if (events[n].events & EPOLLHUP) { LOG(INFO) << "Socket has been closed"; kill(minadbd_pid, SIGKILL); return; } if (!HandleMessageFromMinadbd(socket_fd.get(), command_map)) { kill(minadbd_pid, SIGKILL); return; } } } } // Recovery starts minadbd service as a child process, and spawns another thread to listen for the // message from minadbd through a socket pair. Here is an example to execute one command from adb // host. // a. recovery b. listener thread c. minadbd service // // a1. create socket pair // a2. fork minadbd service // c3. wait for the adb commands // from host // c4. after receiving host commands: // 1) set up pre-condition (i.e. // start fuse for adb sideload) // 2) issue command through // socket. // 3) wait for result // a5. start listener thread // b6. listen for message from // minadbd in a loop. // b7. After receiving a minadbd // command from socket // 1) execute the command function // 2) send the result back to // minadbd // ...... // c8. exit upon receiving the // result // a9. wait for listener thread // to exit. // // a10. wait for minadbd to // exit // b11. exit the listening loop // static void CreateMinadbdServiceAndExecuteCommands( const std::map<MinadbdCommands, CommandFunction>& command_map) { signal(SIGPIPE, SIG_IGN); android::base::unique_fd recovery_socket; android::base::unique_fd minadbd_socket; if (!android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &recovery_socket, &minadbd_socket)) { PLOG(ERROR) << "Failed to create socket"; return; } pid_t child = fork(); if (child == -1) { PLOG(ERROR) << "Failed to fork child process"; return; } if (child == 0) { recovery_socket.reset(); execl("/system/bin/minadbd", "minadbd", "--socket_fd", std::to_string(minadbd_socket.release()).c_str(), nullptr); _exit(EXIT_FAILURE); } minadbd_socket.reset(); // We need to call SetUsbConfig() after forking minadbd service. Because the function waits for // the usb state to be updated, which depends on sys.usb.ffs.ready=1 set in the adb daemon. if (!SetUsbConfig("sideload")) { LOG(ERROR) << "Failed to set usb config to sideload"; return; } std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket), std::ref(command_map)); if (listener_thread.joinable()) { listener_thread.join(); } } int status; waitpid(child, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { if (WEXITSTATUS(status) == 3) { if (WEXITSTATUS(status) == MinadbdErrorCode::kMinadbdAdbVersionError) { ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n"); LOG(ERROR) << "\nYou need adb 1.0.32 or newer to sideload\nto this device.\n"; } else if (!WIFSIGNALED(status)) { } else if (!WIFSIGNALED(status)) { ui->Print("\n(adbd status %d)\n", WEXITSTATUS(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"); // Clean up state and stop adbd. if (usb_state != "none" && !SetUsbConfig("none")) { LOG(ERROR) << "Failed to clear USB config"; return INSTALL_ERROR; } ui->Print( "\n\nNow send the package you want to apply\n" "to the device with \"adb sideload <filename>\"...\n"); int install_result = INSTALL_ERROR; std::map<MinadbdCommands, CommandFunction> command_map{ { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, wipe_cache, ui, &install_result) }, }; CreateMinadbdServiceAndExecuteCommands(command_map); // Clean up before switching to the older state, for example setting the state // Clean up before switching to the older state, for example setting the state // to none sets sys/class/android_usb/android0/enable to 0. // to none sets sys/class/android_usb/android0/enable to 0. if (!SetUsbConfig("none")) { if (!SetUsbConfig("none")) { Loading @@ -124,5 +313,5 @@ int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { } } } } return result; return install_result; } }
minadbd/Android.bp +30 −1 Original line number Original line Diff line number Diff line Loading @@ -40,7 +40,6 @@ cc_library { srcs: [ srcs: [ "fuse_adb_provider.cpp", "fuse_adb_provider.cpp", "minadbd.cpp", "minadbd_services.cpp", "minadbd_services.cpp", ], ], Loading @@ -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 { cc_test { name: "minadbd_test", name: "minadbd_test", isolated: true, isolated: true, Loading
minadbd/minadbd.cpp +36 −12 Original line number Original line Diff line number Diff line Loading @@ -14,18 +14,42 @@ * limitations under the License. * limitations under the License. */ */ #include "minadbd.h" #include <errno.h> #include <errno.h> #include <fcntl.h> #include <signal.h> #include <signal.h> #include <stdio.h> #include <stdio.h> #include <stdlib.h> #include <stdlib.h> #include <strings.h> #include <android-base/logging.h> #include <android-base/parseint.h> #include "adb.h" #include "adb.h" #include "adb_auth.h" #include "adb_auth.h" #include "transport.h" #include "transport.h" int minadbd_main() { #include "minadbd_services.h" #include "minadbd_types.h" 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); } 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); adb_device_banner = "sideload"; adb_device_banner = "sideload"; signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN); Loading