// Parses the command line argument from various sources; and reads the stage field from BCB. // command line args come from, in decreasing precedence: // - the actual command line // - the bootloader control block (one per line, after "recovery") // - the contents of COMMAND_FILE (one per line) static std::vector<std::string> get_args(constint argc, char** const argv, std::string* stage) {
CHECK_GT(argc, 0);
bootloader_message boot = {};
std::string err; if (!read_bootloader_message(&boot, &err)) {
LOG(ERROR) << err; // If fails, leave a zeroed bootloader_message.
boot = {};
} if (stage) {
*stage = std::string(boot.stage);
}
// --- if arguments weren't supplied, look in the bootloader control block if (args.size() == 1) {
boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
std::string boot_recovery(boot.recovery);
std::vector<std::string> tokens = android::base::Split(boot_recovery, "\n"); if (!tokens.empty() && tokens[0] == "recovery") { for (auto it = tokens.begin() + 1; it != tokens.end(); it++) { // Skip empty and '\0'-filled tokens. if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
}
LOG(INFO) << "Got " << args.size() << " arguments from boot message " << android::base::Join(args, ", ");
} elseif (boot.recovery[0] != 0) {
LOG(ERROR) << "Bad boot message: \"" << boot_recovery << "\"";
}
}
// --- if that doesn't work, try the command file (if we have /cache). if (args.size() == 1 && HasCache()) {
std::string content; if (ensure_path_mounted(COMMAND_FILE) == 0 &&
android::base::ReadFileToString(COMMAND_FILE, &content)) {
std::vector<std::string> tokens = android::base::Split(content, "\n"); // All the arguments in COMMAND_FILE are needed (unlike the BCB message, // COMMAND_FILE doesn't use filename as the first argument). for (auto it = tokens.begin(); it != tokens.end(); it++) { // Skip empty and '\0'-filled tokens. if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
}
LOG(INFO) << "Got " << args.size() << " arguments from " << COMMAND_FILE;
}
}
// 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 FinishRecovery() is called. // This should only be done for boot-recovery command so that other commands // won't be overwritten. if (boot_command == "boot-recovery") {
std::vector<std::string> options(args.cbegin() + 1, args.cend()); if (!update_bootloader_message(options, &err)) {
LOG(ERROR) << "Failed to set BCB message: " << err;
}
}
// Finally, if no arguments were specified, check whether we should boot // into fastboot or rescue mode. if (args.size() == 1 && boot_command == "boot-fastboot") {
args.emplace_back("--fastboot");
} elseif (args.size() == 1 && boot_command == "boot-rescue") {
args.emplace_back("--rescue");
}
return args;
}
static std::string load_locale_from_cache() { if (ensure_path_mounted(LOCALE_FILE) != 0) {
LOG(ERROR) << "Can't mount " << LOCALE_FILE; return"";
}
// Sets the usb config to 'state'. staticbool SetUsbConfig(const std::string& state) {
android::base::SetProperty("sys.usb.config", state); return android::base::WaitForProperty("sys.usb.state", state);
}
staticvoid ListenRecoverySocket(RecoveryUI* ui, std::atomic<Device::BuiltinAction>& action) {
android::base::unique_fd sock_fd(android_get_control_socket("recovery")); if (sock_fd < 0) {
PLOG(ERROR) << "Failed to open recovery socket"; return;
}
listen(sock_fd, 4);
while (true) {
android::base::unique_fd connection_fd;
connection_fd.reset(accept(sock_fd, nullptr, nullptr)); if (connection_fd < 0) {
PLOG(ERROR) << "Failed to accept socket connection"; continue;
} char msg;
constexpr char kSwitchToFastboot = 'f';
constexpr char kSwitchToRecovery = 'r';
constexpr char kSwitchToSideload = 's';
ssize_t ret = TEMP_FAILURE_RETRY(read(connection_fd, &msg, sizeof(msg))); if (ret != sizeof(msg)) {
PLOG(ERROR) << "Couldn't read from socket"; continue;
} switch (msg) { case kSwitchToRecovery:
action = Device::BuiltinAction::ENTER_RECOVERY; break; case kSwitchToFastboot:
action = Device::BuiltinAction::ENTER_FASTBOOT; break; case kSwitchToSideload:
action = Device::BuiltinAction::APPLY_ADB_SIDELOAD; break; default:
LOG(ERROR) << "Unrecognized char from socket " << msg; continue;
}
ui->InterruptKey();
}
}
staticvoid redirect_stdio(constchar* filename) {
android::base::unique_fd pipe_read, pipe_write; // Create a pipe that allows parent process sending logs over. if (!android::base::Pipe(&pipe_read, &pipe_write)) {
PLOG(ERROR) << "Failed to create pipe for redirecting stdio";
// Fall back to traditional logging mode without timestamps. If these fail, there's not really // anywhere to complain...
freopen(filename, "a", stdout);
setbuf(stdout, nullptr);
freopen(filename, "a", stderr);
setbuf(stderr, nullptr);
return;
}
pid_t pid = fork(); if (pid == -1) {
PLOG(ERROR) << "Failed to fork for redirecting stdio";
// Fall back to traditional logging mode without timestamps. If these fail, there's not really // anywhere to complain...
freopen(filename, "a", stdout);
setbuf(stdout, nullptr);
freopen(filename, "a", stderr);
setbuf(stderr, nullptr);
return;
}
if (pid == 0) { // Child process reads the incoming logs and doesn't write to the pipe.
pipe_write.reset();
auto start = std::chrono::steady_clock::now();
// Child logger to actually write to the log file.
FILE* log_fp = fopen(filename, "ae"); if (log_fp == nullptr) {
PLOG(ERROR) << "fopen \"" << filename << "\" failed";
_exit(EXIT_FAILURE);
}
int main(int argc, char** argv) { // We don't have logcat yet under recovery; so we'll print error on screen and log to stdout // (which is redirected to recovery.log) as we used to do.
android::base::InitLogging(argv, &UiLogger);
// Take last pmsg contents and rewrite it to the current pmsg session. static constexpr constchar filter[] = "recovery/"; // Do we need to rotate? bool do_rotate = false;
__android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logbasename, &do_rotate); // Take action to refresh pmsg contents
__android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &do_rotate);
time_t start = time(nullptr);
// redirect_stdio should be called only in non-sideload mode. Otherwise we may have two logger // instances with different timestamps.
redirect_stdio(Paths::Get().temporary_log_file().c_str());
// 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;
if (locale.empty()) { if (HasCache()) {
locale = load_locale_from_cache();
}
if (locale.empty()) {
locale = DEFAULT_LOCALE;
}
}
static constexpr constchar* kDefaultLibRecoveryUIExt = "librecovery_ui_ext.so"; // Intentionally not calling dlclose(3) to avoid potential gotchas (e.g. `make_device` may have // handed out pointers to code or static [or thread-local] data and doesn't collect them all back // in on dlclose). void* librecovery_ui_ext = dlopen(kDefaultLibRecoveryUIExt, RTLD_NOW);
using MakeDeviceType = decltype(&make_device);
MakeDeviceType make_device_func = nullptr; if (librecovery_ui_ext == nullptr) {
printf("Failed to dlopen %s: %s\n", kDefaultLibRecoveryUIExt, dlerror());
} else { reinterpret_cast<void*&>(make_device_func) = dlsym(librecovery_ui_ext, "make_device"); if (make_device_func == nullptr) {
printf("Failed to dlsym make_device: %s\n", dlerror());
}
}
Device* device; if (make_device_func == nullptr) {
printf("Falling back to the default make_device() instead\n");
device = make_device();
} else {
printf("Loading make_device from %s\n", kDefaultLibRecoveryUIExt);
device = (*make_device_func)();
}
if (android::base::GetBoolProperty("ro.boot.quiescent", false)) {
printf("Quiescent recovery mode.\n");
device->ResetUI(new StubRecoveryUI());
} else { if (!device->GetUI()->Init(locale)) {
printf("Failed to initialize UI; using stub UI instead.\n");
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()) {
device->RemoveMenuItemForAction(Device::WIPE_CACHE);
}
if (!android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
device->RemoveMenuItemForAction(Device::ENTER_FASTBOOT);
}
if (!IsRoDebuggable()) {
device->RemoveMenuItemForAction(Device::ENTER_RESCUE);
}
ui->SetBackground(RecoveryUI::NONE); if (show_text) ui->ShowText(true);
auto sehandle = selinux_android_file_context_handle();
selinux_android_set_sehandle(sehandle); if (!sehandle) {
ui->Print("Warning: No file_contexts\n");
}
Device::BuiltinAction next_recovery_action = Device::NO_ACTION; while (true) { // 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 (fastboot) {
device->PreFastboot();
} else {
device->PreRecovery();
} if (usb_config != usb_state) { if (!SetUsbConfig("none")) {
LOG(ERROR) << "Failed to clear USB config";
} if (!SetUsbConfig(usb_config)) {
LOG(ERROR) << "Failed to set USB config to " << usb_config;
}
}
Device::BuiltinAction ret; if (fastboot) {
ret = StartFastboot(device, args);
} else { // If we received a sideload command via the recovery socket, then // force recovery into sideload mode. if (next_recovery_action == Device::APPLY_ADB_SIDELOAD) {
ret = start_recovery(device, { args[0], "--sideload" });
} else {
ret = start_recovery(device, args);
}
}
// Reset the next recovery action.
next_recovery_action = Device::NO_ACTION;
if (ret == Device::KEY_INTERRUPTED) {
ret = action.exchange(ret); if (ret == Device::NO_ACTION) { continue;
}
} switch (ret) { case Device::SHUTDOWN:
ui->Print("Shutting down...\n");
Shutdown("userrequested,recovery"); break;
case Device::SHUTDOWN_FROM_FASTBOOT:
ui->Print("Shutting down...\n");
Shutdown("userrequested,fastboot"); break;
case Device::REBOOT_BOOTLOADER:
ui->Print("Rebooting to bootloader...\n");
Reboot("bootloader"); break;
case Device::REBOOT_FASTBOOT:
ui->Print("Rebooting to recovery/fastboot...\n");
Reboot("fastboot"); break;
case Device::REBOOT_RECOVERY:
ui->Print("Rebooting to recovery...\n");
Reboot("recovery"); break;
case Device::REBOOT_RESCUE: { // 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));
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("recovery"); break;
}
case Device::ENTER_FASTBOOT: if (android::fs_mgr::LogicalPartitionsMapped()) {
ui->Print("Partitions may be mounted - rebooting to enter fastboot.");
Reboot("fastboot");
} else {
LOG(INFO) << "Entering fastboot";
fastboot = true;
} break;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.