根据前文所述,恢复出厂设置在Settings界面点击后,最终可以视为是通过如下两条命令来执行的最终操作:

adb shell 'echo "--wipe_data\n--locale=en_US" > /cache/recovery/command'

adb shell setprop sys.powerctl reboot,recovery

接下来就从这两条命令开始描述。

属性处理

上边设置的属性实际上是在/init.rc中进行触发的:

on property:sys.powerctl=*

powerctl ${sys.powerctl}

在源码中对应的文件为device/rockchip/rksdk/recovery/etc/init.rc。可以看到,该属性设置后, 就相当于调用了命令powerctl ${sys.powerctl},对应于参数reboot,recovery,则最终命令为powerctl reboot,recover。

处理流程

底层处理流程

do_powerctl

这里不再描述Android的系统属性处理流程,直接从处理代码开始。可以看到,命令行powerctl reboot,recover对应的处理函数位于文件system/core/init/builtins.c,对应的函数为do_powerctl:

int do_powerctl(int nargs, char **args)

{

char command[PROP_VALUE_MAX];

int res;

int len = 0;

int cmd = 0;

char *reboot_target;

res = expand_props(command, args[1], sizeof(command));

if (res) {

ERROR("powerctl: cannot expand '%s'\n", args[1]);

return -EINVAL;

}

if (strncmp(command, "shutdown", 8) == 0) {

cmd = ANDROID_RB_POWEROFF;

len = 8;

} else if (strncmp(command, "reboot", 6) == 0) {

cmd = ANDROID_RB_RESTART2;

len = 6;

} else {

ERROR("powerctl: unrecognized command '%s'\n", command);

return -EINVAL;

}

if (command[len] == ',') {

reboot_target = &command[len + 1];

} else if (command[len] == '\0') {

reboot_target = "";

} else {

ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);

return -EINVAL;

}

return android_reboot(cmd, 0, reboot_target);

}

这段代码的核心思想就是对传入的命令行参数进行拆分处理,根据前边描述的命令行,则最终调用到的函数为android_reboot(ANDROID_RB_RESTART2, 0, "recovery")。

android_reboot

函数android_reboot实现在文件system/core/libcutils/android_reboot.c中:

int android_reboot(int cmd, int flags, char *arg)

{

int ret;

sync();

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_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, "recovery")。

__reboot

函数__reboot定义在文件bionic/libc/include/sys/reboot.h内:

extern int __reboot(int, int, int, void *);

具体实现实现在文件bionic/libc/arch-arm/syscalls/__reboot.S中:

ENTRY(__reboot)

mov ip, r7

ldr r7, =__NR_reboot

swi #0

mov r7, ip

cmn r0, #(MAX_ERRNO + 1)

bxls lr

neg r0, r0

b __set_errno

END(__reboot)

根据定义在文件kernel/include/uapi/asm-generic/unistd.h中的如下内容可知:

#define __NR_reboot 142

__SYSCALL(__NR_reboot, sys_reboot)

这里对__NR_reboot的调用,事实上是在调用文件kernel/kernel/sys.c中的函数sys_reboot()。

sys_reboot

考虑到函数sys_reboot定义在文件include/linux/syscalls.h:

asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user * arg);

则最终的调用为sys_reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, "recovery"),又根据前文可知,该函数实现在文件kernel/kernel/sys.c中:

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,

void __user *, arg)

