recovery介绍

recovery是android的一种特殊模式,我称之为还原升级模式。这里从recovery的功能说起:
1.擦除用户数据
设置菜单中的恢复到出厂模式,即擦除用户数据
2.系统升级
设置菜单中的系统升级功能;OTA INSTALL,即使用update.zip包升级
recovery的详细功能在源码 bootable/recovery/recovery.c 文件的注释中有详细介绍

uboot、main system、recovery

将android系统分成三个部分:
1、Main system:用boot.img启动的Linux系统,Android的正常工作模式;
2、Recovery:用recovery.img启动的Linux系统,recovery模式;
3、uboot:引导程序,通常情况下是引导kernel uImage,挂载根文件系统,启动android镜像;也可以引导recovery.img,进入recovery模 式

这三个部分通信的两中方式:cache分区、BCB(bootloader control block)数据块
1.cache分区: main system –> recovery
cache分区主要是main system 与recovery通信,main system通过cache向recovery发送消息,控制recovery功能,如:升级或擦除数据。
通信主要通过cache下的三个文件
1)/cache/recovery/command
Main system传给Recovery的命令行,控制recovery的功能 ,如:升级或擦除数据。具体有以下几种:
–send_intent=anystring write the text out to recovery/intent
–update_package=root:path 系统升级,path表示升级包的路径
–wipe_data erase user data (and cache), then reboot
–wipe_cache wipe cache (but not user data), then reboot

2)/cache/recovery/log
存放 recvoery 过程的log。在recovery运行过程中,stdout及stderr会重定位到/tmp/recovery.log文件,Recovery退出之前会将其转储 到/cache/recovery/log中。

3)/cache/recovery/intent
Recovery传给Main system的信息

2.BCB(bootloader control block)数据块

struct bootloader_message { char command[32]; char status[32]; char recovery[1024];
}; 

BCB是Bootloader与Recovery的通信接口,也是Bootloader与Main system的通信接口,存储在flash中的MISC分区,占用三个page,各成员意义如下:
command:
当Main system想要重启进入recovery模式,或升级radio/bootloader firmware时,会更新这个域。
当firmware更新完毕,为了启动后进入recovery做最终的清除,bootloader还会修改它。
下面是command域的三种情况:
(1). command = “boot-recovery” 时,系统会进入Recovery模式。Recovery服务会具体根据/cache/recovery/command中的命令执行相应的操作
(例如,升级update.zip或擦除cache,data等)。
(2). command = “update-radia” 或 “update-hboot” 时,系统会进入更新firmware(更新bootloader),具体由bootloader完成。
(3). command为空时,即没有任何命令,系统会进入正常的启动,最后进入主系统(main system)。这种是最通常的启动流程。

status:
update-radio或update-hboot完成后,bootloader会写入相应的信息,一般是一些状态或执行结果。

recovery:
当Main system想要重启进入recovery模式,可能会更新这个域,注意:不是一定更新。必须以“recovery\n”开头,否则这个域的所有内容会被忽略。这一项的内容中“recovery/\n”以后的部分,是/cache/recovery/command支持的命令,可以认为这是在Recovery操作过程中,对命令操作的备份。
Recovery也会更新这个域的信息,执行某操作前把该操作命令写到recovery域,并更新command域,防止recovery过程被中断后系统不能正常启动。操作完成后再清空recovery域及command域,这样在进入Main system之前,就能确保操作被执行。

从android上层进入recvoery具体流程:

android部分:

