您的位置:首页 > 移动开发 > Android开发

Android 关机、重启、recovery流程分析

2018-02-23 14:18 393 查看

以Android5.1的代码来分析。

应用层和框架层实现

上层应用可以通过PowerManager来实现关机、重启、进recovery等功能。比如RecoverySystem 中就是使用PM使系统进入recovery模式:源码路径:
frameworks/base/core/java/android/os/RecoverySystem.java
private static void bootCommand(Context context, String... args) throws IOException {
......
// Having written the command file, go ahead and reboot
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
pm.reboot(PowerManager.REBOOT_RECOVERY);
......
}
PowerManager会通过aidl与PowerManagerService交互,reboot带reason参数,像重启、关机、恢复出厂设置等等;源码路径:
frameworks/base/core/java/android/os/IPowerManager.aidl
reboot在aidl中的定义
interface IPowerManager
{
// WARNING: The first five methods must remain the first five methods because their
// transaction numbers must not change unless IPowerManager.cpp is also updated.
void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws,
String historyTag);
void acquireWakeLockWithUid(IBinder lock, int flags, String tag, String packageName,
int uidtoblame);
void releaseWakeLock(IBinder lock, int flags);
void updateWakeLockUids(IBinder lock, in int[] uids);
......

void reboot(boolean confirm, String reason, boolean wait);
void shutdown(boolean confirm, boolean wait);
void crash(String message);

void setStayOnSetting(int val);
void boostScreenBrightness(long time);

// temporarily overrides the screen brightness settings to allow the user to
// see the effect of a settings change without applying it immediately
void setTemporaryScreenBrightnessSettingOverride(int brightness);
void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj);

