Merge "Add fastboot mode to recovery"
am: 19a5316412
Change-Id: I115017c90c8ee4a7b28a2742462d157585b1edaa
diff --git a/Android.bp b/Android.bp
index 1e7f543..e97f71d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -96,6 +96,29 @@
],
}
+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",
@@ -125,6 +148,7 @@
],
static_libs: [
+ "librecovery_fastboot",
"libminui",
"libverifier",
"libotautil",
diff --git a/device.cpp b/device.cpp
index 3c6334e..eec1932 100644
--- a/device.cpp
+++ b/device.cpp
@@ -28,6 +28,7 @@
static std::vector<std::pair<std::string, Device::BuiltinAction>> 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 a6ad627..6a8daf8 100644
--- a/device.h
+++ b/device.h
@@ -48,6 +48,8 @@
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 3821eb6..9add249 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -6,6 +6,8 @@
start ueventd
+ setprop sys.usb.configfs 0
+
on init
export ANDROID_ROOT /system
export ANDROID_DATA /data
@@ -31,20 +33,6 @@
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 @@
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 @@
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 0000000..8458c99
--- /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 <stdio.h>
+#include <stdlib.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <bootloader_message/bootloader_message.h>
+
+#include "device.h"
+#include "ui.h"
+
+static const std::vector<std::pair<std::string, Device::BuiltinAction>> 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<std::string>& /* args */) {
+ RecoveryUI* ui = device->GetUI();
+
+ std::vector<std::string> 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<std::string> 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<size_t>(RecoveryUI::KeyError::INTERRUPTED)) {
+ return Device::KEY_INTERRUPTED;
+ }
+ if (chosen_item == static_cast<size_t>(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 0000000..53a2adc
--- /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 <string>
+#include <vector>
+
+#include "device.h"
+
+Device::BuiltinAction StartFastboot(Device* device, const std::vector<std::string>& args);
diff --git a/recovery.cpp b/recovery.cpp
index 1299095..01bd83b 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -779,6 +779,8 @@
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:
@@ -995,6 +997,7 @@
Device::BuiltinAction start_recovery(Device* device, const std::vector<std::string>& 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 },
@@ -1049,7 +1052,7 @@
std::string option = OPTIONS[option_index].name;
if (option == "fsck_unshare_blocks") {
fsck_unshare_blocks = true;
- } else if (option == "locale") {
+ } else if (option == "locale" || option == "fastboot") {
// Handled in recovery_main.cpp
} else if (option == "prompt_and_wipe_data") {
should_prompt_and_wipe_data = true;
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 9a9890d..99f9650 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -30,15 +30,19 @@
#include <time.h>
#include <unistd.h>
+#include <atomic>
#include <string>
+#include <thread>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
+#include <cutils/sockets.h>
#include <private/android_logger.h> /* private pmsg functions */
#include <selinux/android.h>
#include <selinux/label.h>
@@ -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 @@
return android::base::Trim(content);
}
+static void 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';
+ 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 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 @@
// 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 @@
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 @@
std::string option = OPTIONS[option_index].name;
if (option == "locale") {
locale = optarg;
+ } else if (option == "fastboot") {
+ fastboot = true;
}
break;
}
@@ -328,8 +378,6 @@
}
}
- 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 @@
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<Device::BuiltinAction> 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;
}