当我们在设置中点击系统升级时,系统会起一个reboot线程,在线程中根据不同的功能调用RecoverySystem类的方法,目标文件:frameworks/base/services/java/com/android/server/MasterClearReceiver.java
注意:这里可能不同平台有所不同,但大体思想一致

    public void onReceive(final Context context, final Intent intent) {............Slog.w(TAG, "!!! FACTORY RESET !!!");// The reboot call is blocking, so we need to do it on another thread.Thread thr = new Thread("Reboot") {@Overridepublic void run() {try {if("from-sd".equals(intent.getStringExtra("update-type"))){RecoverySystem.rebootUpdateFromSD(context);  /*从sd卡升级*/}else if("from-udisk".equals(intent.getStringExtra("update-type"))){                       RecoverySystem.rebootUpdateFromUDisk(context);  /*从u盘升级*/}elseRecoverySystem.rebootWipeUserData(context);  /*擦除数据*/} catch (IOException e) {Slog.e(TAG, "Can't perform master clear/factory reset", e);}}};thr.start();}

RecoverySystem类定义于文件:frameworks/base/core/java/android/os/RecoverySystem.java

public class RecoverySystem {/*这里创建了cache/recovery 下的文件,用于向recovery传递消息*/private static File RECOVERY_DIR = new File("/cache/recovery");private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");private static File LOG_FILE = new File(RECOVERY_DIR, "log");private static String LAST_PREFIX = "last_";  /*最新的log 以last_开头*/public static void rebootWipeUserData(Context context) throws IOException {final ConditionVariable condition = new ConditionVariable();Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,android.Manifest.permission.MASTER_CLEAR,new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {condition.open();}}, null, 0, null, null);condition.block();/*擦除用户分区  走这里*/bootCommand(context, "--wipe_data\n--locale=" + Locale.getDefault().toString());}public static void rebootUpdateFromSD(Context context) throws IOException {   .............../*从sd卡升级  走这里*bootCommand(context, "--update_package=/ext_sdcard1/update.zip\n--locale=" + Locale.getDefault().toString());}public static void rebootUpdateFromUDisk(Context context) throws IOException {........../*从u盘升级  走这里*/bootCommand(context, "--update_package=/udisk1/update.zip\n--locale=" + Locale.getDefault().toString());}private static void bootCommand(Context context, String arg) throws IOException {RECOVERY_DIR.mkdirs();  // In case we need itCOMMAND_FILE.delete();  // In case it's not writableLOG_FILE.delete();FileWriter command = new FileWriter(COMMAND_FILE);try {command.write(arg);  /*到这里可以发现 无论要实现是什么功能,都是往cache/recvoery/command文件中写命令 */command.write("\n");} finally {command.close();}// Having written the command file, go ahead and rebootPowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);pm.reboot("recovery");  /*最后调用pm.reboot重启并进入recovery*/throw new IOException("Reboot failed (no permissions?)");}

注意:这里容易混淆,一个是进入以后干什么,一个是如何进入
1.调用bootCommand往往cache/recvoery/command文件中写命令是为了进入recovery后,由recovery解析命令,然后执行命令;
2.pm.reboot(“recovery”); 以及下面的所有操作都是为了重启系统,并进入recvoery模式;

PowerManager类定义于文件:frameworks/base/core/java/android/os/PowerManager.java

 public class PowerManager{...IPowerManager mService;Handler mHandler;public PowerManager(IPowerManager service, Handler handler){mService = service;mHandler = handler;}...public void reboot(String reason){try {mService.reboot(reason);  /*reason 是recvoery*/} catch (RemoteException e) {}}}

mService指向的是PowerManagerService类,这个类定义于文件:frameworks/base/services/java/com/android/server/power/PowerManagerService.java

public void reboot(boolean confirm, String reason, boolean wait) {mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);final long ident = Binder.clearCallingIdentity();try {shutdownOrRebootInternal(false, confirm, reason, wait);  /*走这里*/} finally {Binder.restoreCallingIdentity(ident);}}
shutdownOrRebootInternal(false, confirm, reason, wait);  参数为(false,false,"recovery",true)
private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,...Runnable runnable = new Runnable() {@Overridepublic void run() {synchronized (this) {if (shutdown) {   ShutdownThread.shutdown(mContext, confirm);} else {   /*shutdown应该为false 所以走这里, reason为recovery*/ShutdownThread.reboot(mContext, reason, confirm);}}}};

ShutdownThread类在文件:frameworks/base/services/java/com/android/server/power/ShutdownThread.java

public static void reboot(final Context context, String reason, boolean confirm) { //方法中的变量为全局变量mReboot = true;mRebootSafeMode = false;mRebootUpdate = false;mRebootReason = reason;shutdownInner(context, confirm);       //此方法是在手机界面上弹出一个确认框,是否重启}这里不再详细跟踪代码了,shutdownInner(context, confirm); -->rebootOrShutdown(mContext, mReboot, mRebootReason); --> PowerManagerService.lowLevelReboot(reason); reason为“recoery”

PowerManagerService.lowLevelReboot方法定义在文件:./frameworks/base/services/java/com/android/server/power/PowerManagerService.java

public static void lowLevelReboot(String reason) throws IOException {nativeReboot(reason);  /*这里说明调用了本地jni库  reason为recovery*/
}

注意:这里不同的平台可能有所不同,本地库的编写不可能都相同,仅做参考
nativeReboot定义在文件frameworks/base/services/jni/com_android_server_power_PowerManagerService.cpp

static void nativeReboot(JNIEnv *env, jclass clazz, jstring reason) {if (reason == NULL) { /*reason参数为空 正常启动*/android_reboot(ANDROID_RB_RESTART, 0, 0);} else {...android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);  /*chars 为 recovery*/}...
}

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();swich (cmd) {case ANDROID_RB_RESTART:  /*正常启动*/ret = reboot(RB_AUTOBOOT);break;case ANDROID_RB_POWEROFF: /*关机*/  ret = reboot(RB_POWER_OFF);break;case ANDROID_RB_RESTART2:  /*特殊启动 recvoery*/if(0 == strcmp(arg,"recovery")){writemisc();   /*这里是往misc分区写recovery命令行,在uboot中会检测misc分区中的BCB数据块*/ }/*这里是系统调用,参数LINUX_REBOOT_MAGIC1和LINUX_REBOOT_MAGIC2是个固定的整数,在内核中用来检验;arg为recovery*/    ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART2, arg);  break;default:ret = -1;}return ret;
}
/*在uboot中会检测BCB数据块,进入recovery模式*/
static int build_recovery_cmd()
{memcpy(bm.command, "boot-recovery", 14);   /*将"boot-recovery" 写入command域*/memcpy(bm.recovery, "recovery", 9);return 0;}static int writemisc(){int phy_block_size = 0;int index, fd=0;fd = open(MISC_PARTITION, O_RDWR);if (fd < 0) return -1;build_recovery_cmd();lseek(fd, 0x0, SEEK_SET);int cnt = write(fd, &bm, sizeof(bm));printf("Write %d bytes to misc partition\r\n", cnt);close(fd);return 0;
}

