android系统reboot

时间:2022-10-21 19:34:40

这里所说的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.****.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.****.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&period;zip升级过程---updater-script脚本语法简介以及执行流程&lpar;转&rpar;

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

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

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

  5. 【Android 系统开发】CyanogenMod 13&period;0 源码下载 编译 ROM 制作 &lpar; 手机平台 &colon; 小米4 &vert; 编译平台 &colon; Ubuntu 14&period;04 LTS 虚拟机&rpar;

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

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

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

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

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

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

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

  9. Android 系统工具类

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

随机推荐

  1. sharepoint 增删改查

    前端提交 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="MeetingOneW ...

  2. 安卓--Toast

    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWind ...

  3. C&num;功能杂集

    使用unsafe代码 Unsafe, fixed, stackalloc 由于C#可以使用元数据,验证函数签名.对象类型,保证执行过程的安全,如果要使用指针,则不能进行验证,用unsafe表示.uns ...

  4. MySQL记录

    1.unixtime和可读时间的转换 unixtime是距"1970-01-01 08:00:00"的时间秒数 unixtime -> readable select fro ...

  5. 一分钟可知css3版大白源码

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  6. Mindjet&period;MindManager&OpenCurlyDoubleQuote;参数错误”解决办法,适用于9&period;0、10&period;0和14&period;0

    MindManager出14.0版本了,但是在应用个别模板的时候会提示“参数错误”,然后自动关闭. 解决办法:   如果是win7系统,可以进入C:\Users\(用户名) \AppData\Loca ...

  7. rsyslog 传输mysql 日志

    在另外一种环境中,让我们假定你已经在机器上安装了一个名为"foobar"的应用程序,它会在/var/log下生成foobar.log日志文件.现在,你想要将它的日志定向到rsysl ...

  8. &lpar;转&rpar;B-树和B&plus;树的应用:数据搜索和数据库索引

    B-树 1 .B-树定义 B-树是一种平衡的多路查找树,它在文件系统中很有用. 定义:一棵m 阶的B-树,或者为空树,或为满足下列特性的m 叉树: ⑴树中每个结点至多有m 棵子树: ⑵若根结点不是叶子 ...

  9. Django 中间件 请求前

    中间件: class TestMiddleware(object): """中间件类""" def __init__(self): &quo ...

  10. hibernate一级缓存和快照

    摘自网络: Hibernate中的一级缓存的底层是以Map形式存在的,key是主键,value是对象,所以它的泛型为Map<Serializable,Object>,key的泛型为串行化是 ...