diff --git a/Android.bp b/Android.bp index 630c7965..5677b6c2 100644 --- a/Android.bp +++ b/Android.bp @@ -96,6 +96,29 @@ cc_library_static { ], } +cc_library_static { + name: "librecovery_fastboot", + recovery_available: true, + defaults: [ + "recovery_defaults", + ], + + srcs: [ + "fastboot/fastboot.cpp", + ], + + shared_libs: [ + "libbase", + "libbootloader_message", + "libcutils", + "liblog", + ], + + static_libs: [ + "librecovery_ui_default", + ], +} + cc_defaults { name: "librecovery_defaults", @@ -124,6 +147,7 @@ cc_defaults { ], static_libs: [ + "librecovery_fastboot", "libminui", "libverifier", "libotautil", diff --git a/device.cpp b/device.cpp index 3c6334e5..eec1932c 100644 --- a/device.cpp +++ b/device.cpp @@ -28,6 +28,7 @@ static std::vector> g_menu_actions{ { "Reboot system now", Device::REBOOT }, { "Reboot to bootloader", Device::REBOOT_BOOTLOADER }, + { "Enter fastboot", Device::ENTER_FASTBOOT }, { "Apply update from ADB", Device::APPLY_ADB_SIDELOAD }, { "Apply update from SD card", Device::APPLY_SDCARD }, { "Wipe data/factory reset", Device::WIPE_DATA }, diff --git a/device.h b/device.h index a6ad6278..6a8daf83 100644 --- a/device.h +++ b/device.h @@ -48,6 +48,8 @@ class Device { RUN_GRAPHICS_TEST = 11, RUN_LOCALE_TEST = 12, KEY_INTERRUPTED = 13, + ENTER_FASTBOOT = 14, + ENTER_RECOVERY = 15, }; explicit Device(RecoveryUI* ui); diff --git a/etc/init.rc b/etc/init.rc index 3821eb6a..9add2494 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -6,6 +6,8 @@ on early-init start ueventd + setprop sys.usb.configfs 0 + on init export ANDROID_ROOT /system export ANDROID_DATA /data @@ -31,20 +33,6 @@ on init write /proc/sys/kernel/panic_on_oops 1 write /proc/sys/vm/max_map_count 1000000 -on fs - write /sys/class/android_usb/android0/f_ffs/aliases adb - mkdir /dev/usb-ffs 0770 shell shell - mkdir /dev/usb-ffs/adb 0770 shell shell - mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000 - - write /sys/class/android_usb/android0/enable 0 - write /sys/class/android_usb/android0/idVendor 18D1 - write /sys/class/android_usb/android0/idProduct D001 - write /sys/class/android_usb/android0/functions adb - write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer} - write /sys/class/android_usb/android0/iProduct ${ro.product.model} - write /sys/class/android_usb/android0/iSerial ${ro.serialno} - on boot ifup lo hostname localhost @@ -86,6 +74,7 @@ service charger /charger -r seclabel u:r:charger:s0 service recovery /system/bin/recovery + socket recovery stream 422 system system seclabel u:r:recovery:s0 service adbd /system/bin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery @@ -93,13 +82,89 @@ service adbd /system/bin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery socket adbd stream 660 system system seclabel u:r:adbd:s0 -# Always start adbd on userdebug and eng builds -on property:ro.debuggable=1 - write /sys/class/android_usb/android0/enable 1 - start adbd +service fastbootd /system/bin/fastbootd + disabled + group system + seclabel u:r:fastbootd:s0 # Restart adbd so it can run as root on property:service.adb.root=1 - write /sys/class/android_usb/android0/enable 0 restart adbd + +# Always start adbd on userdebug and eng builds +on fs && property:ro.debuggable=1 + setprop sys.usb.config adb + +on fs && property:sys.usb.configfs=1 + mount configfs none /config + mkdir /config/usb_gadget/g1 0770 shell shell + write /config/usb_gadget/g1/idVendor 0x18D1 + mkdir /config/usb_gadget/g1/strings/0x409 0770 + write /config/usb_gadget/g1/strings/0x409/serialnumber ${ro.serialno} + write /config/usb_gadget/g1/strings/0x409/manufacturer ${ro.product.manufacturer} + write /config/usb_gadget/g1/strings/0x409/product ${ro.product.model} + mkdir /config/usb_gadget/g1/functions/ffs.adb + mkdir /config/usb_gadget/g1/functions/ffs.fastboot + mkdir /config/usb_gadget/g1/configs/b.1 0777 shell shell + mkdir /config/usb_gadget/g1/configs/b.1/strings/0x409 0770 shell shell + +on fs && property:sys.usb.configfs=0 + write /sys/class/android_usb/android0/f_ffs/aliases adb,fastboot + write /sys/class/android_usb/android0/idVendor 18D1 + write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer} + write /sys/class/android_usb/android0/iProduct ${ro.product.model} + write /sys/class/android_usb/android0/iSerial ${ro.serialno} + +on fs + mkdir /dev/usb-ffs 0775 shell shell + mkdir /dev/usb-ffs/adb 0770 shell shell + mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000 + mkdir /dev/usb-ffs/fastboot 0770 system system + mount functionfs fastboot /dev/usb-ffs/fastboot rmode=0770,fmode=0660,uid=1000,gid=1000 + +on property:sys.usb.config=adb + start adbd + +on property:sys.usb.config=fastboot + start fastbootd + +on property:sys.usb.config=none + stop adbd + stop fastbootd + +on property:sys.usb.config=none && property:sys.usb.configfs=0 + write /sys/class/android_usb/android0/enable 0 + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=adb && property:sys.usb.configfs=0 + write /sys/class/android_usb/android0/idProduct D001 + write /sys/class/android_usb/android0/functions adb write /sys/class/android_usb/android0/enable 1 + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=fastboot && property:sys.usb.configfs=0 + write /sys/class/android_usb/android0/idProduct 4EE0 + write /sys/class/android_usb/android0/functions fastboot + write /sys/class/android_usb/android0/enable 1 + setprop sys.usb.state ${sys.usb.config} + +# Configfs triggers +on property:sys.usb.config=none && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/UDC "none" + setprop sys.usb.ffs.ready 0 + rm /config/usb_gadget/g1/configs/b.1/f1 + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=adb && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/idProduct 0xD001 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb" + symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=fastboot && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/idProduct 0x4EE0 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "fastboot" + symlink /config/usb_gadget/g1/functions/ffs.fastboot /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp new file mode 100644 index 00000000..8458c99d --- /dev/null +++ b/fastboot/fastboot.cpp @@ -0,0 +1,82 @@ +/* + * 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 "fastboot.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "device.h" +#include "ui.h" + +static const std::vector> kFastbootMenuActions{ + { "Reboot system now", Device::REBOOT }, + { "Enter recovery", Device::ENTER_RECOVERY }, + { "Reboot to bootloader", Device::REBOOT_BOOTLOADER }, + { "Power off", Device::SHUTDOWN }, +}; + +Device::BuiltinAction StartFastboot(Device* device, const std::vector& /* args */) { + RecoveryUI* ui = device->GetUI(); + + std::vector title_lines = { "Android Fastboot" }; + title_lines.push_back("Product name - " + android::base::GetProperty("ro.product.device", "")); + title_lines.push_back("Bootloader version - " + android::base::GetProperty("ro.bootloader", "")); + title_lines.push_back("Baseband version - " + + android::base::GetProperty("ro.build.expect.baseband", "")); + title_lines.push_back("Serial number - " + android::base::GetProperty("ro.serialno", "")); + title_lines.push_back(std::string("Secure boot - ") + + ((android::base::GetProperty("ro.secure", "") == "1") ? "yes" : "no")); + title_lines.push_back("HW version - " + android::base::GetProperty("ro.revision", "")); + + ui->ResetKeyInterruptStatus(); + ui->SetTitle(title_lines); + ui->ShowText(true); + + // 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, + // set the 'command' field of BCB to 'boot-recovery' so the next boot is into recovery + // to finish any interrupted tasks. + std::string err; + if (!clear_bootloader_message(&err)) { + LOG(ERROR) << "Failed to clear BCB message: " << err; + } + + std::vector fastboot_menu_items; + std::transform(kFastbootMenuActions.cbegin(), kFastbootMenuActions.cend(), + std::back_inserter(fastboot_menu_items), + [](const auto& entry) { return entry.first; }); + + auto chosen_item = ui->ShowMenu( + {}, fastboot_menu_items, 0, false, + std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); + + if (chosen_item == static_cast(RecoveryUI::KeyError::INTERRUPTED)) { + return Device::KEY_INTERRUPTED; + } + if (chosen_item == static_cast(RecoveryUI::KeyError::TIMED_OUT)) { + return Device::BuiltinAction::NO_ACTION; + } + return kFastbootMenuActions[chosen_item].second; +} diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h new file mode 100644 index 00000000..53a2adca --- /dev/null +++ b/fastboot/fastboot.h @@ -0,0 +1,24 @@ +/* + * 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. + */ + +#pragma once + +#include +#include + +#include "device.h" + +Device::BuiltinAction StartFastboot(Device* device, const std::vector& args); diff --git a/recovery.cpp b/recovery.cpp index cc30035d..24f105da 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -779,6 +779,8 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { case Device::REBOOT: case Device::SHUTDOWN: case Device::REBOOT_BOOTLOADER: + case Device::ENTER_FASTBOOT: + case Device::ENTER_RECOVERY: return chosen_action; case Device::WIPE_DATA: @@ -1006,6 +1008,7 @@ 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 }, { "just_exit", no_argument, nullptr, 'x' }, { "locale", required_argument, nullptr, 0 }, @@ -1060,7 +1063,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector #include +#include #include +#include #include #include #include #include #include +#include #include #include +#include #include /* private pmsg functions */ #include #include @@ -46,6 +50,7 @@ #include "common.h" #include "device.h" +#include "fastboot/fastboot.h" #include "logging.h" #include "minadbd/minadbd.h" #include "otautil/paths.h" @@ -162,6 +167,44 @@ static std::string load_locale_from_cache() { return android::base::Trim(content); } +static void ListenRecoverySocket(RecoveryUI* ui, std::atomic& 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'; + 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; + default: + LOG(ERROR) << "Unrecognized char from socket " << msg; + continue; + } + ui->InterruptKey(); + } +} + static void redirect_stdio(const char* filename) { int pipefd[2]; if (pipe(pipefd) == -1) { @@ -251,6 +294,11 @@ static void redirect_stdio(const char* filename) { } } +static bool SetUsbConfig(const std::string& state) { + android::base::SetProperty("sys.usb.config", state); + return android::base::WaitForProperty("sys.usb.state", state); +} + 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. @@ -281,8 +329,6 @@ int main(int argc, char** argv) { // instances with different timestamps. redirect_stdio(Paths::Get().temporary_log_file().c_str()); - printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); - load_volume_table(); has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr; @@ -290,12 +336,14 @@ int main(int argc, char** argv) { auto args_to_parse = StringVectorToNullTerminatedArray(args); static constexpr struct option OPTIONS[] = { + { "fastboot", no_argument, nullptr, 0 }, { "locale", required_argument, nullptr, 0 }, { "show_text", no_argument, nullptr, 't' }, { nullptr, 0, nullptr, 0 }, }; bool show_text = false; + bool fastboot = false; std::string locale; int arg; @@ -310,6 +358,8 @@ int main(int argc, char** argv) { std::string option = OPTIONS[option_index].name; if (option == "locale") { locale = optarg; + } else if (option == "fastboot") { + fastboot = true; } break; } @@ -328,8 +378,6 @@ int main(int argc, char** argv) { } } - printf("locale is [%s]\n", locale.c_str()); - static constexpr const char* 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 @@ -374,33 +422,67 @@ int main(int argc, char** argv) { ui->SetBackground(RecoveryUI::NONE); if (show_text) ui->ShowText(true); + LOG(INFO) << "Starting recovery (pid " << getpid() << ") on " << ctime(&start); + LOG(INFO) << "locale is [" << locale << "]"; + sehandle = selinux_android_file_context_handle(); selinux_android_set_sehandle(sehandle); if (!sehandle) { ui->Print("Warning: No file_contexts\n"); } - Device::BuiltinAction after = start_recovery(device, args); + std::atomic action; + std::thread listener_thread(ListenRecoverySocket, ui, std::ref(action)); + listener_thread.detach(); - switch (after) { - case Device::SHUTDOWN: - ui->Print("Shutting down...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,"); - break; - - case Device::REBOOT_BOOTLOADER: - ui->Print("Rebooting to bootloader...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); - break; - - default: - ui->Print("Rebooting...\n"); - reboot("reboot,"); - break; - } while (true) { - pause(); + std::string usb_config = fastboot ? "fastboot" : is_ro_debuggable() ? "adb" : "none"; + std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); + 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; + } + } + + auto ret = fastboot ? StartFastboot(device, args) : start_recovery(device, args); + + 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"); + android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,"); + break; + + case Device::REBOOT_BOOTLOADER: + ui->Print("Rebooting to bootloader...\n"); + android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); + break; + + case Device::ENTER_FASTBOOT: + LOG(INFO) << "Entering fastboot"; + fastboot = true; + break; + + case Device::ENTER_RECOVERY: + LOG(INFO) << "Entering recovery"; + fastboot = false; + break; + + default: + ui->Print("Rebooting...\n"); + reboot("reboot,"); + break; + } } + // Should be unreachable. return EXIT_SUCCESS; }