写这个的原因是在一个新的方案中,发现reboot recovery无法进入recovery模式。按照以往的理解,我一直以为android到recovery流程是因为在misc分区中写入了boot-recovery字段,但是翻来翻去,也没找到是在哪里写入,所以跟了一下reboot流程,发现boot-recovery的写入好像不是我原来想的那么一回事 ,这个流程不是在上层做的。
以重启进入recovery来说。
1、framework
1)RecoverySystem提供的接口中,顺序如此:
installPackage->bootCommand->pm.reboot(“recovery”);
2)这里调用powerManager的reboot接口
该接口唯一参数reason代表需要的特定重启模式,比如recovery,当然也可以为null。
public void reboot(String reason) {
try {
mService.reboot(false, reason, true);
} catch (RemoteException e) {
}
}
通过binder,调用到powerManagerService的reboot函数
@Override // Binder call
public void reboot(boolean confirm, String reason, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(false, confirm, reason, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,
final String reason, boolean wait) {
if (mHandler == null || !mSystemReady) {
throw new IllegalStateException("Too early to call shutdown() or reboot()");
}
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
if (shutdown) {
ShutdownThread.shutdown(mContext, confirm);
} else {
ShutdownThread.reboot(mContext, reason, confirm);
}
}
}
};
.....省略
}
3)调用到ShutdownThread.reboot(mContext, reason, confirm);
public static void reboot(final Context context, String reason, boolean confirm) {
mReboot = true;
mRebootSafeMode = false;
mRebootReason = reason;
shutdownInner(context, confirm);
}
这里说明是需要重启,且不是安全模式,重启参数为传递下来的reason,shutdownInner的confirm参数是用来设置是否有确认提示框的,通过reboot接口调用重启是没有的,为false。
重启的实现在run()中,因为ShutdownThread是Thread的扩展,所以run会自动运行。
run中会调用rebootOrShutdown(mReboot, mRebootReason);
然后又重新回到PowerManagerService.lowLevelShutdown();
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");
}
...省略
PowerManagerService.lowLevelShutdown();
}
我们跟回PowerManagerService看下,其实无论是lowLevelShutdown还是lowLevelReboot,就是设置属性”sys.powerctl”。
reboot是SystemProperties.set(“sys.powerctl”, “reboot,” + reason);
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();
}
}
2、init
SystemProperties.set(“sys.powerctl”, “reboot,” + reason);我们看下init.rc是怎么写的。
on property:sys.powerctl=*
powerctl ${sys.powerctl}
所以我们要再看下init的代码,可以看到对应的函数是do_powerctl
KEYWORD(powerctl, COMMAND, 1, do_powerctl)
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);
}
这里会继续去调用android_reboot函数,根据参入参数的不同
1)关机 ANDROID_RB_POWEROFF, 无需reason,直接调用reboot进行关机;
2)带参数的特殊重启 ANDROID_RB_RESTART2,可以有reason,也可以为“”
android_reboot在system/core/libcutils/android_reboot.c
int android_reboot(int cmd, int flags, char *arg)
{
int ret,fd;
sync();
fd = open("/sys/class/remount/need_remount", O_WRONLY);
if (fd < 0) {
return -1;
}
write(fd, "1", 1);
close(fd);
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;
}
首先做了一些关机重启前的预处理工作,sync()作用是将缓存中的信息写入磁盘,以免程序异常结束导致文件被损坏,linux系统关机前会做几次这样的动作;而remount_ro()作用是通过调用emergency_remount()强制将文件系统挂载为只读,不再允许任何写入操作,同时会通过检查/proc/mounts的设备状态来确认是否当前的所有写入工作已经完成,这个检查过程是阻塞操作。
以reboot recovery为例,arg即为recovery,会传入ANDROID_RB_RESTART2。
最终会调用:
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, arg);
3、kernel
__reboot通过syscall来到内核,这部分我也不懂,接下来参看别人的解读。
可参考:
http://blog.sina.com.cn/s/blog_6695f9eb0101hse4.html