这里所说的reboot指的是软件重启,并非断电重启。我们知道android系统的几个功能,比如:回复出厂设置、OTA升级等都需要重启系统,而且重 启后要进入recovery模式,有的手机还带有重启进入fastboot或者其他模式。这些在软重启中式怎么做到的呢?
经过一段查找找到了这个文件:\frameworks\base\core\java\android\os\RecoverySystem.java
我们来看这个文件里面有一个类public class RecoverySystem 我们来看这个类的说明
[cpp] view plain copy
- /**
- * RecoverySystem contains methods for interacting with the Android
- * recovery system (the separate partition that can be used to install
- * system updates, wipe user data, etc.)
- */
这个类里面完成了android系统的回复包括安装系统更新,删除用户数据等。
下面我们来看这个类里面的几个 函数
[cpp] view plain copy
- /**
- * Reboots the device in order to install the given update
- * package.
- * 重启系统来安装升级包
- * Requires the {@link android.Manifest.permission#REBOOT} permission.
- *
- * @param context the Context to use
- * @param packageFile the update package to install. Must be on
- * a partition mountable by recovery. (The set of partitions
- * known to recovery may vary from device to device. Generally,
- * /cache and /data are safe.)
- *
- * @throws IOException if writing the recovery command file
- * fails, or if the reboot itself fails.
- */
- public static void installPackage(Context context, File packageFile)
- throws IOException {
- String filename = packageFile.getCanonicalPath();
- Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
- String arg = "--update_package=" + filename;
- bootCommand(context, arg);
- }
-
- /**
- * Reboots the device and wipes the user data partition. This is
- * sometimes called a "factory reset", which is something of a
- * misnomer because the system partition is not restored to its
- * factory state.
- * 重启系统删除用户数据,这个通常被回复出厂设置调用
- * Requires the {@link android.Manifest.permission#REBOOT} permission.
- *
- * @param context the Context to use
- *
- * @throws IOException if writing the recovery command file
- * fails, or if the reboot itself fails.
- */
- public static void rebootWipeUserData(Context context) throws IOException {
- final ConditionVariable condition = new ConditionVariable();
-
- Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
- context.sendOrderedBroadcast(intent, android.Manifest.permission.MASTER_CLEAR,
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- condition.open();
- }
- }, null, 0, null, null);
-
- // Block until the ordered broadcast has completed.
- condition.block();
-
- bootCommand(context, "--wipe_data");
- }
- /**
- * Reboot into the recovery system to wipe the /cache partition.
- * 重启系统来删除 /cache目录文件
- * @throws IOException if something goes wrong.
- */
- public static void rebootWipeCache(Context context) throws IOException {
- bootCommand(context, "--wipe_cache");
- }
这 几个函数功能的注释写的很清楚,android系统做 wipe_data、wipe_cache、OTA升级就是调用的这三个函数。具体在哪调用的我们不一一列举了,简单说一下,比如 rebootWipeUserData是在回复出厂设置时候调用的,代码在\frameworks\base\services\java\com \android\server\MasterClearReceiver.java中。
我们仔细研究着几个 函数发现,要实现不同的重启模式就是要bootCommand()这个函数传送不同的参数,android重启就是由这个函数来实现的,
我们就来看
[cpp] view plain copy
- /**
- * Reboot into the recovery system with the supplied argument.
- * @param arg to pass to the recovery utility.
- * @throws IOException if something goes wrong.
- */
- private static void bootCommand(Context context, String arg) throws IOException {
- RECOVERY_DIR.mkdirs(); // 创建recovery的目录
- COMMAND_FILE.delete(); // 清空recovery命令文件
- /*
- *这两个文件都在/cache目录中,他俩的定义是
- * private static File RECOVERY_DIR = new File("/cache/recovery");
- * private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
- */
- LOG_FILE.delete();
-
- FileWriter command = new FileWriter(COMMAND_FILE);//穿件新的命令文件
- try {
- command.write(arg); //将命令写入命令文件,给recovery模式使用
- command.write("\n");
- } finally {
- command.close();
- }
-
- // Having written the command file, go ahead and reboot
- PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- pm.reboot("recovery"); //重启系统
-
- throw new IOException("Reboot failed (no permissions?)");
- }
这里首先要说明/cache目录和命令文件这两个东西,他们是主系统和recovery模式之间交流的桥梁,主系统把要做的事情写入到这两个中,然后recovery会读取这两个文件,再做相应的处理,这一点在我的另一篇文章中做了更详细的说明 http://blog.csdn.net/dkleikesa/article/details/9706137
从这个函数就可以看出了,到了这里上面的三个功能最终合并成了一个--进入recovery模式,于是我们就来看 pm.reboot("recovery");
在\frameworks\base\core\java\android\os\PowerManager.java
[cpp] view plain copy
- public void reboot(String reason)
- {
- try {
- mService.reboot(reason);
- } catch (RemoteException e) {
- }
- }
这里mService的定义
[cpp] view plain copy
- public PowerManager(IPowerManager service, Handler handler)
- {
- mService = service;
- mHandler = handler;
- }
是在构造函数里传进来的,我们继续来看这个参数的传送在\frameworks\base\core\java\android\app\ContextImpl.java中有这么一段
[cpp] view plain copy
- registerService(POWER_SERVICE, new ServiceFetcher() {
- public Object createService(ContextImpl ctx) {
- IBinder b = ServiceManager.getService(POWER_SERVICE);
- IPowerManager service = IPowerManager.Stub.asInterface(b);
- return new PowerManager(service, ctx.mMainThread.getHandler());
- }});
可 以知道 b = ServiceManager.getService(POWER_SERVICE);得到了PowerManagerService这个服 务,service = IPowerManager.Stub.asInterface(b);然后将这个sevice的指针传给PowerManager()的构造函数。也就 是说 这里的mService就是 PowerManagerService这个服务,因此这里的reboot也就是PowerManagerService的 reboot 我们来看它的代码在:\frameworks\base\services\java\com\android\server \PowerManagerService.java中
[cpp] view plain copy
- /**
- * Reboot the device immediately, passing 'reason' (may be null)
- * to the underlying __reboot system call. Should not return.
- */
- public void reboot(String reason)
- {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
-
- if (mHandler == null || !ActivityManagerNative.isSystemReady()) {
- throw new IllegalStateException("Too early to call reboot()");
- }
- //建立一个 shutdown线程来执行整个关机过程
- final String finalReason = reason;
- Runnable runnable = new Runnable() {
- public void run() {
- synchronized (this) {
- ShutdownThread.reboot(mContext, finalReason, false);
- }
-
- }
- };
- // ShutdownThread must run on a looper capable of displaying the UI.
- //关机时的UI显示
- mHandler.post(runnable);
-
- // PowerManager.reboot() is documented not to return so just wait for the inevitable.
- synchronized (runnable) {
- while (true) {
- try {
- runnable.wait();
- } catch (InterruptedException e) {
- }
- }
- }
- }
下面我们就来看shutdown线程的 reboot函数:\frameworks\base\core\java\com\android\internal\app\ShutdownThread.java
[cpp] view plain copy
- /**
- * Request a clean shutdown, waiting for subsystems to clean up their
- * state etc. Must be called from a Looper thread in which its UI
- * is shown.
- *
- * @param context Context used to display the shutdown progress dialog.
- * @param reason code to pass to the kernel (e.g. "recovery"), or null.
- * @param confirm true if user confirmation is needed before shutting down.
- */
- public static void reboot(final Context context, String reason, boolean confirm) {
- mReboot = true; //吧reboot设置为true
- mRebootReason = reason;
- shutdown(context, confirm);//关机之前的 系统清理,保存好所有的数据
- }
这里要进一步执行的话靠的是mReboot=true;这一句,具体的执行过程在ShutdownThread:run()函数中
[cpp] view plain copy
- /**
- * 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() {
- 。。。。。。。。。。。。。
- 。。。。。。。。。。。。。
-
- rebootOrShutdown(mReboot, mRebootReason);
- }
继续看:\frameworks\base\core\java\com\android\internal\app\ShutdownThread.java
[cpp] view plain copy
- /**
- * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
- * or {@link #shutdown(Context, boolean)} instead.
- *
- * @param reboot true to reboot or false to shutdown
- * @param reason reason for reboot
- */
- public static void rebootOrShutdown(boolean reboot, String reason) {
- if (reboot) {
- Log.i(TAG, "Rebooting, reason: " + reason);
- try {
- Power.reboot(reason);
- } catch (Exception e) {
- Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
- }
- } else if (SHUTDOWN_VIBRATE_MS > 0) {
- // vibrate before shutting down
- Vibrator vibrator = new Vibrator();
- try {
- vibrator.vibrate(SHUTDOWN_VIBRATE_MS);//关机震动一下,如果是reboot就不震动
- } 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) {
- }
- }
-
- // Shutdown power
- Log.i(TAG, "Performing low-level shutdown...");
- Power.shutdown();
- }
这里最终调用的是Power.reboot(reason);代码在\frameworks\base\core\java\android\os\Power.java
[cpp] view plain copy
- /**
- * Reboot the device.
- * @param reason code to pass to the kernel (e.g. "recovery"), or null.
- *
- * @throws IOException if reboot fails for some reason (eg, lack of
- * permission)
- */
- public static void reboot(String reason) throws IOException
- {
- rebootNative(reason);
- }
这里到了jni层 代码在\frameworks\base\core\jni\android_os_Power.cpp
[cpp] view plain copy
- static void android_os_Power_reboot(JNIEnv *env, jobject clazz, jstring reason)
- {
- if (reason == NULL) {
- android_reboot(ANDROID_RB_RESTART, 0, 0);
- } else {
- const char *chars = env->GetStringUTFChars(reason, NULL);
- android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);
- env->ReleaseStringUTFChars(reason, chars); // In case it fails.
- }
- jniThrowIOException(env, errno);
- }
继续看这里调用的android_reboot()在:\system\core\libcutils\android_reboot.c
[cpp] view plain copy
- int android_reboot(int cmd, int flags, char *arg)
- {
- int ret;
-
- if (!(flags & ANDROID_RB_FLAG_NO_SYNC))
- sync();
-
- if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO))
- 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;
- }
剩下的我们是要看__reboot()这个函数了,这个函数其实是一个系统调用,调用的linux内核reboot函数它的实现在\android40\bionic\libc\arch-arm\syscalls\__reboot.S
[cpp] view plain copy
- ENTRY(__reboot)
- .save {r4, r7}
- stmfd sp!, {r4, r7}
- ldr r7, =__NR_reboot
- swi #0
- ldmfd sp!, {r4, r7}
- movs r0, r0
- bxpl lr
- b __set_syscall_errno
- END(__reboot)
这里__NR_reboot 定义为88 也就是说它是linux系统调用列表里的第88个函数,这样我们就可以去kernel里找这个函数的定义了
系统调用的原理这里就不多讲了,可以百度一下很容易找到,最终得到的reboot函数在\kernel_imx\kernel\sys.c中
[cpp] view plain copy
- SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
- void __user *, arg)
- {
- char buffer[256];
- int ret = 0;
-
- /* We only trust the superuser with rebooting the system. */
- if (!capable(CAP_SYS_BOOT))
- return -EPERM;
-
- /* For safety, we require "magic" arguments. */
- if (magic1 != LINUX_REBOOT_MAGIC1 ||
- (magic2 != LINUX_REBOOT_MAGIC2 &&
- magic2 != LINUX_REBOOT_MAGIC2A &&
- magic2 != LINUX_REBOOT_MAGIC2B &&
- magic2 != LINUX_REBOOT_MAGIC2C))
- return -EINVAL;
-
- /* Instead of trying to make the power_off code look like
- * halt when pm_power_off is not set do it the easy way.
- */
- if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
- cmd = LINUX_REBOOT_CMD_HALT;
-
- mutex_lock(&reboot_mutex);
- switch (cmd) {
- 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;
-
- #ifdef CONFIG_KEXEC
- case LINUX_REBOOT_CMD_KEXEC:
- ret = kernel_kexec();
- break;
- #endif
-
- #ifdef CONFIG_HIBERNATION
- case LINUX_REBOOT_CMD_SW_SUSPEND:
- ret = hibernate();
- break;
- #endif
-
- default:
- ret = -EINVAL;
- break;
- }
- mutex_unlock(&reboot_mutex);
- return ret;
这里下一步调用的是kernel_restart()代码在本文件中
[cpp] view plain copy
- /**
- * kernel_restart - reboot the system
- * @cmd: pointer to buffer containing command to execute for restart
- * or %NULL
- *
- * Shutdown everything and perform a clean reboot.
- * This is not safe to call in interrupt context.
- */
- void kernel_restart(char *cmd)
- {
- kernel_restart_prepare(cmd);
- if (!cmd)
- printk(KERN_EMERG "Restarting system.\n");
- else
- printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);
- kmsg_dump(KMSG_DUMP_RESTART);
- machine_restart(cmd);
- }
下一步调用machine_restart()代码在\kernel_imx\arch\arm\kernel\process.c中
[cpp] view plain copy
- void machine_restart(char *cmd)
- {
- machine_shutdown();
- arm_pm_restart(reboot_mode, cmd);
- }
下一步看arm_pm_restart(reboot_mode, cmd);,在本文件中有这个的定义void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart;
也就是这个函数指针指向了arm_machine_restart,它的代码在本文件中
[cpp] view plain copy
- void arm_machine_restart(char mode, const char *cmd)
- {
-
-
- //ar_mode(mode,cmd); //lijianzhang
- /* Flush the console to make sure all the relevant messages make it
- * out to the console drivers */
- arm_machine_flush_console();
-
- /* Disable interrupts first */
- local_irq_disable();
- local_fiq_disable();
-
- /*
- * Tell the mm system that we are going to reboot -
- * we may need it to insert some 1:1 mappings so that
- * soft boot works.
- */
- setup_mm_for_reboot(mode);
-
- /* Clean and invalidate caches */
- flush_cache_all();
-
- /* Turn off caching */
- cpu_proc_fin();
-
- /* Push out any further dirty data, and ensure cache is empty */
- flush_cache_all();
-
- /*
- * Now call the architecture specific reboot code.
- */
- arch_reset(mode, cmd);
-
- /*
- * Whoops - the architecture was unable to reboot.
- * Tell the user!
- */
- mdelay(1000);
- printk("Reboot failed -- System halted\n");
- while (1);
- }
这 里执行reboot的函数是arch_reset(mode, cmd),在执行arch_reset的代码都是关闭系统和cpu一些东西,其中包括了mmu,关闭了mmu以后,访问寄存器必须直接使用寄存器地 址,ioremap这个函数就不能再使用了,同样的,由于mmu关闭了 ,这个函数下面就是printk是打印不出来的,字符过多还会报出内核错误,因此这里虽然写了printk 可以打印出reboot错误,其实也是打不出来的,这点gun官网上也有说明,网上有人修改了内核,在关闭mmu以后将printk缓存替换成物理地址, 这样才能正常使用。
好了下面来看arch_reset(mode, cmd); \kernel_imx\arch\arm\plat-mxc\system.c
[cpp] view plain copy
- /*
- * Reset the system. It is called by machine_restart().
- */
- void arch_reset(char mode, const char *cmd)
- {
- unsigned int wcr_enable;
-
- arch_reset_special_mode(mode, cmd);
-
- #ifdef CONFIG_ARCH_MX6
- /* wait for reset to assert... */
- #ifdef CONFIG_MX6_INTER_LDO_BYPASS
- wcr_enable = 0x14; /*reset system by extern pmic*/
- #else
- wcr_enable = (1 << 2);
- #endif
-
- __raw_writew(wcr_enable, wdog_base);
- /*errata TKT039676, SRS bit may be missed when
- SRC sample it, need to write the wdog controller
- twice to avoid it */
- __raw_writew(wcr_enable, wdog_base/*MX6Q_WDOG1_BASE_ADDR*/);
-
- /* wait for reset to assert... */
- mdelay(500);
-
- printk(KERN_ERR "Watchdog reset failed to assert reset\n");
-
- return;
- #endif
-
- #ifdef CONFIG_MACH_MX51_EFIKAMX
- if (machine_is_mx51_efikamx()) {
- mx51_efikamx_reset();
- return;
- }
- #endif
-
- if (cpu_is_mx1()) {
- wcr_enable = (1 << 0);
- } else {
- struct clk *clk;
-
- clk = clk_get_sys("imx2-wdt.0", NULL);
- if (!IS_ERR(clk))
- clk_enable(clk);
- wcr_enable = (1 << 2);
- }
-
- /* Assert SRS signal */
- __raw_writew(wcr_enable, wdog_base);
-
- /* wait for reset to assert... */
- mdelay(500);
-
- printk(KERN_ERR "Watchdog reset failed to assert reset\n");
-
- /* delay to allow the serial port to show the message */
- mdelay(50);
-
- /* we'll take a jump through zero as a poor second */
- }
好了这里看arch_reset_special_mode(mode, cmd); 这个函数就是进入各个模式,与uboot通信的地方,来看代码
[cpp] view plain copy
- static void arch_reset_special_mode(char mode, const char *cmd)
- {
- if (strcmp(cmd, "download") == 0)
- do_switch_mfgmode();
- else if (strcmp(cmd, "recovery") == 0)
- do_switch_recovery();
- else if (strcmp(cmd, "fastboot") == 0)
- do_switch_fastboot();
- else if (strcmp(cmd, "bootloader") == 0)
- do_switch_bootloader();
- }
这里可以看到内核支持4个模式,每个模式都有相应的函数,我们来贴一个recovery的,在\kernel_imx\arch\arm\mach-mx6\system.c
[cpp] view plain copy
- void do_switch_recovery(void)
- {
- u32 reg;
-
- reg = __raw_readl((SRC_BASE_ADDR + SRC_GPR10));
- reg |= ANDROID_RECOVERY_BOOT;
- __raw_writel(reg, (SRC_BASE_ADDR + SRC_GPR10));
- }
原理是把一个寄存器的值的一位置高,这个寄存器在复位过程中是不会被清零的,重启以后,uboot可以检测这一位来确定启动的模式,
过了这个函数,下面执行__raw_writew(wcr_enable, wdog_base); 这个是打开看门狗,可以知道系统重启最后就是利用看门狗,产生复位信号,来重启的。
到了这里reboot的流程就走完了,下一步就是正常启动了,详细的启动过程参见我的另一篇文章 http://blog.csdn.net/dkleikesa/article/details/9792747
下面来说明一个小技巧,如果要重启进入各个模式,是可以由命令来实现的,也就是"reboot recovery"进入recovery模式 “reboot fastboot”是进入fastboot模式