android系统reboot

  这里所说的reboot指的是软件重启,并非断电重启。我们知道android系统的几个功能,比如:回复出厂设置、OTA升级等都需要重启系统,而且重启后要进入recovery模式,有的手机还带有重启进入fastboot或者其他模式。这些在软重启中式怎么做到的呢?

经过一段查找找到了这个文件:\frameworks\base\core\java\android\os\RecoverySystem.java

我们来看这个文件里面有一个类public class RecoverySystem  我们来看这个类的说明

 

/**

 * 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系统的回复包括安装系统更新,删除用户数据等。

 

下面我们来看这个类里面的几个 函数

 

 /**

     * 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重启就是由这个函数来实现的,

我们就来看

 

   /**

     * 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

 

public void reboot(String reason)

    {

        try {

            mService.reboot(reason);

        } catch (RemoteException e) {

        }

    }

这里mService的定义

 

 

   public PowerManager(IPowerManager service, Handler handler)

    {

        mService = service;

        mHandler = handler;

    }

是在构造函数里传进来的,我们继续来看这个参数的传送在\frameworks\base\core\java\android\app\ContextImpl.java中有这么一段

 

 

        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中

    /**

     * 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

    /**

     * 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()函数中

    /**

     * 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

 

 

    /**

     * 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

 

 

    /**

     * 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

 

 

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

 

 

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

 

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中

 

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()代码在本文件中

 

 

/**

 *	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中

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,它的代码在本文件中

 




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

/*

 * 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通信的地方,来看代码

 

 

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

 

 

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模式


 

你可能感兴趣的:(android)