下面是android简单的reboot流程视图
下面是源码分析
从Java层的ShutdownThread.java的shutdown()开始:
frameworks/base/services/java/com/android/server/power/ShutdownThread.java
发出shutdown的广播,这个很重要,要通知所有应用我要关机了。
[java]
private static void beginShutdownSequence(Context context) {
.......
Log.i(TAG, "Sending shutdown broadcast...");
// First send the high-level shut down broadcast.
mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
synchronized (mActionDoneSync) {
while (!mActionDone) {
long delay = endTime - SystemClock.elapsedRealtime();
if (delay <= 0) {
Log.w(TAG, "Shutdown broadcast timed out");
break;
}
try {
mActionDoneSync.wait(delay);
} catch (InterruptedException e) {
}
}
}
关闭activity管理器,因为不需要了。
Log.i(TAG, "Shutting down activity manager...");
final IActivityManager am =
ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
if (am != null) {
try {
am.shutdown(MAX_BROADCAST_TIME);
} catch (RemoteException e) {
}
}
关闭radio设备,包括NFC,modem等。
shutdownRadios(MAX_RADIO_WAIT_TIME);
关闭mountservice,这个是阻塞操作,失败会导致关机失败,把电耗光。成功则打印:Result code 0 from mount....
Log.i(TAG, "Shutting down MountService");
// Set initial variables and time out time.
mActionDone = false;
final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
synchronized (mActionDoneSync) {
try {
final IMountService mount = IMountService.Stub.asInterface(
ServiceManager.checkService("mount"));
if (mount != null) {
mount.shutdown(observer);
} else {
Log.w(TAG, "MountService unavailable for shutdown");
}
} catch (Exception e) {
Log.e(TAG, "Exception during MountService shutdown", e);
}
while (!mActionDone) {
long delay = endShutTime - SystemClock.elapsedRealtime();
if (delay <= 0) {
Log.w(TAG, "Shutdown wait timed out");
break;
}
try {
mActionDoneSync.wait(delay);
} catch (InterruptedException e) {
}
}
}
}
然后调rebootOrShutdown()函数
public static void rebootOrShutdown(boolean reboot, String reason) {
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
PowerManagerService.lowLevelReboot(reason);
Log.e(TAG, "Reboot failed, will attempt shutdown instead");
} else if (SHUTDOWN_VIBRATE_MS > 0) {
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator();
try {
vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
} catch (Exception e) {
// Failure to vibrate shouldn‘t interrupt shutdown. Just log it.
Log.w(TAG, "Failed to vibrate during shutdown.", e);
}
// vibrator is asynchronous so we need to wait to avoid shutting down too soon.
try {
Thread.sleep(SHUTDOWN_VIBRATE_MS);
} catch (InterruptedException unused) {
}
}
// Oem specific shutdown
deviceRebootOrShutdown(reboot, reason);
// Shutdown power
Log.i(TAG, "Performing low-level shutdown...");
PowerManagerService.lowLevelShutdown();
}
仔细看这函数,会走两个不同流程PowerManagerService.lowLevelReboot(reason);
或者PowerManagerService.lowLevelShutdown();
注意reason是重启用的,比如recovery,rtc,等。
下面来到PowerManagerSercice.java文件
frameworks/base/services/java/com/android/server/power/PowerManagerService.java
[java]
public static void lowLevelShutdown() {
SystemProperties.set("sys.powerctl", "shutdown");
}
/**
* Low-level function to reboot the device. On success, this function
* doesn‘t return. If more than 5 seconds passes from the time,
* a reboot is requested, this method returns.
*
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
*/
public static void lowLevelReboot(String reason) {
if (reason == null) {
reason = "";
}
SystemProperties.set("sys.powerctl", "reboot," + reason);
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
不管是lowLevelReboot还是lowLevelShutdown,都会执行SystemProperties.set("sys.powerctl",XXX)的操作。设置一下系统属性,有些同学可能会问,设下属性就可以了吗?当然可以。
因为这个属性会触发init.rc的某些操作,建议不懂的同学好好恶补一下init进程的作用吧。
来到init进程
system/core/init/
[init.rc]
on property:sys.powerctl=*
powerctl ${sys.powerctl}
看到吧,往sys.powerctl属性写东西时,就会触发这个开关。就是调用powerctl指令,并把${sys.powerctl}参数传递给它。
这个指令init进程会解释。
在这里关联,powerctl指令对应的操作是do_powerctl
system/core/init/keywords.h
[h]
KEYWORD(powerctl, COMMAND, 1, do_powerctl)
KEYWORD(restart, COMMAND, 1, do_restart)
KEYWORD(restorecon, COMMAND, 1, do_restorecon)
system/core/init/builtins.c
[c]
int do_powerctl(int nargs, char **args)
{
char command[PROP_VALUE_MAX];
int res;
int len = 0;
int cmd = 0;
char *reboot_target;
res = expand_props(command, args[1], sizeof(command));
if (res) {
ERROR("powerctl: cannot expand ‘%s‘\n", args[1]);
return -EINVAL;
}
if (strncmp(command, "shutdown", 8) == 0) {
cmd = ANDROID_RB_POWEROFF;
len = 8;
} else if (strncmp(command, "reboot", 6) == 0) {
cmd = ANDROID_RB_RESTART2;
len = 6;
} else {
ERROR("powerctl: unrecognized command ‘%s‘\n", command);
return -EINVAL;
}
if (command[len] == ‘,‘) {
reboot_target = &command[len + 1];
} else if (command[len] == ‘\0‘) {
reboot_target = "";
} else {
ERROR("powerctl: unrecognized reboot target ‘%s‘\n", &command[len]);
return -EINVAL;
}
return android_reboot(cmd, 0, reboot_target);
}
do_powerctl函数接受${sys.powerctl}参数,然后调用android_reboot()函数
这个函数在android_reboot.c定义
system/core/libcutils/android_reboot.c
android_reboot()函数做了如下几个工作:
第一:sync()回写block设备的内容,这个是阻塞操作
第二:remount_ro()把所有block设备remount成只读,不允许写操作,所以你会发现logkit抓的demsg最后一句话是SysRq : Emergency Remount R/O 因为后面的存储成只读模式了,无法写入,这时的LOG只存在内存中,要用last kmsg技术导出来。这个也是阻塞操作。
第三:调用reboot或__reboot函数
int android_reboot(int cmd, int flags, char *arg)
{
int ret;
sync();
remount_ro();
switch (cmd) {
case ANDROID_RB_RESTART:
ret = reboot(RB_AUTOBOOT);
break;
case ANDROID_RB_POWEROFF:
ret = reboot(RB_POWER_OFF);
break;
case ANDROID_RB_RESTART2:
ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, arg);
break;
default:
ret = -1;
}
return ret;
}
bionic/libc/bionic/reboot.c
int reboot (int mode)
{
return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );
}
从这里可以看出reboot还是调到__reboot函数,这个函数是汇编指令
bionic/libc/arch-arm/syscalls/__reboot.S
ENTRY(__reboot)
mov ip, r7
ldr r7, =__NR_reboot
swi #0
mov r7, ip
cmn r0, #(MAX_ERRNO + 1)
bxls lr
neg r0, r0
b __set_errno
END(__reboot)
好吧,__reboot被扯到__NR_reboot里来了。那__NR_reboot是那里定义呢。
好吧,在这!!
bionic/libc/kernel/arch-arm/asm/unistd.h
#define __NR_reboot (__NR_SYSCALL_BASE+ 88)
__NR_reboot指向内核系统调用的88入口处。
好吧,下面要进内核找找了
-------------------------------------------
注意:android的系统调用都在bionic里。
bionic/libc/kernel/arch-arm/unistd.h与kernel/include/asm-generic/uinistd.h系统调用是相对应的。
这点很重要,方便以后代码跟踪
---------------------------------------------
看kernel里的unistd.h文件
kernel/include/asm-generic/unistd.h
#define __NR_reboot 142
__SYSCALL(__NR_reboot, sys_reboot)
__NR_reboot被映射到sys_reboot
搜索一下sys_reboot那里定义,好像找不到,别急
在这里,这里涉及到SYSCALL_DEFINE的宏,不懂的同事可以去补一下。
kernel/kernel/sys.c
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;
case LINUX_REBOOT_CMD_CAD_ON:
C_A_D = 1;
break;
case LINUX_REBOOT_CMD_CAD_OFF:
C_A_D = 0;
break;
case LINUX_REBOOT_CMD_HALT:
kernel_halt();
do_exit(0);
panic("cannot halt");
case LINUX_REBOOT_CMD_POWER_OFF:
kernel_power_off();
do_exit(0);
break;
case LINUX_REBOOT_CMD_RESTART2:
if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
ret = -EFAULT;
break;
}
buffer[sizeof(buffer) - 1] = ‘\0‘;
kernel_restart(buffer);
break;
}
sys_reboot干了这些事
根据传入的reason,选择是kernel_restart()还是kernel_power_off()
如果是restart2还会把reason拷到内核空间,传给kernel_restart()函数
下面就转到平台相关代码,这里以高通为例
kernel/arch/arm/mach-msm/restart.c
如果是关机就调用
static void __msm_power_off(int lower_pshold)
{
printk(KERN_CRIT "Powering off the SoC\n");
}
打印关键LOG:Powering off the Soc
如果是重启会调用
void msm_restart(char mode, const char *cmd)
{
msm_restart_prepare(cmd);
}
打印关键LOG:Going down for restart now
注意到msm_restart_prepare()没?
static void msm_restart_prepare(const char *cmd)
{
#ifdef CONFIG_MSM_DLOAD_MODE
/* This looks like a normal reboot at this point. */
set_dload_mode(0);
/* Write download mode flags if we‘re panic‘ing */
set_dload_mode(in_panic);
/* Write download mode flags if restart_mode says so */
if (restart_mode == RESTART_DLOAD)
set_dload_mode(1);
/* Kill download mode if master-kill switch is set */
if (!download_mode)
set_dload_mode(0);
#endif
pm8xxx_reset_pwr_off(1);
/* Hard reset the PMIC unless memory contents must be maintained. */
if (get_dload_mode() || (cmd != NULL && cmd[0] != ‘\0‘))
qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET);
else
qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET);
if (cmd != NULL) {
if (!strncmp(cmd, "bootloader", 10)) {
__raw_writel(0x77665500, restart_reason);
} else if (!strncmp(cmd, "recovery", 8)) {
__raw_writel(0x77665502, restart_reason);
} else if (!strcmp(cmd, "rtc")) {
__raw_writel(0x77665503, restart_reason);
} else if (!strncmp(cmd, "oem-", 4)) {
unsigned long code;
code = simple_strtoul(cmd + 4, NULL, 16) & 0xff;
__raw_writel(0x6f656d00 | code, restart_reason);
} else if (!strncmp(cmd, "edl", 3)) {
enable_emergency_dload_mode();
} else {
__raw_writel(0x77665501, restart_reason);
}
}
}
msm_restart_prepare()函数是处理重启前的一些工作。
是否重启进入dload模式
根据reason判断是warm or hard reset
根据reason往imem里写内容
重启时sbl1读取,以便判断进入那种模式。
好吧。完了