__reboot系统调用定义在:./bionic/libc/arch-arm/syscalls/__reboot.S

#include <sys/linux-syscalls.h>ENTRY(__reboot).save   {r4, r7}stmfd   sp!, {r4, r7}ldr     r7, =__NR_reboot  // 系统调用号 88, binoic/libc/include/sys/linux-syscalls.hswi     #0ldmfd   sp!, {r4, r7}movs    r0, r0bxpl    lrb       __set_syscall_errno
END(__reboot)

最终实现在内核中的__NR_reboot

内核部分

__NR_reboot定义在文件:kernel/include/asm-generic/unistd.h

#define __NR_reboot 142
__SYSCALL(__NR_reboot, sys_reboot)

__NR_reboot被映射到sys_reboot,最终的代码实现在文件:kernel/kernel/sys.c

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,void __user *, arg)
{char buffer[256];int ret = 0;...../* 这里说明了两个魔数的功能,注意两个地方的魔数定义必须相同*/if (magic1 != LINUX_REBOOT_MAGIC1 ||(magic2 != LINUX_REBOOT_MAGIC2 &&magic2 != LINUX_REBOOT_MAGIC2A &&magic2 != LINUX_REBOOT_MAGIC2B &&magic2 != LINUX_REBOOT_MAGIC2C))return -EINVAL;....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:   /*recovery重启走这里*/if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {ret = -EFAULT;break;}buffer[sizeof(buffer) - 1] = '\0';kernel_restart(buffer);  /*buffer 为 reovery*/break;....default:ret = -EINVAL;break;}mutex_unlock(&reboot_mutex);return ret;
}void kernel_restart(char *cmd) /*这里cmd 为 recovery*/
{kernel_restart_prepare(cmd);disable_nonboot_cpus();if (!cmd)printk(KERN_EMERG "Restarting system.\n");elseprintk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);kmsg_dump(KMSG_DUMP_RESTART);machine_restart(cmd);
}
EXPORT_SYMBOL_GPL(kernel_restart);

