关于Android或linux的引导流程,网上大都是从开机开始讲述的,或者直接跳过bootloader引导阶段,直接从init进程开始说起。这里我从手机正常运行状态开始,到重启状态以及重启之后的状态略做陈述,意在给读者展开一个更加直白的整机引导框架。 
一、device重启之前 
在手机的“setting–>备份与重置—>恢复出厂设置”里可以找到该设置,一旦执行了该设置,我们的手机便会恢复到原出厂设置状态,当然里面的用户数据、我们自行安装的应用等都将被全部清除(有些选项是可选择性删除的,eg:内部空间上的音乐、图片等)。下面一起看下恢复出厂设置的工作流程。 
操作中是从setting中进行的,当然代码中我们也从settings中开始看起。 
settings中涉及到恢复出厂设置的源码流程文件在MasterClearConfirm.java中。我们可以根据settings中的privacy_settings.xml进行查找,privacy_settings.xml是settings中主布局文件中的“备份与重置”Fragment选项,通过它我们可以找到“factory reset”的PreferenceScreen标签:

<!-- Factory reset --><PreferenceScreen
        android:key="factory_reset"android:title="@string/master_clear_title"settings:keywords="@string/keywords_factory_data_reset"android:fragment="com.android.settings.MasterClear" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

从中我们会发现对应Fragment是“com.android.settings.MasterClear”。到此我们就可以就找到了对应的java文件了——MasterClear.java。进入到该java文件后我们发现,在showFinalConfirmation()函数中真正加载的Fragment是“MasterClearConfirm.class”,如下所示:

