Android(安卓)关机、重启、recovery流程分析
以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; 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 datasunxi_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中了。
更多相关文章
- Android(安卓)源码结构【搜集】
- Android(安卓)launcher -- launcher源码修改 1
- Android整机源码如何轻松下载?清华大学Android源码镜像
- Android中检测手机制式和移动网络类型
- android面试-面试知识点总结
- android 4.4 设置默认日期格式
- Android(安卓)JsBridge 源码解析
- Android(安卓)登录界面Demo源码
- Android(安卓)Handler消息机制(源码分析)