machine_restart定义文件为:kernel/arch/arm/kernel/process.c

void machine_restart(char *cmd)
{...arm_pm_restart(reboot_mode, cmd);  /*走这里,cmd 为 recovery*/....
}

最终是调用arch_reset(mode,cmd),重启系统;cmd为传入的启动参数
目标文件在平台配置文件中,例如:arch/arm/mach-prima2/include/mach/system.h

/********************
这里只是让系统重启,没有其他操作;
*********************/
void arch_reset(char mode, const char *cmd)
{/** use powerdown watch dog to reset system*/uint32_t u4Test;PDWNC_WRITE32(REG_RW_RESRV1, 0x33633363);PDWNC_WRITE32(REG_RW_WDT, 0xffd00000);for(u4Test = 0; u4Test < 10000; u4Test++){}PDWNC_WRITE32(REG_RW_WDTSET, 1);while(1);}

各个平台有不同的实现方式,有的是直接设置寄存器,进入recovery;
这里是通过设置BCB进入reovery,在前面已经分析了
接下来可以想象在uboot中肯定会去检测BCB数据块,然后加载recoery镜像,进入recovery模式

uboot部分

uboot中会先后检查三种方式进入recovery是否成立:第一种是kernel直接写一个寄存器来标记下次启动将进入recovery模式;第二种是快捷键:powerkey+downVOL;第三中就是上层应用发送下来的command命令,这个命令在系统重启之前会往MISC分区中
command(“boot-recovery”)。这里采用的是第三中方式;
uboot检查代码在文件:uboot-83xx/lib_arm/board.c如果没有,就在该文件中搜索“recovery”,应该会有相关函数

BOOL recovery_check_command_trigger(void)
{struct misc_message misc_msg;struct misc_message *pmisc_msg = &misc_msg;const unsigned int size = NAND_WRITE_SIZE * MISC_PAGES;unsigned char *pdata;int ret;pdata = (uchar*)malloc(sizeof(uchar)*size);ret = mboot_recovery_load_misc(pdata, size);if (ret < 0){return FALSE; } memcpy(pmisc_msg, &pdata[NAND_WRITE_SIZE * MISC_COMMAND_PAGE], sizeof(misc_msg)); MSG("Boot command: %.*s\n", sizeof(misc_msg.command), misc_msg.command);MSG("Boot status: %.*s\n", sizeof(misc_msg.status), misc_msg.status);MSG("Boot message\n\"%.20s\"\n", misc_msg.recovery);if(strcmp(misc_msg.command, "boot-recovery")==0)  /*判断command域中的值,如果是"boot-recovery",就进入  recvoery*/{ g_boot_mode = RECOVERY_BOOT;}return TRUE;
}

该到此结束!
进入recvoery的相关流程,下文分析。