// sets the attention light (used by phone app only)
void setAttentionLight(boolean on, int color);
}
PMS中reboot调用shutdownOrRebootInternal源码路径:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
@Override // Binder call
public void reboot(boolean confirm, String reason, boolean wait) {
......
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(false, confirm, reason, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}

public static void lowLevelShutdown() {
SystemProperties.set("sys.powerctl", "shutdown");
}
public static void lowLevelReboot(String reason) {
if (reason == null) {
reason = "";
}
long duration;
if (reason.equals(PowerManager.REBOOT_RECOVERY)) {
SystemProperties.set("ctl.start", "pre-recovery");
duration = 300 * 1000L;
} else {
SystemProperties.set("sys.powerctl", "reboot," + reason);
duration = 20 * 1000L;
}
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
shutdownOrRebootInternal根据reason来决定是调用ShutdownThread的reboot或者shutdown:
private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,
final String reason, boolean wait) {
......
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
if (shutdown) {
ShutdownThread.shutdown(mContext, confirm);
} else {
ShutdownThread.reboot(mContext, reason, confirm);
}
......
不管是关机还是重启都是走shutdownInner 源码路径:
frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
public static void reboot(final Context context, String reason, boolean confirm) {
mReboot = true;
mRebootSafeMode = false;
mRebootReason = reason;
shutdownInner(context, confirm);
}
......
public static void shutdown(final Context context, boolean confirm) {
mReboot = false;

1208e
mRebootSafeMode = false;
shutdownInner(context, confirm);
}
......
static void shutdownInner(final Context context, boolean confirm) {
......
beginShutdownSequence(context);
}
}
看一下beginShutdownSequence
private static void beginShutdownSequence(Context context) {
......
// start the thread that initiates shutdown
sInstance.mHandler = new Handler() {
};
sInstance.start();
}
已经start了,我们就去run里面瞧一瞧干了什么事,就对“REBOOT_SAFEMODE_PROPERTY”设置了1或者0,然后调用rebootOrShutdown。
/**
* Makes sure we handle the shutdown gracefully.
* Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
*/
public void run() {
....//关机或者重启前的一些处理
String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
......
if (mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}

......
rebootOrShutdown(mReboot, mRebootReason);
}
rebootOrShutdown中会调用PMS的lowLevelReboot或者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");
} else if (SHUTDOWN_VIBRATE_MS > 0) {
....//关机震动相关处理
// Shutdown power
Log.i(TAG, "Performing low-level shutdown...");
PowerManagerService.lowLevelShutdown();//关机
}
再回过头去看PowerManagerService里面的lowLevelShutdown,发现关机、重启、recovery等是通过设置Properties来实现控制的:SystemProperties.set("sys.powerctl"......

native实现

Android系统开始会执行init,而init会去解析执行init.rc中的内容。其中就有对于Properties “sys.powerctl ”的处理(当“sys.powerctl 发生改变时触发进行powerctl 操作)。 位于源码:
system/core/rootdir/init.rc
on property:sys.powerctl=*
powerctl ${sys.powerctl}
而powerctl 是init的内嵌命令,在init源码中: 源码路径:system/core/init/builtins.c
int do_powerctl(int nargs, char **args)
{
......
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系统调用reboot,如果是recovery、wipe data等操作还需要额外写参数到misc分区,以便开机时区分正常进Android系统还是recovery、efex。源码路径:system/core/libcutils/android_reboot.c
int android_reboot(int cmd, int flags UNUSED, 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 = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
//               LINUX_REBOOT_CMD_RESTART2, arg);
write_misc(arg);
ret = reboot(RB_AUTOBOOT);
break;

default:
ret = -1;
}

return ret;
}
write_misc源码路径:system/core/libcutils/misc_rw.c有兴趣的可以研究一下。
static const char *MISC_DEVICE = "/dev/block/by-name/misc";
/* Bootloader Message
*/
struct bootloader_message {
char command[32];
char status[32];
char recovery[1024];
};
......
/* force the next boot to recovery/efex */
int write_misc(char *reason) {
struct bootloader_message boot, temp;

memset(&boot, 0, sizeof(boot));
if (!strcmp("recovery", reason)) {
reason = "boot-recovery";
}

strcpy(boot.command, reason);
if (set_bootloader_message_block(&boot, MISC_DEVICE) )
return -1;

//read for compare
memset(&temp, 0, sizeof(temp));
if (get_bootloader_message_block(&temp, MISC_DEVICE))
return -1;

if( memcmp(&boot, &temp, sizeof(boot)) )
return -1;

return 0;
}
static int set_bootloader_message_block(const struct bootloader_message *in,
const char* device) {
FILE* f = fopen(device, "wb");
if (f == NULL) {
LOGE("Can't open %s\n(%s)\n", device, strerror(errno));
return -1;
}
int count = fwrite(in, sizeof(*in), 1, f);
if (count != 1) {
LOGE("Failed writing %s\n(%s)\n", device, strerror(errno));
return -1;
}
sync();
if (fclose(f) != 0) {
LOGE("Failed closing %s\n(%s)\n", device, strerror(errno));
return -1;
}
return 0;
}
那么问题来了,开机时怎么判断是该正常开机、还是进入recovery呢? 一般是在u-boot中读取misc分区的数据,然后进行解析后对boot env "bootcmd"进行参数配置,这样就可以引导进入不同的系统了。 例如全志平台u-boot: board/sunxi/common/board_common.c
int update_bootcmd(void)
{
......
misc_message = (struct bootloader_message *)misc_args;
memset(misc_args, 0x0, 2048);
memset(misc_fill, 0xff, 2048);

mode = detect_other_boot_mode();
switch(mode)
{
......
case ANDROID_NULL_MODE:
{
......
else
{
misc_offset = sunxi_partition_get_offset_byname("misc");
......
printf("misc partition found\n");
//read misc partition data
sunxi_flash_read(misc_offset, 2048/512, misc_args);
......
else if(!strcmp(misc_message->command, "boot-recovery"))
......
puts("Recovery detected, will boot recovery\n");
sunxi_str_replace(boot_commond, "boot_normal", "boot_recovery");
}
/* android recovery will clean the misc */
}
else if(!strcmp(misc_message->command, "bootloader"))
{
puts("Fastboot detected, will boot fastboot\n");
sunxi_str_replace(boot_commond, "boot_normal", "boot_fastboot");
if(misc_offset)
sunxi_flash_write(misc_offset, 2048/512, misc_fill);
}
else if(!strcmp(misc_message->command, "usb-recovery"))
{
puts("Recovery detected, will usb recovery\n");
sunxi_str_replace(boot_commond, "boot_normal", "boot_recovery");
}
else if(!strcmp(misc_message->command ,"debug_mode"))
{
puts("debug_mode detected ,will enter debug_mode");
if(0 == debug_mode_set())
{
debug_mode_update_info();
}
sunxi_flash_write(misc_offset,2048/512,misc_fill);
......
setenv("bootcmd", boot_commond);
printf("to be run cmd=%s\n", boot_commond);

return 0;
}
boot_recovery的evn:
"boot_recovery=sunxi_flash read 4007f800 recovery;boota 4007f800\0"
boot_recovery将recovery加载到内存中并引导,然后就这样就进入recovery中了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息