{

struct pid_namespace *pid_ns = task_active_pid_ns(current);

char buffer[256];

int ret = 0;

/* We only trust the superuser with rebooting the system. */

if (!ns_capable(pid_ns->user_ns, 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;

/*

* If pid namespaces are enabled and the current task is in a child

* pid_namespace, the command is handled by reboot_pid_ns() which will

* call do_exit().

*/

ret = reboot_pid_ns(pid_ns, cmd);

if (ret)

return ret;

/* 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;

}

这里的主要操作如下:

因为只有超级用户有权限执行重启操作,因此首先判断的是进程的所有者是否为超级用户,是超级用户则执行重启操作,否则不作为而直接退出;

当然因为重启操作是个大工程,因此还要校验魔数,不符的话,还是不能执行重启操作;

检查是否由该接口处理重启操作;

根据输入命令判断具体执行的操作,当在recovery模式时,最终调用的函数为kernel_restart('recovery');

kernel_restart

函数kernel_restart实现在文件kernel/kernel/sys.c中:

void kernel_restart(char *cmd)

{

kernel_restart_prepare(cmd);

migrate_to_reboot_cpu();

syscore_shutdown();

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);

}

这里不再向下描述,而只在当前层做个简单描述,这里做了如下几件事:

调用kernel_restart_prepare完成如下任务:

调用blocking_notifier_call_chain接口向关心reboot事件的进程,发送SYS_RESTART事件,包括其中的参数recovery;

将系统状态设置为SYSTEM_RESTART;

调用usermodehelper_disable接口,禁止User mode helper;

调用device_shutdown,关闭所有设备;

调用migrate_to_reboot_cpu将当前进程移植到一个CPU上,在多CPU存在的情况下,无论哪个CPU触发了当前的系统调用,代码都可以运行在任意的CPU上,这个接口将代码分派到一个特定的CPU上,也就是说,这个接口被执行后,只有一个CPU在运行,泳衣完成后续的reboot动作;

调用syscore_shutdown关闭系统核心器件;

然后调用printk打印日志;

最后调用到machine_restart执行重启操作;

machine_restart

machine_restart实现在文件kernel/arch/arm/kernel/process.c中:

void machine_restart(char *cmd)

{

local_irq_disable();

smp_send_stop();

/* Flush the console to make sure all the relevant messages make it

* out to the console drivers */

arm_machine_flush_console();

arm_pm_restart(reboot_mode, cmd);

/* Give a grace period for failure to restart of 1s */

mdelay(1000);

/* Whoops - the platform was unable to reboot. Tell the user! */

printk("Reboot failed -- System halted\n");

local_irq_disable();

while (1);

}

对于多CPU的机器而言,在重启之前需要保证其他的CPU都处于非活动状态,由其中的一个CPU来负责重启动作即可。另外必须实现一个基于硬件的重启操作,以保证所有的CPU同步重启。该函数中具体执行了如下这些任务:

调用local_irq_disable屏蔽当前CPU上的所有中断,通过操作arm核心中的寄存器来屏蔽到达CPU上的中断,此时中断控制器上所有送往该CPU的中断信号都将被忽略;

调用smp_send_stop确保其他CPU处于非活动状态;

调用arm_machine_flush_console将相关信息输出到终端设备;

调用arm_pm_restart执行重启操作;

调用mdelay等待一段时间以进行优雅的重启;

调用printk重启失败则输出信息;

调用local_irq_disable屏蔽当前CPU上的所有中断;

调用while(1)重启失败,就死在这里咯;

rk3288_restart

根据上边的描述,真正的重启是在函数arm_pm_restart中进行的,而该函数实际上是个函数指针,在系统初始化时赋值的,其对应的函数为rk3288_restart,在文件kernel/arch/arm/mach-rockchip/rk3288.c中实现:

static void rk3288_restart(char mode, const char *cmd)

{

u32 boot_flag, boot_mode;

rockchip_restart_get_boot_mode(cmd, &boot_flag, &boot_mode);

writel_relaxed(boot_flag, RK_PMU_VIRT + RK3288_PMU_SYS_REG0); // for loader

writel_relaxed(boot_mode, RK_PMU_VIRT + RK3288_PMU_SYS_REG1); // for linux

dsb();

/* pll enter slow mode */

writel_relaxed(0xf3030000, RK_CRU_VIRT + RK3288_CRU_MODE_CON);

dsb();

writel_relaxed(0xeca8, RK_CRU_VIRT + RK3288_CRU_GLB_SRST_SND_VALUE);

dsb();

}

首先调用rockchip_restart_get_boot_mode,根据输入的recovery设置boot_flag的值为SYS_LOADER_REBOOT_FLAG + BOOT_FASTBOOT,boot_mode的值为BOOT_MODE_REBOOT;再调用writel_relaxed将boot_flag写入地址RK3288_PMU_SYS_REG0,将boot_mode写入地址RK3288_PMU_SYS_REG1;

到此就完成了重启的上半部分,也就是关机操作的内容,接下来就是系统启动了。

android4.4 恢复出厂设置,Android4.4.2恢复出厂设置(三)相关推荐

  1. win10计算机恢复出厂设置,Windows 10 一键恢复出厂设置详细教程

    当Windows10系统使用久了,体积越来越大,运行卡顿缓慢,就会选择重装系统.其实Windows10内置了一键恢复出厂设置工具,我们可以使用此方法先解决系统问题哦!接下来,我就教大家Windows1 ...

  2. w10恢复出厂设置_路由器如何恢复出厂设置

    出厂设置是将设备恢复到出厂时的默认状态,清除保存的参数设置,可以帮助我们解决出现代一些小问题.那么路由器如何恢复出厂设置呢?接下来,我就将路由器恢复出厂设置的方法分享给你们 路由器是连接两个以上网络的 ...

  3. 计算机重启恢复系统怎么操作,电脑如何恢复出厂设置 电脑开机怎么一键还原...

    很多朋友都会问电脑如何恢复出厂设置,电脑开机后怎么一键还原,其实电脑恢复出厂设置就是重做系统或者将BIOS的恢复出厂,小编今天也给大家介绍电脑如何恢复出厂设置以及电脑开机怎么一键还原的相关知识,一起来 ...

  4. 计算机系统如何恢复出厂设置路由器,怎么让路由器恢复出厂设置?路由器怎么恢复默认设置?...

    怎么让路由器恢复出厂设置?路由器恢复出厂设置有两种方法,要么长按复位键,要么在路由器设置中按照流程恢复出厂设置,前者有一个弊端,不容易连接上计算机,后者则需要登录路由器之后才能恢复,如果忘记密码,只能 ...

  5. android刷机恢复出厂设置吗,安卓手机恢复出厂设置和双清有什么区别?

    1.恢复出厂设置是:把手机恢复到你刚买到手时候的状态,手机里的软件设置等都恢复到出厂时候的,后来设置都取消.但是内存卡里的东西还在. 2.双清指的是:清理手机内的用户数据和储存里的东西,可以有效的提高 ...

  6. android 恢复出厂 自动恢复文件夹,基于Android系统快速恢复出厂设置方法的实现...

    龚强 摘 要:针对使用Android系统的智能电视进行恢复出厂设置时重置速度慢的情况进行了研究和分析,从其重置原理入手,通过简化备份.导入.执行等设置方法以实现该系统的快速恢复出厂设置.实践证明,该方 ...

  7. vivo手机计算机恢复出厂设置,vivo手机系统恢复出厂设置里面清除所有数据

    "哎呀,手机怎么又卡了!完了这局农药又要输了..." "这个破手机,才用了一年就卡的不要不要的了." "是时候要通过恢复出厂设置来解决了~!" ...

  8. 华为7c手机怎么恢复出厂设置_华为手机恢复出厂设置在哪里。

    展开全部 1.打开华为手机主来界面:源 2.在手机桌面上找到"设bai置",并点击一下打开du:zhi 3.设置页面dao打开: 4.向下滑动设置页面,找到"高级设置&q ...

  9. 计算机怎么恢复初始设定模式,将电脑恢复出厂设置的4种方法,简单易操作,值得收藏...

    当计算机受到恶意软件感染.运行缓慢或显示软件无法解决的令人担忧的错误消息时,恢复出厂设置是最后的手段. Windows 10附带了内置的恢复选项,你可以在不删除所有文件的情况下来重置电脑. 下面我们将 ...

  10. dell主板恢复出厂设置_戴尔dell电脑恢复出厂设置教程(BIOS恢复出厂设置,新版台式机)...

    本博客只要总结戴尔dell最新版的台式电脑恢复出厂设置.戴尔dell恢复出厂设置有许多方法,可以直接在win10系统里面进行恢复,也可以通过BIOS恢复出厂设置.其中,本文是从BIOS进行恢复的,下面 ...

最新文章

  1. RHEL-5搭建SSH服务器
  2. 恢复误删数据(SQL Server 2000)--Log Explorer
  3. ElementUI el-table 在flex下的宽度自适应问题
  4. centos7启动与切换图形界面
  5. Nginx配置参数说明
  6. jenkins学习笔记2-在centos中安装jenkins master测试环境
  7. Supermap 组合单值专题图与标签专题图演示样例
  8. 云计算将会让数据中心消失?
  9. php写linux应用程序,Linux应用程序使用写文件调试程序的方法
  10. ecshop flow.php goods_number,修复ECSHOP一重要BUG,当商品设置数量优惠时,加入不同属性的商品数量优惠判断错误...
  11. Python3.6 deep learning first step
  12. js获取页面传来参数的方法
  13. hisicv200 exfat支持(转)
  14. 本周进步要点(第3周1.9--1.15)
  15. vueRouter使用心得
  16. Jboss jar包冲突及jar加载顺序
  17. centos6.5 tomcat开机启动
  18. 我奋斗了18年,不是为了和你一起喝咖啡。
  19. (26)基于cookie的登陆认证(写入cookie、删除cookie、登陆后所有域下的网页都可访问、登陆成功跳转至用户开始访问的页面、使用装饰器完成所有页面的登陆认证)...
  20. 北斗导航 | 两个地面站之间的多跳卫星通信链路(附matlab代码)

热门文章

  1. 古人犯了重罪在脸上刻字,是什么字?
  2. 最长回文子串和最长回文子序列
  3. android 适配最佳方案,android适配方案
  4. JavaScript 鼠标坐标、元素尺寸、浏览器尺寸及坐标
  5. 凤变冰入门 #2560
  6. 一些CSS文本框样式
  7. 企业等保测评的必要性
  8. 名帖338 张旭 草书《李青莲序》
  9. PMP | 备考 合同管理类型
  10. C# word中添加横线