• android系统reboot


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

    经过一段查找找到了这个文件:frameworksasecorejavaandroidosRecoverySystem.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是在回复出厂设置时候调用的,代码在frameworksaseservicesjavacomandroidserverMasterClearReceiver.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("
    ");
            } 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");

    在frameworksasecorejavaandroidosPowerManager.java

    public void reboot(String reason)
        {
            try {
                mService.reboot(reason);
            } catch (RemoteException e) {
            }
        }
    

    这里mService的定义

       public PowerManager(IPowerManager service, Handler handler)
        {
            mService = service;
            mHandler = handler;
        }

    是在构造函数里传进来的,我们继续来看这个参数的传送在frameworksasecorejavaandroidappContextImpl.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 我们来看它的代码在:frameworksaseservicesjavacomandroidserverPowerManagerService.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函数:frameworksasecorejavacomandroidinternalappShutdownThread.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);
        }

    继续看:frameworksasecorejavacomandroidinternalappShutdownThread.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);代码在frameworksasecorejavaandroidosPower.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层 代码在frameworksasecorejniandroid_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()在:systemcorelibcutilsandroid_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函数它的实现在android40ioniclibcarch-armsyscalls\__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_imxkernelsys.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] = '';
    
    		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.
    ");
    	else
    		printk(KERN_EMERG "Restarting system with command '%s'.
    ", cmd);
    	kmsg_dump(KMSG_DUMP_RESTART);
    	machine_restart(cmd);
    }
    

    下一步调用machine_restart()代码在kernel_imxarcharmkernelprocess.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
    ");
    	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_imxarcharmplat-mxcsystem.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
    ");
    
    	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
    ");
    
    	/* 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_imxarcharmmach-mx6system.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模式


  • 相关阅读:
    总结(1)--- 数据库
    在路上---学习篇(一)Python 数据结构和算法 (4) --希尔排序、归并排序
    在路上---学习篇(一)Python 数据结构和算法 (3) --快速排序
    在路上---学习篇(一)Python 数据结构和算法 (2) -- 冒泡排序、选择排序、插入排序
    javascript观察者模式
    es6对象的扩展
    es6数组的扩展
    vue全局API
    javascript原型链
    简单工厂,工厂方法模式
  • 原文地址:https://www.cnblogs.com/riskyer/p/3297113.html
Copyright © 2020-2023  润新知