From 20c81b308d834f84f3decf795575dc7a0baeb69d Mon Sep 17 00:00:00 2001 From: Hridya Valsaraju Date: Fri, 27 Jul 2018 22:09:12 -0700 Subject: Add fastboot mode to recovery Add a fastboot mode to recovery that can be entered with command line args or with the ui. Add usb property triggers to switch between fastboot and adb configurations. Allow switching between fastboot and adb through usb commands by opening a unix socket. adbd/fastbootd writes to this socket, which interrupts the ui and switches to the new mode. Test: Use fastboot mode Bug: 78793464 Change-Id: I7891bb84427ec734a21a872036629b95ab3fb13c --- Android.bp | 24 ++++++++++ device.cpp | 1 + device.h | 2 + etc/init.rc | 103 +++++++++++++++++++++++++++++++++-------- fastboot/fastboot.cpp | 82 +++++++++++++++++++++++++++++++++ fastboot/fastboot.h | 24 ++++++++++ recovery.cpp | 5 +- recovery_main.cpp | 124 +++++++++++++++++++++++++++++++++++++++++--------- 8 files changed, 324 insertions(+), 41 deletions(-) create mode 100644 fastboot/fastboot.cpp create mode 100644 fastboot/fastboot.h diff --git a/Android.bp b/Android.bp index 630c7965e..5677b6c2a 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 3c6334e5c..eec1932c2 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 a6ad62788..6a8daf83e 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 3821eb6a8..9add2494a 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 000000000..8458c99dd --- /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 000000000..53a2adcae --- /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 cc30035df..24f105da0 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(); + + while (true) { + 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; + } + } - switch (after) { - case Device::SHUTDOWN: - ui->Print("Shutting down...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,"); - break; + auto ret = fastboot ? StartFastboot(device, args) : start_recovery(device, args); - case Device::REBOOT_BOOTLOADER: - ui->Print("Rebooting to bootloader...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); - break; + 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; - default: - ui->Print("Rebooting...\n"); - reboot("reboot,"); - break; - } - while (true) { - pause(); + 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; } -- cgit v1.2.3