Android系統(tǒng)底層Reboot流程源碼解讀
Framework 中 Reboot 流程
Reboot 在 Android 系統(tǒng)中主要通過物理按鍵或UI菜單進行觸發(fā),最終由 PowerManager 執(zhí)行 Reboot 流程。下圖描述了 Reboot 執(zhí)行時,F(xiàn)ramework 中相關(guān)線程的狀態(tài),最終將 Reboot 相關(guān)信息設(shè)置到屬性 sys.powerctl 中。Framework 中的具體流程本文不再描述。

Init 中 Reboot 流程
Android framework 處理完 Reboot 流程后,更新了屬性 sys.powerctl。Init 正是依靠該屬性來執(zhí)行底層 Reboot 動作。Init 對 Reboot 的處理主要為以下幾個方面:
1,進程監(jiān)控屬性 sys.powerctl 的改變
/system/core/init/init.cpp
void PropertyChanged(const std::string& name, const std::string& value) {
// If the property is sys.powerctl, we bypass the event queue and immediately handle it.
// This is to ensure that init will always and immediately shutdown/reboot, regardless of
// if there are other pending events to process or if init is waiting on an exec service or
// waiting on a property.
// In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
// commands to be executed.
// sys.powerctl 做為特殊屬性來處理,直接觸發(fā) shutdown/reboot 流程。
if (name == "sys.powerctl") {
trigger_shutdown(value);
}
if (property_triggers_enabled) {
ActionManager::GetInstance().QueuePropertyChange(name, value);
WakeMainInitThread();
}
prop_waiter_state.CheckAndResetWait(name, value);
}2,HandlePowerctlMessage()對屬性 sys.powerctl 進行解析
真正 shutdown/reboot 的流程在 HandlePowerctlMessage(),對屬性 sys.powerctl 進行解析,并存儲相關(guān)信息。
/system/core/init/reboot.cpp
void HandlePowerctlMessage(const std::string& command) {
unsigned int cmd = 0;
std::vector<std::string> cmd_params = Split(command, ",");
std::string reboot_target = "";
bool run_fsck = false;
bool command_invalid = false;
bool userspace_reboot = false;
// 解析 shutdown 參數(shù)
if (cmd_params[0] == "shutdown") {
cmd = ANDROID_RB_POWEROFF;
if (cmd_params.size() >= 2) {
if (cmd_params[1] == "userrequested") { // shutdown,userrequested
// The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
// Run fsck once the file system is remounted in read-only mode.
run_fsck = true;
} else if (cmd_params[1] == "thermal") { // shutdown,thermal
// Turn off sources of heat immediately.
TurnOffBacklight();
// run_fsck is false to avoid delay
cmd = ANDROID_RB_THERMOFF;
}
}
// 解析 reboot 參數(shù)
} else if (cmd_params[0] == "reboot") {
cmd = ANDROID_RB_RESTART2;
if (cmd_params.size() >= 2) {
reboot_target = cmd_params[1];
if (reboot_target == "userspace") { // reboot,userspace
LOG(INFO) << "Userspace reboot requested";
userspace_reboot = true;
}
// adb reboot fastboot should boot into bootloader for devices not
// supporting logical partitions.
if (reboot_target == "fastboot" &&
!android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
reboot_target = "bootloader"; // 在非動態(tài)分區(qū)的系統(tǒng)上,reboot后進入bootloader
}
// When rebooting to the bootloader notify the bootloader writing
// also the BCB.
if (reboot_target == "bootloader") { // reboot,bootloader
std::string err;
if (!write_reboot_bootloader(&err)) { // 更新BCB
LOG(ERROR) << "reboot-bootloader: Error writing "
"bootloader_message: "
<< err;
}
} else if (reboot_target == "recovery") { // reboot,recovery
bootloader_message boot = {};
if (std::string err; !read_bootloader_message(&boot, &err)) {
LOG(ERROR) << "Failed to read bootloader message: " << err;
}
// Update the boot command field if it's empty, and preserve
// the other arguments in the bootloader message.
if (!CommandIsPresent(&boot)) { // 更新BCB
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
if (std::string err; !write_bootloader_message(boot, &err)) {
LOG(ERROR) << "Failed to set bootloader message: " << err;
return;
}
}
} else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
reboot_target == "fastboot") { // reboot,fastboot
std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
: reboot_target;
const std::vector<std::string> options = {
"--" + arg,
};
std::string err;
if (!write_bootloader_message(options, &err)) { // 更新BCB
LOG(ERROR) << "Failed to set bootloader message: " << err;
return;
}
reboot_target = "recovery"; // reboot后進入recovery
}
// If there are additional parameter, pass them along
for (size_t i = 2; (cmd_params.size() > i) && cmd_params[i].size(); ++i) {
reboot_target += "," + cmd_params[i];
}
}
} else {
command_invalid = true;
}
if (command_invalid) {
LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
return;
}
// We do not want to process any messages (queue'ing triggers, shutdown messages, control
// messages, etc) from properties during reboot.
StopSendingMessages(); // 停止所有的屬性處理
if (userspace_reboot) { // reboot,userspace 執(zhí)行用戶空間重啟,并不重啟整個系統(tǒng)
HandleUserspaceReboot();
return;
}
LOG(INFO) << "Clear action queue and start shutdown trigger";
ActionManager::GetInstance().ClearQueue(); // 清空init action隊列
// Queue shutdown trigger first
ActionManager::GetInstance().QueueEventTrigger("shutdown"); // 執(zhí)行init中的shutdown action
// Queue built-in shutdown_done
auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
DoReboot(cmd, command, reboot_target, run_fsck); // 執(zhí)行 shutdown/reboot 動作
return Result<void>{};
};
ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
EnterShutdown(); // 清理相關(guān)資源
}3,DoReboot() 執(zhí)行 shutdown/reboot 動作
/system/core/init/reboot.cpp
static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& reboot_target,
bool run_fsck) {
Timer t;
LOG(INFO) << "Reboot start, reason: " << reason << ", reboot_target: " << reboot_target;
bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
// 配置shutdown timeout時間,缺省是6秒
auto shutdown_timeout = 0ms;
if (!SHUTDOWN_ZERO_TIMEOUT) {
constexpr unsigned int shutdown_timeout_default = 6;
constexpr unsigned int max_thermal_shutdown_timeout = 3;
auto shutdown_timeout_final = android::base::GetUintProperty("ro.build.shutdown_timeout",
shutdown_timeout_default);
if (is_thermal_shutdown && shutdown_timeout_final > max_thermal_shutdown_timeout) {
shutdown_timeout_final = max_thermal_shutdown_timeout;
}
shutdown_timeout = std::chrono::seconds(shutdown_timeout_final);
}
......
// Start a thread to monitor init shutdown process
// 啟動一個reboot監(jiān)控線程
LOG(INFO) << "Create reboot monitor thread.";
bool reboot_monitor_run = true;
std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, reboot_target, &reboot_semaphore,
shutdown_timeout, &reboot_monitor_run);
reboot_monitor_thread.detach();
......
// 保存reboot原因到屬性中
std::vector<std::string> reasons = Split(reason, ",");
if (reasons.size() >= 2 && reasons[0] == "reboot" &&
(reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
reasons[1] == "hard" || reasons[1] == "warm")) {
skip = strlen("reboot,");
}
PersistRebootReason(reason.c_str() + skip, true);
......
// 安全關(guān)閉watchdogd
const std::set<std::string> to_starts{"watchdogd"};
std::set<std::string> stop_first;
for (const auto& s : ServiceList::GetInstance()) {
......
}
// remaining operations (specifically fsck) may take a substantial duration
if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
TurnOffBacklight(); // 先關(guān)背光
}
// 顯示shutdown animation
Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) {
......
}
// optional shutdown step
// 1. terminate all services except shutdown critical ones. wait for delay to finish
if (shutdown_timeout > 0ms) { // 使用SIGTERM終止所有非關(guān)鍵服務(wù)
StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true /* SIGTERM */);
}
// Send SIGKILL to ones that didn't terminate cleanly.
StopServicesAndLogViolations(stop_first, 0ms, false /* SIGKILL */); // 使用SIGKILL終止所有非關(guān)鍵服務(wù)
SubcontextTerminate();
// Reap subcontext pids.
ReapAnyOutstandingChildren();
// 3. send volume abort_fuse and volume shutdown to vold
Service* vold_service = ServiceList::GetInstance().FindService("vold");
if (vold_service != nullptr && vold_service->IsRunning()) {
// Manually abort FUSE connections, since the FUSE daemon is already dead
// at this point, and unmounting it might hang.
CallVdc("volume", "abort_fuse");
CallVdc("volume", "shutdown");
vold_service->Stop(); // 關(guān)閉vold服務(wù)
} else {
LOG(INFO) << "vold not running, skipping vold shutdown";
}
// logcat stopped here
StopServices(kDebuggingServices, 0ms, false /* SIGKILL */);
// 4. sync, try umount, and optionally run fsck for user shutdown
{
Timer sync_timer;
LOG(INFO) << "sync() before umount...";
sync(); // 同步文件系統(tǒng)
LOG(INFO) << "sync() before umount took" << sync_timer;
}
// 5. drop caches and disable zram backing device, if exist
KillZramBackingDevice(); // kill ZRAM服務(wù)
LOG(INFO) << "Ready to unmount apexes. So far shutdown sequence took " << t;
// 6. unmount active apexes, otherwise they might prevent clean unmount of /data.
if (auto ret = UnmountAllApexes(); !ret.ok()) {
LOG(ERROR) << ret.error();
}
UmountStat stat = // unmount
TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
// Follow what linux shutdown is doing: one more sync with little bit delay
{
Timer sync_timer;
LOG(INFO) << "sync() after umount...";
sync(); // 再次同步文件系統(tǒng)
LOG(INFO) << "sync() after umount took" << sync_timer;
}
if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
LogShutdownTime(stat, &t);
// Send signal to terminate reboot monitor thread.
reboot_monitor_run = false;
sem_post(&reboot_semaphore);
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, reboot_target); // 執(zhí)行系統(tǒng)reboot
abort();
}4,通過RebootSystem() 執(zhí)行系統(tǒng) Reboot 調(diào)用
/system/core/init/reboot_utils.cpp
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
LOG(INFO) << "Reboot ending, jumping to kernel";
if (!IsRebootCapable()) {
// On systems where init does not have the capability of rebooting the
// device, just exit cleanly.
exit(0);
}
switch (cmd) {
case ANDROID_RB_POWEROFF: // 執(zhí)行關(guān)機
reboot(RB_POWER_OFF);
break;
case ANDROID_RB_RESTART2: // 執(zhí)行重啟
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
break;
case ANDROID_RB_THERMOFF: // 過熱保護,根據(jù)屬性來執(zhí)行關(guān)機或重起
if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) {
LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
} else {
reboot(RB_POWER_OFF);
}
break;
}
// In normal case, reboot should not return.
PLOG(ERROR) << "reboot call returned";
abort();
}屬性 sys.powerctl 的值決定了shutdown/reboot的行為,其格式為:[mode],[reason]。mode 為 reboot 或 shutdown,常見reason如下:
| shutdown,[reason] | userrequested | thermal | <null> |
|---|---|---|---|
| 用戶請求關(guān)機,需要運行fsck檢查 | 溫度異常引起的關(guān)機 | 執(zhí)行基本關(guān)機流程 |
| reboot,[reason] | userspace | fastboot | bootloader | recovery | sideload | sideload-auto-reboot | cold / warm / hard / <null> |
|---|---|---|---|---|---|---|---|
| 用戶空間軟重啟,用于更新應(yīng)用 | 重啟到fastboot模式。不支持邏輯分區(qū)時,重啟到bootloader模式。寫入BCB | 重啟到bootloader模式。寫入BCB | 重啟進入recvoery。寫入BCB | 重啟進入recovery,執(zhí)行sideload,用于本地升級系統(tǒng)。寫入BCB | sideload完成后自動重啟。寫入BCB | 執(zhí)行基本重啟流程 |
內(nèi)核中 Reboot 流程
內(nèi)核中的入口
Android Native 中最終執(zhí)行了 reboot 系統(tǒng)調(diào)用,對應(yīng)在內(nèi)核中的入口為:
/kernel/reboot.c
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
......
mutex_lock(&system_transition_mutex);
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;
........
case LINUX_REBOOT_CMD_POWER_OFF: // 關(guān)機
kernel_power_off();
do_exit(0);
break;
case LINUX_REBOOT_CMD_RESTART2: // 重啟
ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
if (ret < 0) {
ret = -EFAULT;
break;
}
buffer[sizeof(buffer) - 1] = '\0';
kernel_restart(buffer);
break;
........
}kernel_restart() 完成重啟
內(nèi)核通過 kernel_power_off() 完成關(guān)機動作,通過 kernel_restart() 完成重啟動作。
/kernel/reboot.c
void kernel_restart(char *cmd)
{
kernel_restart_prepare(cmd); // 執(zhí)行重啟的準備工作:調(diào)用reboot通知隊列,關(guān)閉usermodehelper,關(guān)閉所有設(shè)備
migrate_to_reboot_cpu(); // 遷移所有任務(wù)到cpu0上
syscore_shutdown(); // 關(guān)閉syscore設(shè)備
if (!cmd)
pr_emerg("Restarting system\n");
else
pr_emerg("Restarting system with command '%s'\n", cmd);
kmsg_dump(KMSG_DUMP_SHUTDOWN);
machine_restart(cmd); // 調(diào)用machine_restart()
}
EXPORT_SYMBOL_GPL(kernel_restart);
......
void kernel_power_off(void)
{
kernel_shutdown_prepare(SYSTEM_POWER_OFF); // 執(zhí)行重啟的準備工作:調(diào)用reboot通知隊列,關(guān)閉usermodehelper,關(guān)閉所有設(shè)備
if (pm_power_off_prepare)
pm_power_off_prepare();
migrate_to_reboot_cpu(); // 遷移所有任務(wù)到cpu0上
syscore_shutdown(); // 關(guān)閉syscore設(shè)備
pr_emerg("Power down\n");
kmsg_dump(KMSG_DUMP_SHUTDOWN);
machine_power_off(); // 調(diào)用machine_power_off()
}
EXPORT_SYMBOL_GPL(kernel_power_off);Reboot 和 Power Off 的大致流程
Reboot 和 Power Off 的大致流程是一樣的,主要區(qū)別在調(diào)用reboot通知隊列的傳參不同和machine執(zhí)行函數(shù)不同。這里簡單看一下 ARM64 的 machine_restart() 函數(shù)。
/arch/arm64/kernel/process.c
void machine_restart(char *cmd)
{
/* Disable interrupts first */
local_irq_disable(); // 關(guān)閉中斷
smp_send_stop(); // 停止當前處理器外的所有處理器
/*
* UpdateCapsule() depends on the system being reset via
* ResetSystem().
*/
if (efi_enabled(EFI_RUNTIME_SERVICES))
efi_reboot(reboot_mode, NULL); // EFI系統(tǒng)時
/* Now call the architecture specific reboot code. */
do_kernel_restart(cmd); // 調(diào)用restart處理隊列
/*
* Whoops - the architecture was unable to reboot.
*/
printk("Reboot failed -- System halted\n");
while (1);
}
/kernel/reboot.c
/**
* do_kernel_restart - Execute kernel restart handler call chain
*
* Calls functions registered with register_restart_handler.
*
* Expected to be called from machine_restart as last step of the restart
* sequence.
*
* Restarts the system immediately if a restart handler function has been
* registered. Otherwise does nothing.
*/
void do_kernel_restart(char *cmd)
{
atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd);
}內(nèi)核中的 Reboot 流程比較簡單,核心就是處理內(nèi)核、芯片、外設(shè)的狀態(tài),然后進行重啟。
Reboot 后的流程
重啟后,硬件相當于重新上電,最先進入 Bootloader,Bootloader 會根據(jù) Reboot Reason 進入到不同的系統(tǒng)狀態(tài)。通常來說,Bootloader 會分為多級,每家芯片原廠的實現(xiàn)都會有些區(qū)別,這里不去分析客制化的代碼,只看一下 Android 在 U-boot 中對 Reboot 的處理。
/u-boot/common/android_bootloader.c
int android_bootloader_boot_flow(const char* iface_str,
const char* dev_str,
struct blk_desc *dev_desc,
const struct disk_partition *misc_part_info,
const char *slot,
bool verify,
unsigned long kernel_address,
struct blk_desc *persistant_dev_desc)
{
......
/* Determine the boot mode and clear its value for the next boot if
* needed.
*/
// 根據(jù)misc分區(qū)信息獲取啟動模式
mode = android_bootloader_load_and_clear_mode(dev_desc, misc_part_info);
printf("ANDROID: reboot reason: \"%s\"\n", android_boot_mode_str(mode));
// TODO (rammuthiah) fastboot isn't suported on cuttlefish yet.
// Once it is, these lines can be removed.
if (mode == ANDROID_BOOT_MODE_BOOTLOADER) {
mode = ANDROID_BOOT_MODE_NORMAL;
}
bool normal_boot = (mode == ANDROID_BOOT_MODE_NORMAL);
switch (mode) {
case ANDROID_BOOT_MODE_NORMAL: // 正常啟動
#ifdef CONFIG_ANDROID_SYSTEM_AS_ROOT
/* In normal mode, we load the kernel from "boot" but append
* "skip_initramfs" to the cmdline to make it ignore the
* recovery initramfs in the boot partition.
*/
mode_cmdline = "skip_initramfs"; // System-as-root時跳過boot分區(qū)中的initramfs
#endif
break;
case ANDROID_BOOT_MODE_RECOVERY: // 進入recovery
#if defined(CONFIG_ANDROID_SYSTEM_AS_ROOT) || defined(CONFIG_ANDROID_USES_RECOVERY_AS_BOOT)
/* In recovery mode we still boot the kernel from "boot" but
* don't skip the initramfs so it boots to recovery.
* If on Android device using Recovery As Boot, there is no
* recovery partition.
*/
// System-as-root時使用boot分區(qū)中的initramfs,Recovery-as-root時沒有recovery分區(qū)
#else
boot_partition = ANDROID_PARTITION_RECOVERY;
#endif
break;
case ANDROID_BOOT_MODE_BOOTLOADER: // 進入bootloader·
/* Bootloader mode enters fastboot. If this operation fails we
* simply return since we can't recover from this situation by
* switching to another slot.
*/
return android_bootloader_boot_bootloader(); // 啟動進入bootloader
}
......
/* Load the kernel from the desired "boot" partition. */
// 獲取boot分區(qū)信息,用于加載kernel
boot_part_num =
android_part_get_info_by_name_suffix(dev_desc, boot_partition,
slot_suffix, &boot_part_info);
/* Load the vendor boot partition if there is one. */
// 獲取vendor boot分區(qū)信息。當使用GKI時,boot分區(qū)存儲GKI kernel,vendor boot供應(yīng)商客制化的boot代碼
vendor_boot_part_num =
android_part_get_info_by_name_suffix(dev_desc, vendor_boot_partition,
slot_suffix,
&vendor_boot_part_info);
struct disk_partition *bootconfig_part_info_ptr = NULL;
......
// 加載boot鏡像
struct andr_boot_info* boot_info = android_image_load(dev_desc, &boot_part_info,
vendor_boot_part_info_ptr,
kernel_address, slot_suffix, normal_boot, avb_bootconfig,
persistant_dev_desc, bootconfig_part_info_ptr,
verified_boot_img, verified_vendor_boot_img);
......
/* Assemble the command line */
// 整合boot信息到command line中,傳遞給kernel
command_line = android_assemble_cmdline(slot_suffix, mode_cmdline, normal_boot,
android_image_get_kernel_cmdline(boot_info),
android_image_is_bootconfig_used(boot_info),
avb_cmdline);
env_set("bootargs", command_line);
debug("ANDROID: bootargs: \"%s\"\n", command_line);
android_bootloader_boot_kernel(boot_info); // 啟動進入kernel
......
}Bootloader 的啟動流程也比較清晰,先解析啟動需要的信息,然后加載鏡像進行啟動。啟動信息是通過 MISC 分區(qū)讀取的,MISC 分區(qū)存儲的正是 Android 系統(tǒng)關(guān)機過程中需要更新的 BCB。
BCB(Bootloader Control Block)是 Android 系統(tǒng)中定義的一個啟動控制區(qū)域,以 RAW 格式進行存儲,用于在 Android 用戶空間和 Android 兼容的 bootloader 之間交換交換信息。在 Bootloader 中,BCB 的讀寫代碼如下,
/u-boot/common/android_bootloader.c
static int android_bootloader_message_load(
struct blk_desc *dev_desc,
const struct disk_partition *part_info,
struct bootloader_message *message)
{
ulong message_blocks = sizeof(struct bootloader_message) /
part_info->blksz;
if (message_blocks > part_info->size) {
printf("misc partition too small.\n");
return -1;
}
if (blk_dread(dev_desc, part_info->start, message_blocks, message) !=
message_blocks) {
printf("Could not read from misc partition\n");
return -1;
}
debug("ANDROID: Loaded BCB, %lu blocks.\n", message_blocks);
return 0;
}
static int android_bootloader_message_write(
struct blk_desc *dev_desc,
const struct disk_partition *part_info,
struct bootloader_message *message)
{
ulong message_blocks = sizeof(struct bootloader_message) /
part_info->blksz;
if (message_blocks > part_info->size) {
printf("misc partition too small.\n");
return -1;
}
if (blk_dwrite(dev_desc, part_info->start, message_blocks, message) !=
message_blocks) {
printf("Could not write to misc partition\n");
return -1;
}
debug("ANDROID: Wrote new BCB, %lu blocks.\n", message_blocks);
return 0;
}
......
static enum android_boot_mode android_bootloader_load_and_clear_mode(
struct blk_desc *dev_desc,
const struct disk_partition *misc_part_info)
{
struct bootloader_message bcb;
#ifdef CONFIG_FASTBOOT
char *bootloader_str;
/* Check for message from bootloader stored in RAM from a previous boot.
*/
bootloader_str = (char *)CONFIG_FASTBOOT_BUF_ADDR; // fastboot模式先先檢查RAM中的boot信息
if (!strcmp("reboot-bootloader", bootloader_str)) {
bootloader_str[0] = '\0';
return ANDROID_BOOT_MODE_BOOTLOADER;
}
#endif
/* Check and update the BCB message if needed. */
// 從Misc分區(qū)中加載BCB信息
if (android_bootloader_message_load(dev_desc, misc_part_info, &bcb) <
0) {
printf("WARNING: Unable to load the BCB.\n");
return ANDROID_BOOT_MODE_NORMAL;
}
// bootonce-bootloader意味著要啟動計入bootloader,此時擦除BCB內(nèi)容。
if (!strcmp("bootonce-bootloader", bcb.command)) {
/* Erase the message in the BCB since this value should be used
* only once.
*/
memset(bcb.command, 0, sizeof(bcb.command));
android_bootloader_message_write(dev_desc, misc_part_info,
&bcb);
return ANDROID_BOOT_MODE_BOOTLOADER;
}
if (!strcmp("boot-recovery", bcb.command))
return ANDROID_BOOT_MODE_RECOVERY;
return ANDROID_BOOT_MODE_NORMAL;
}BCB在 Android bootloader 中定義為一個結(jié)構(gòu)體數(shù)據(jù),在 Flash 中以 RAW 格式存儲。其結(jié)構(gòu)定義為,
/u-boot/include/android_bootloader_message.h
// Spaces used by misc partition are as below:
// 0 - 2K For bootloader_message
// 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used
// as bootloader_message_ab struct)
// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices
// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
// are not configurable without changing all of them.
static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
/* Bootloader Message (2-KiB)
*
* This structure describes the content of a block in flash
* that is used for recovery and the bootloader to talk to
* each other.
*
* The command field is updated by linux when it wants to
* reboot into recovery or to update radio or bootloader firmware.
* It is also updated by the bootloader when firmware update
* is complete (to boot into recovery for any final cleanup)
*
* The status field was used by the bootloader after the completion
* of an "update-radio" or "update-hboot" command, which has been
* deprecated since Froyo.
*
* The recovery field is only written by linux and used
* for the system to send a message to recovery or the
* other way around.
*
* The stage field is written by packages which restart themselves
* multiple times, so that the UI can reflect which invocation of the
* package it is. If the value is of the format "#/#" (eg, "1/3"),
* the UI will add a simple indicator of that status.
*
* We used to have slot_suffix field for A/B boot control metadata in
* this struct, which gets unintentionally cleared by recovery or
* uncrypt. Move it into struct bootloader_message_ab to avoid the
* issue.
*/
struct bootloader_message {
char command[32];
char status[32];
char recovery[768];
// The 'recovery' field used to be 1024 bytes. It has only ever
// been used to store the recovery command line, so 768 bytes
// should be plenty. We carve off the last 256 bytes to store the
// stage string (for multistage packages) and possible future
// expansion.
char stage[32];
// The 'reserved' field used to be 224 bytes when it was initially
// carved off from the 1024-byte recovery field. Bump it up to
// 1184-byte so that the entire bootloader_message struct rounds up
// to 2048-byte.
char reserved[1184];
};BCB主要的功能如下
- 實現(xiàn) Android 特定的 bootloader 流程。
- 在用戶空間和 bootloader 之間傳遞
boot reason,并控制對應(yīng)的行為。 - 傳遞 Recovery 系統(tǒng)需要的 commands。
Android 用戶空間(normal / recovery) 也是讀寫B(tài)CB來控制啟動行為,如上文中 Init 的 Reboot 過程中就會更新BCB。BCB的讀寫函數(shù)如下,
/bootable/recovery/bootloader_message/bootloader_message.cpp
bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
std::string* err) {
return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
}
// 從Misc分區(qū)讀取BCB
bool read_bootloader_message(bootloader_message* boot, std::string* err) {
std::string misc_blk_device = get_misc_blk_device(err);
if (misc_blk_device.empty()) {
return false;
}
return read_bootloader_message_from(boot, misc_blk_device, err);
}
bool write_bootloader_message_to(const bootloader_message& boot, const std::string& misc_blk_device,
std::string* err) {
return write_misc_partition(&boot, sizeof(boot), misc_blk_device,
BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
}
// 寫B(tài)CB到Misc分區(qū)
bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
std::string misc_blk_device = get_misc_blk_device(err);
if (misc_blk_device.empty()) {
return false;
}
return write_bootloader_message_to(boot, misc_blk_device, err);
}
// 清空BSC
bool clear_bootloader_message(std::string* err) {
bootloader_message boot = {};
return write_bootloader_message(boot, err);
}
// 寫recovery commands到BCB
bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) {
bootloader_message boot = {};
update_bootloader_message_in_struct(&boot, options);
return write_bootloader_message(boot, err);
}
bool write_bootloader_message_to(const std::vector<std::string>& options,
const std::string& misc_blk_device, std::string* err) {
bootloader_message boot = {};
update_bootloader_message_in_struct(&boot, options);
return write_bootloader_message_to(boot, misc_blk_device, err);
}
// 更新recovery commands
bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
bootloader_message boot;
if (!read_bootloader_message(&boot, err)) {
return false;
}
update_bootloader_message_in_struct(&boot, options);
return write_bootloader_message(boot, err);
}
bool update_bootloader_message_in_struct(bootloader_message* boot,
const std::vector<std::string>& options) {
if (!boot) return false;
// Replace the command & recovery fields.
memset(boot->command, 0, sizeof(boot->command));
memset(boot->recovery, 0, sizeof(boot->recovery));
strlcpy(boot->command, "boot-recovery", sizeof(boot->command));
std::string recovery = "recovery\n";
for (const auto& s : options) {
recovery += s;
if (s.back() != '\n') {
recovery += '\n';
}
}
strlcpy(boot->recovery, recovery.c_str(), sizeof(boot->recovery));
return true;
}
// 將重啟到bootloader的命令寫入到BCB,這里是bootonce-bootloader
bool write_reboot_bootloader(std::string* err) {
bootloader_message boot;
if (!read_bootloader_message(&boot, err)) {
return false;
}
if (boot.command[0] != '\0') {
*err = "Bootloader command pending.";
return false;
}
strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
return write_bootloader_message(boot, err);
}以上就是Android系統(tǒng)中底層Reboot流程的詳細內(nèi)容,更多關(guān)于Android系統(tǒng)中底層Reboot流程的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android UI控件之ListView實現(xiàn)圓角效果
這篇文章主要為大家詳細介紹了Android UI控件之ListView實現(xiàn)圓角效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12
Android Studio實現(xiàn)仿微信APP門戶界面詳解及源碼
這篇文章帶你通過Android studio來實現(xiàn)微信APP的門戶界面,主要說明框架的各部分功能與實現(xiàn)過程,下文包含了整個開發(fā)過程,以及解決問題的思路并再末尾提供了源碼鏈接2021-10-10
Android與單片機通信常用數(shù)據(jù)轉(zhuǎn)換方法總結(jié)
本文主要介紹Android與單片機通信常用數(shù)據(jù)轉(zhuǎn)換方法,這里提供了代碼示例,有需要的小伙伴可以參考下2016-09-09
Android4.4下MediaProvider無法向外置SD卡中文件寫數(shù)據(jù)的解決方法
這篇文章主要介紹了Android4.4下MediaProvider無法向外置SD卡中文件寫數(shù)據(jù)的解決方法,實例分析了Android4.4下針對讀寫限制的修改技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-10-10
Android如何監(jiān)測文件夾內(nèi)容變化詳解
最近在開發(fā)android應(yīng)用程序的時候遇到了一個監(jiān)測文件夾的功能,所以下面這篇文章主要給大家介紹了關(guān)于Android如何監(jiān)測文件夾內(nèi)容變化的相關(guān)資料,需要的朋友可以參考下2021-12-12
輕松實現(xiàn)功能強大的Android刮獎效果控件(ScratchView)
這篇文章主要為大家詳細介紹了ScratchView如何一步步打造萬能的Android刮獎效果控件,,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09
Android Fragment 和 FragmentManager 的代碼分析
這篇文章主要介紹了Android Fragment 和 FragmentManager 的代碼分析,非常不錯,具有參考借鑒價值,需要的的朋友參考下吧2017-01-01

