这里所说的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系统reboot的更多相关文章

  1. Android 系统 reboot

    /*********************************************************************** * Android 系统 reboot * 说明: * ...

  2. Android系统在超级终端下必会的命令大全(adb shell命令大全)

    . 显示系统中全部Android平台: android list targets . 显示系统中全部AVD(模拟器): android list avd . 创建AVD(模拟器): android c ...

  3. Android系统Recovery工作原理之使用update.zip升级过程---updater-script脚本语法简介以及执行流程(转)

    目前update-script脚本格式是edify,其与amend有何区别,暂不讨论,我们只分析其中主要的语法,以及脚本的流程控制. 一.update-script脚本语法简介: 我们顺着所生成的脚本 ...

  4. Android系统关机或几种方式重启

    ---------------------------------------------------------------------------------------------------- ...

  5. 【Android 系统开发】CyanogenMod 13.0 源码下载 编译 ROM 制作 ( 手机平台 : 小米4 | 编译平台 : Ubuntu 14.04 LTS 虚拟机)

                 分类: Android 系统开发(5)                                              作者同类文章X 版权声明:本文为博主原创文章 ...

  6. Android系统--输入系统(九)Reader线程_核心类及配置文件

    Android系统--输入系统(九)Reader线程_核心类及配置文件 1. Reader线程核心类--EventHub 1.1 Reader线程核心结构体 实例化对象:mEventHub--表示多个 ...

  7. Android系统--输入系统(十五)实战_使用GlobalKey一键启动程序

    Android系统--输入系统(十五)实战_使用GlobalKey一键启动程序 1. 一键启动的过程 1.1 对于global key, 系统会根据global_keys.xml发送消息给某个组件 & ...

  8. 【Android 系统开发】 编译 Android文件系统 u-boot 内核 并烧写到 OK-6410A 开发板上

    博客地址 : http://blog.csdn.net/shulianghan/article/details/40299813  本篇文章中用到的工具源码下载 : -- ok-6410A 附带的 A ...

  9. Android 系统工具类

    系统工具类 public class systemUtil { //隐藏ipad底部虚拟按键栏 @RequiresApi(api = Build.VERSION_CODES.KITKAT) publi ...

随机推荐

  1. GNOME界面简单使用

    GNOME界面 CentOS下的文件夹打开方式,默认是打开一个文件夹就重新的打开一个窗口,并不是在原有的文件夹中显示要打开文件夹的内容. 怎么修改: 打开任意一个文件夹. Edit --> pr ...

  2. 转换函数CONVERSION_EXIT_TSTRN_OUTPUT

    CONVERSION_EXIT_TSTRN_OUTPUT 在路线表TVRO中字段TDVZND 运输提前时间,取出来的数值没有转换,需要此函数进行转换.如14400,000  转换后为14,400:00 ...

  3. 通过程序预览Office文档

    我承认,标题是夸大了,就是为了吸引注意力.这里只有Word文档和Excel文档的预览代码. Word://此部分来源:http://princed.mblogger.cn/posts/11885.as ...

  4. redis+tomcat共享session问题(转)

    为了让楼主看看Java开发是多么的简单和轻松,你说你耗时一周都没搞好,简直就是胡说八道!!我从没搞过reids和tomcat整合的(我做的项目一直都是去session用cookie,为了验证你在胡说八 ...

  5. How to find configuration file MySQL uses?(转)

    http://www.dbasquare.com/2012/04/01/how-to-find-mysql-configuration-file/ A customer called me today ...

  6. UVA 529 Addition Chains(迭代搜索)

      Addition Chains  An addition chain for n is an integer sequence  with the following four propertie ...

  7. Swift - 文本输入框(UITextField)的用法

    1,文本框的创建,有如下几个样式: UITextBorderStyle.None:无边框 UITextBorderStyle.Line:直线边框 UITextBorderStyle.RoundedRe ...

  8. http2.0笔记

    二进制分帧层 定义了如何封装 HTTP 消息并在客户端与服务器之间传输 http2.0的消息传输特点: 流 已建立的连接上的双向字节流 消息 与逻辑消息对应的完整的一系列数据帧 帧 http2.0通信 ...

  9. JDK动态代理简单小程序

    Jdk动态代理 1.动态代理使用的情况:需要在多个方法上加上相同的逻辑的时候,需要用到动态代理. 原因:在多个方法上写相同的逻辑,第一费事,第二在不用的时候维护麻烦 使用动态代理需要用到两个类:分别为 ...

  10. POJ1291-并查集/dfs

    并查集 题意:找出给定的这些话中是否有冲突.若没有则最多有多少句是对的. /* 思路:如果第x句说y是对的,则x,y必定是一起的,x+n,y+n是一起的:反之x,y+n//y,x+n是一起的. 利用并 ...