这里所说的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.)
*/
下面我们来看这个类里面的几个 函数
/**
* 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) {
}
}
}
}
/**
* 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可以检测这一位来确定启动的模式,
到了这里reboot的流程就走完了,下一步就是正常启动了,详细的启动过程参见我的另一篇文章 http://blog.csdn.net/dkleikesa/article/details/9792747
下面来说明一个小技巧,如果要重启进入各个模式,是可以由命令来实现的,也就是"reboot recovery"进入recovery模式 “reboot fastboot”是进入fastboot模式