Android上层进入recovery流程相关推荐

  1. Android 8.0 recovery 流程分析

    这里主要分析non A/B模式下的recovery流程 A/B模式下的recovery在boot中 后续会不断补充,如果有疏漏或者错误的地方,请指出,共同学习,谢谢! 一.流程分析 首先列出recov ...

  2. Android 上层蓝牙enable流程

    Android 蓝牙框架 基于Android9.0 Amlogic代码分析, 蓝牙应用通过 Binder 与蓝牙进程进行通信.蓝牙进程使用 JNI 与蓝牙堆栈通信,并向开发者提供对各种蓝牙配置文件的访 ...

  3. Android 8.0 学习(23)---recovery 流程分析

    Android 8.0 recovery 流程分析 这里主要分析non A/B模式下的recovery流程  A/B模式下的recovery在boot中  后续会不断补充,如果有疏漏或者错误的地方,请 ...

  4. android上层应用apk到G-sensor driver的大致流程

    android上层应用apk到G-sensor driver的大致流程: Android HAL层,即硬件抽象层,是Google响应厂家"希望不公开源码"的要求推出的新概念 1,源 ...

  5. Android 12 关机重启流程

    文章托管在gitee上 Android Notes , 同步csdn 本文基于Android12 分析 关机流程 Android上层触发关机的入口很多,但最终几乎都是调用ShutdownThread. ...

  6. Android系统开机启动流程及init进程浅析

    Android系统启动概述 Android系统开机流程基于Linux系统,总体可分为三个阶段: Boot Loader引导程序启动 Linux内核启动 Android系统启动,Launcher/app ...

  7. Recovery 流程简介

    Recovery 流程简介 Adroid 系统中的 Recovery 模式主要为用户提供了 OTA 升级和恢复出厂设置两大功能,用户可以通过 setting 中的系统软件更新和恢复出厂设置来进入到 r ...

  8. 结合源码探讨Android系统的启动流程

    结合源码探讨Android系统的启动流程 由于本人能力有限,所考虑或者疏忽错漏的地方或多或少应该存在.同时,Android从启动过程开始,实际上就涉及多个技术难点和多种通信机制的知识点. 基于上面两个 ...

  9. Android Q 开机启动流程

    https://www.it610.com/article/1304931662924124160.htm Android Q 开机启动流程 开机启动概述: step 1: 上电开机 长按power键 ...

  10. Android之wifi工作流程

    Android Wifi的工作流程 一.WIFI工作相关部分 Wifi 网卡状态 1.    WIFI_STATE_DISABLED:WIFI网卡不可用 2.    WIFI_STATE_DISABL ...

最新文章

  1. w ndows连接USB不正常,Raspberry Pi Zero W 连接电脑 – 针对Windows 10 缺少RNDIS驱动
  2. java int数组写入文件中_Java程序将int数组写入文件
  3. 成都东软学院新生周赛(五)
  4. 条件转移指令和无条件转移指令练习
  5. OSPF区域不能与area 0 相连的解决方法
  6. shiro身份验证测试
  7. 算法高级(12)-分布式系统常见负载均衡算法
  8. 《转》浅谈CSRF攻击方式
  9. 中低频量化交易策略研发03_注意事项与应对
  10. 搭建基于Jenkins, Apache Mesos和Marathon的弹性高可用的持续集成环境
  11. 使用Ntdsutil.exe捕获系统状态数据
  12. Hibernate HQL基础
  13. Linux设备驱动模型-Uevent
  14. NLP对放射科医生的评价
  15. 手动批量下载ts文件并合并
  16. 计算机硬件系统册组成,计算机硬件系统的组成教案.doc
  17. 什么是CRM系统,它如何支持客户营销管理?
  18. Spring切入点表达式
  19. java(Springboot) excel模板下载、导入
  20. qq怎么登陆不了微信

热门文章

  1. github创建仓库以及上传项目到github
  2. 反射之前奏Oracle升级版
  3. 修改CentOS默认yum源地址提高下载速度
  4. Kaggle泰坦尼克号提升准确率探索
  5. jq富文本_jQuery富文本编辑器Notebook
  6. qt 富文本 html,Qt富文本编辑器QTextDocument
  7. 生成模型之flow-based model
  8. 为什么mysql中不要用blob这种大字段
  9. 管仲(约前723年-前645年)
  10. 几何公差(GDT)的特征项目及符号