private void showFinalConfirmation() {Preference preference = new Preference(getActivity());preference.setFragment(MasterClearConfirm.class.getName());preference.setTitle(R.string.master_clear_confirm_title);preference.getExtras().putBoolean(ERASE_EXTERNAL_EXTRA, mExternalStorage.isChecked());((SettingsActivity) getActivity()).onPreferenceStartFragment(null, preference);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

我们找到MasterClearConfirm.java就可以看到里面有我们想要的恢复出厂设置的操作。 
在函数doMasterClear()中会发送ACTION_MASTER_CLEAR广播,而接收者可以在framework/base/core/res/ AndroidManifest.xml中找到:

<receiver android:name="com.android.server.MasterClearReceiver"android:permission="android.permission.MASTER_CLEAR"><intent-filter
                    android:priority="100" ><!-- For Checkin, Settings, etc.: action=MASTER_CLEAR --><action android:name="android.intent.action.MASTER_CLEAR" /><!-- MCS always uses REMOTE_INTENT: category=MASTER_CLEAR --><action android:name="com.google.android.c2dm.intent.RECEIVE" /><category android:name="android.intent.category.MASTER_CLEAR" /></intent-filter></receiver><service android:name="com.android.internal.os.storage.ExternalStorageFormatter"android:permission="android.permission.MASTER_CLEAR"android:exported="true" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

从中可以看出接收者就是MasterClearReceiver,它是框架层的一个service。打开MasterClearReceiver.java,里面只重载了一个onReceive()函数。里面开辟了一个新的线程进行rebootWipeUserData的操作。该函数中组织好参数后通过bootCommand()往cache中的command文件中写指令,并远程调用PowerManagerService.java中的reboot()重启至recovery模式。走到这里,不熟悉Android系统启动流程的屌丝们也许这里就走不动了,唯一得到的信息就是附近应该有IPowerManager.aidl文件。其实,在这里是远程调用了一个系统级的服务——Powermanager。按照应用层的逻辑,我们会找对应的aidl文件,并继续寻找其中reboot功能函数的具体实现方法进一步去找onBind函数或asInterface的连接服务函数。但这里我们是找不到的。因为,该服务不是应用层的服务,是在系统启动的时候,zygote进程起来后,通过systemManager直接加载到服务列表中的,这里直接进行了使用(如何从PowerManager.java调到PowerManagerService.java中的,具体参看zygote进程的启动过程,在此过程中有PowerManager服务的注册流程)。按图索骥,我们可以找到PowerManagerService.java文件,该文件就是PowerManager服务的具体实现。在其中,我们可以找到reboot函数。该函数中的前半部分都是对权限的check,往后看会发现shutdownOrRebootInternal函数。在该函数中,设计者直接new出了一个Runnable线程,顺次看下求,在run函数中有ShutdownThread(ShutdownThread继承自Thread,是一个线程类)的reboot函数分支。在其中,将reason赋值给mRebootReason之后,进行了shutdownInner处理。该函数中,我们只需要看其最后一句:beginShutdownSequence(),点进去,进一步我们会发现在该函数最后启动了一个ShutdownThread线程实例sInstance。下面我们直接跳转到其run()函数。在该运行实体的最后会来到rebootOrShutdown()函数,该函数中,我们会发现lowLevelReboot分支和最后的lowLevelShutdown函数,这两个函数里面做的工作十分类似,都是去设置相应的Prop项。在这里我们只看lowLevelReboot分支。该函数中有详细说明:

if (reason.equals(PowerManager.REBOOT_RECOVERY)) {// If we are rebooting to go into recovery, instead of// setting sys.powerctl directly we'll start the// pre-recovery service which will do some preparation for// recovery and then reboot for us.//// This preparation can take more than 20 seconds if// there's a very large update package, so lengthen the// timeout.SystemProperties.set("ctl.start", "pre-recovery");duration = 120 * 1000L;} else {SystemProperties.set("sys.powerctl", "reboot," + reason);duration = 20 * 1000L;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

所以,接下来直接看set函数。在该函数中最终还是调用了JNI的东西(Android4.4之前是在ShutdownThread.java文件中的lowLevelReboot函数中直接调用的): 
 
仅从函数名判断不出什么,再看native_set函数的定义: 
 
这里申明了一个native类型的函数,但怎么去找到其native函数的具体实现呢?观其文件名SystemProperties.java可以得知其对应注册的native函数命中应该是SystemProperties开头的。我们找到AndroidRuntime.cpp文件,里面有大量的native函数的注册(这里为什么直接定位到了AndroidRuntime.cpp文件?也跟Android启动流程中涉及到的systemmanager注册服务机制有关,具体需要研究下这块)。从中可以看到register_android_os_SystemProperties()函数的声明。点击去会发现里面也是调用了AndroidRuntime::registerNativeMethods()函数(该函数是native函数注册的一个工具函数,很多函数注册时都是通过它)。再看其参数method_table,该变量是一JNINativeMethod类型的数组,里面盛放的就是需要注册的函数列表: 
 
从中可以找到SystemProperties_set函数,它就是我们要找的对应java侧SystemProperties.java文件中native_set()函数的native实体。进入到该函数会发现其最终也是通过property_set(key, val)系统函数将”ctl.start”, “pre-recovery”key, val或”sys.powerctl”, “reboot,”设置下去的。至此,在device重启之前的factory reset流程我们便走完了(具体设置完这些属性后,device为什么就会重启了呢?需要深入研究下device的电源管理或Android的关机流程这块了,这里不做分析)。至此,整个factory reset流程,我们才分析完一半,而另一半分布在device重启后的过程中,下面展开分析。 
二、Device重启之后 
提到重启,就不得不提bootloader。它是系统刚启动时运行的一段或几段程序,主要用来初始化硬件设备、引导系统内核启动。下面简单介绍下bootloader文件的一般组成: 
bootloader一般有好多个文件组成,如Android手机一般会有:PBL(Prime Bootloader), SBL1/2/3(Second Bootloader), APPSBL(有的也称为aboot、hboot), HLOS(基带baseband相关)和TZ(TrustZone相关的镜像)。而iphone手机一般是:BootRom(PBL, SecureROM), LLB(Low Level Bootloader),iBoot(stage 2 bootloader,常用于recovery模式), iBBS(精简版的ibOOT)和iBEC(用于从DFU-Device Firmware Upgrade模式恢复)。对于我的Exynos板子,由于其并非手机设备,包含的bootloader相对较少,有:PBL( 也叫bl0,烧在iROM的只读代码), BL1(stage 1 bootloader), BL2(stage 2 bootloader,就是uboot中的spl), tzsw(trustzone firmware)和uboot。Bootloader分为多阶段的引导,这部分除了正常的硬件初始化工作外,还有我们更关注的一点是签名验证。每一阶段都先验证下一阶段的镜像病验证通过后才加载,形成一个安全信任链,保证这些bootloader和后面的内核的完整性。这里之根据factory reset中涉及到的流程做浅尝解析。 
bootloader启动时汇编中入口文件为arch\arm\crt0.S,忽略其前期对硬件和环境的初始化,直接看跳往c语言的函数kmain:”bl Kmain“,该函数位于main.c文件中。 
进入到kmain函数中,会发现函数体中调用的大多数都是”_init”结尾的函数,顾名思义,他们都是为了初始化环境而存在的(该部分省略不议)。我们直接看到该函数最后,在快结束的地方发现它thread_create了一个线程,该线程的名字就叫bootstrap2,点击bootstrap2函数进入。与前面kmain函数类似,一直都是*_init(该部分见名知意,都是平台相关的初始化环节),我们忽略前面的,只看最后一个apps_init()。这里apps_init 是关键,对 LK 中所谓app 初始化并运行起来,而 aboot_init 就将在这里开始被运行,android linux 内核的加载工作就在 aboot_init 中完成的 。该函数中包含两个for函数,且循环条件一致:

/* call all the init routines */for (app = &__apps_start; app != &__apps_end; app++) {if (app->init)app->init(app);}/* start any that want to start on boot */for (app = &__apps_start; app != &__apps_end; app++) {if (app->entry && (app->flags & APP_FLAG_DONT_START_ON_BOOT) == 0) {start_app(app);}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

该循环条件到底是什么意思呢?本人也是在网上大量的搜索,问google,求度娘,然而得到的最多的就是这么一句话:“至于会有那些 app 被放入 boot thread section, 则定义在 include/app.h 中的 APP_START(appname)”。但到app.h文件中却只找到:

#define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname,
#define APP_END };
  • 1
  • 2
  • 1
  • 2

一直不明白其中原理。直到找到对应的system-onesegment.ld文件(该文件在”bootable\bootloader\lk\ build-目标平台”目录下),该问题才有了眉目:

.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*). = ALIGN(4);__commands_start = .;KEEP (*(.commands))__commands_end = .;. = ALIGN(4);__apps_start = .;KEEP (*(.apps))__apps_end = .;. = ALIGN(4); __rodata_end = . ;  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

原来,在其最终的连接文件里,是将需要启动的apps括在了SECTIONS下的.rodata段中,且以__apps_start为开头,以__apps_end标志结束(这里涉及到文件结构的部分内容,内容拓展可以看《程序员的自我修养—链接、装载与库》一书)。此时再结合网上所说的app.h中的那句话也就明了了许多了。正如网上所说“在 app 中只要像 app/aboot/aboot.c 指定就会在 bootloader bootup 时放入 thread section 中被执行”。这点我们可以直接在整个lk中搜索关键字“APP_START”会发现我们的bootloader中到底有多少个类似这样的app(不同的bootloader情况有所不同): 
 
如上图,我们可以得知满足条件的app有aboot、clocktests、pcitests、shell、stringtests和tests,我们这里只关注aboot。 
我们找到aboot.c文件,找到aboot_init()函数。根据源码注释,依次实现了: 
1、设置NAND/EMMC读取信息页面大小:

/* Setup page size information for nv storage */if (target_is_emmc_boot()){page_size = mmc_page_size();page_mask = page_size - 1;}else{page_size = flash_page_size();page_mask = page_size - 1;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2、读取按键信息,判断是正常开机,还是进入 fastboot ,还是进入recovery 模式: 
在函数体内,除了上半部分对按键进行判断以确定模式走向外,还有对BCB区域中command指令的读取来判断:init.c文件中的check_hard_reboot_mode():

unsigned check_hard_reboot_mode(void)
{uint8_t hard_restart_reason = 0;uint8_t value = 0;/* Read reboot reason and scrub it* Bit-5, bit-6 and bit-7 of SOFT_RB_SPARE for hard reset reason*/value = pm8x41_reg_read(PON_SOFT_RB_SPARE);hard_restart_reason = value >> 5;pm8x41_reg_write(PON_SOFT_RB_SPARE, value & 0x1f);return hard_restart_reason;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

或者check_reboot_mode():

unsigned check_reboot_mode(void)
{uint32_t restart_reason = 0;uint32_t soc_ver = 0;uint32_t restart_reason_addr;soc_ver = board_soc_version();if (platform_is_8974() && BOARD_SOC_VERSION1(soc_ver))restart_reason_addr = RESTART_REASON_ADDR;elserestart_reason_addr = RESTART_REASON_ADDR_V2;/* Read reboot reason and scrub it */restart_reason = readl(restart_reason_addr);writel(0x00, restart_reason_addr);return restart_reason;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

从上面代码会发现最终的返回值restart_reason_addr是从RESTART_REASON_ADDR或者RESTART_REASON_ADDR_V2中取得的,这里就是BCB中的上个command里的内容了。也是在这里开始决定接下来系统会走向main system、Recovery还是fastboot模式的。如果启动的是main system或者Recovery模式,则会执行下文的后续步骤;如果是fastboot模式,则通过fastboot_init()直接初始化并启动到fastboot阶段,该模式下没有内核的加载和启动。 
3、加载内核:如果是启动main system则执行boot_linux_from_mmc()进行加载,如果是启动Recovery模式则通过boot_linux_from_flash()加载, 
4、启动内核:

boot_linux((void *)hdr->kernel_addr, (void *)hdr->tags_addr,(const char *)hdr->cmdline, board_machtype(),(void *)hdr->ramdisk_addr, hdr->ramdisk_size);
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

这里linux kernel起来之后,会初始化init进程,并布置ramdisk文件系统等等后续应用工作,这里具体流程请参看linux kernel启动流程。 
三、结束语 
至此,从main system到bootloader然后再次到模式选择分析结束,接下来我们可以选择具体的main system 、recovery或者fastboot模式继续往下分析,具体分析这里不再继续。main system模式具体见网上Android或linux的启动流程。而recovery模式接下的具体流程可以参见recovery源码流程分析。

平述factory reset ——从main system到重引导流程相关推荐

  1. 【蓝桥杯单片机】IAP15在线仿真实验:Connecting to target system lost!please reset your target system and try again

    Connecting to target system lost!please reset your target system and try again. 当显示这行字符时就要注意是否犯了以下几条 ...

  2. android main system log,android日志缓冲区,system,radio,main,events是什么意思啊?

    满意答案 a568103627 2015.06.11 采纳率:57%    等级:11 已帮助:6453人 logcat使用方法如下所示[1]: [adb] logcat [] ... [] ... ...

  3. [Android][FRP]Factory Reset Protection

    Pass:本文有部分截图摘选自CSDN博友画图以及google/qcom文档,由于是上一年写的文章,所以暂时未能提供出处 Factory Reset Protection 功能介绍: Google F ...

  4. Android factory reset 流程

    http://blog.csdn.net/yahoozhuo/article/details/6403140 ===== 应用流程 ===== ====>frameworks/base/serv ...

  5. DSP芯片上电后在执行main函数前的执行流程

    以tms320f28377d为例: 假设我们创建工程test: 该项目中需要注意的文件包括(见图1): 1. main.c文件: 2. DSP用于连接.coff(可执行文件)的  .cmd文件: 3. ...

  6. 升级包的制作与升级流程简介

    #升级包的制作与升级的流程 一.升级包的制作 1.Makefile make otapackage是一个.PHONY伪目标,查看\build\core\Makefile: .PHONY: otapac ...

  7. Android10.0 OTA升级流程分析

    原文地址:https://skytoby.github.io/2019/Android%20OTA%E5%8D%87%E7%BA%A7%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E ...

  8. Recovery 简介

    概述 Recovery模式指的是一种可以对安卓机内部的数据或系统进行修改的模式(类似于windows PE或DOS).在这个模式下我们可以刷入新的Android系 统,或者对已有的系统进行备份或升级, ...

  9. note3 android system recovery,三星note3 N900v刷第三方rom

    一台老手机,废物利用,如下为刷第三方rom过程 原系统为android4.4.2 ,Verizon美版,支持移动2G,联通3G,电信3G,高通骁龙800处理器,2.3GHZ主频, 3GB RAM,32 ...

最新文章

  1. OpenCV位姿与投影变换
  2. Java:Java和c的区别
  3. TypeScript学习笔记3:运算符
  4. 【重要】如何彻底夯实CV基础,有三AI三大导师一起带你学习!
  5. qss样式表笔记大全(一):qss名词解析(包含相关示例)
  6. dot-- 资源汇总
  7. Chapter2-1_Voice Conversion(Feature Disentangle)
  8. 利用Android属性动画实现Banner的原理与实践
  9. Linux下使用wget下载FTP服务器文件
  10. ftk学习记(窗口全屏设置篇)
  11. Linux下的进程池(3)
  12. C语言/C++常见字符串函数
  13. python列表排序sort_python列表排序方法reverse、sort、sorted
  14. 英雄联盟一直连接服务器win10,浅析win10英雄联盟连接不上服务器的解决教程
  15. PowerBi包含什么,以及每一个的介绍
  16. IT运维审计系统是什么?有推荐的吗?
  17. laravel定时任务设置
  18. 可胜任任何网吧技术主管的绝招
  19. eos bp节点 超级节点搭建
  20. matlab循迹小车,Arduino智能小车系列教程6——循迹小车

热门文章

  1. 005_Redis的Hash数据类型
  2. 集合框架源码分析六之堆结构的实现(PriorityQueue)
  3. day01: oracle12C在Linux7.5上图形化安装部署方法:
  4. 小米 一亿像素传感器 笔记
  5. python mysql库对比_mysqlclient和PyMySQL对比
  6. 卡片的sak为不支持的类型_“师傅”两字是不能随便叫的!78%的网友不支持称顺风车主为“师傅”...
  7. 2015快捷键在哪里设置_炒股软件的选择通达信软件的基本设置
  8. 计算机网页div怎么划分,《网页布局与实现(DIV+CSS)》
  9. python生成姓名,python自动生成姓名
  10. java windows wifi_[转] win7 电脑无线网变身WiFi热点