平述factory reset——从main system到重引导流程
關(guān)于Android或linux的引導(dǎo)流程,網(wǎng)上大都是從開機(jī)開始講述的,或者直接跳過bootloader引導(dǎo)階段,直接從init進(jìn)程開始說起。這里我從手機(jī)正常運(yùn)行狀態(tài)開始,到重啟狀態(tài)以及重啟之后的狀態(tài)略做陳述,意在給讀者展開一個(gè)更加直白的整機(jī)引導(dǎo)框架。?
一、device重啟之前?
在手機(jī)的“setting–>備份與重置—>恢復(fù)出廠設(shè)置”里可以找到該設(shè)置,一旦執(zhí)行了該設(shè)置,我們的手機(jī)便會(huì)恢復(fù)到原出廠設(shè)置狀態(tài),當(dāng)然里面的用戶數(shù)據(jù)、我們自行安裝的應(yīng)用等都將被全部清除(有些選項(xiàng)是可選擇性刪除的,eg:內(nèi)部空間上的音樂、圖片等)。下面一起看下恢復(fù)出廠設(shè)置的工作流程。?
操作中是從setting中進(jìn)行的,當(dāng)然代碼中我們也從settings中開始看起。?
settings中涉及到恢復(fù)出廠設(shè)置的源碼流程文件在MasterClearConfirm.java中。我們可以根據(jù)settings中的privacy_settings.xml進(jìn)行查找,privacy_settings.xml是settings中主布局文件中的“備份與重置”Fragment選項(xiàng),通過它我們可以找到“factory reset”的PreferenceScreen標(biāo)簽:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
從中我們會(huì)發(fā)現(xiàn)對(duì)應(yīng)Fragment是“com.android.settings.MasterClear”。到此我們就可以就找到了對(duì)應(yīng)的java文件了——MasterClear.java。進(jìn)入到該java文件后我們發(fā)現(xiàn),在showFinalConfirmation()函數(shù)中真正加載的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就可以看到里面有我們想要的恢復(fù)出廠設(shè)置的操作。?
在函數(shù)doMasterClear()中會(huì)發(fā)送ACTION_MASTER_CLEAR廣播,而接收者可以在framework/base/core/res/ AndroidManifest.xml中找到:
- 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,它是框架層的一個(gè)service。打開MasterClearReceiver.java,里面只重載了一個(gè)onReceive()函數(shù)。里面開辟了一個(gè)新的線程進(jìn)行rebootWipeUserData的操作。該函數(shù)中組織好參數(shù)后通過bootCommand()往cache中的command文件中寫指令,并遠(yuǎn)程調(diào)用PowerManagerService.java中的reboot()重啟至recovery模式。走到這里,不熟悉Android系統(tǒng)啟動(dòng)流程的屌絲們也許這里就走不動(dòng)了,唯一得到的信息就是附近應(yīng)該有IPowerManager.aidl文件。其實(shí),在這里是遠(yuǎn)程調(diào)用了一個(gè)系統(tǒng)級(jí)的服務(wù)——Powermanager。按照應(yīng)用層的邏輯,我們會(huì)找對(duì)應(yīng)的aidl文件,并繼續(xù)尋找其中reboot功能函數(shù)的具體實(shí)現(xiàn)方法進(jìn)一步去找onBind函數(shù)或asInterface的連接服務(wù)函數(shù)。但這里我們是找不到的。因?yàn)?#xff0c;該服務(wù)不是應(yīng)用層的服務(wù),是在系統(tǒng)啟動(dòng)的時(shí)候,zygote進(jìn)程起來后,通過systemManager直接加載到服務(wù)列表中的,這里直接進(jìn)行了使用(如何從PowerManager.java調(diào)到PowerManagerService.java中的,具體參看zygote進(jìn)程的啟動(dòng)過程,在此過程中有PowerManager服務(wù)的注冊(cè)流程)。按圖索驥,我們可以找到PowerManagerService.java文件,該文件就是PowerManager服務(wù)的具體實(shí)現(xiàn)。在其中,我們可以找到reboot函數(shù)。該函數(shù)中的前半部分都是對(duì)權(quán)限的check,往后看會(huì)發(fā)現(xiàn)shutdownOrRebootInternal函數(shù)。在該函數(shù)中,設(shè)計(jì)者直接new出了一個(gè)Runnable線程,順次看下求,在run函數(shù)中有ShutdownThread(ShutdownThread繼承自Thread,是一個(gè)線程類)的reboot函數(shù)分支。在其中,將reason賦值給mRebootReason之后,進(jìn)行了shutdownInner處理。該函數(shù)中,我們只需要看其最后一句:beginShutdownSequence(),點(diǎn)進(jìn)去,進(jìn)一步我們會(huì)發(fā)現(xiàn)在該函數(shù)最后啟動(dòng)了一個(gè)ShutdownThread線程實(shí)例sInstance。下面我們直接跳轉(zhuǎn)到其run()函數(shù)。在該運(yùn)行實(shí)體的最后會(huì)來到rebootOrShutdown()函數(shù),該函數(shù)中,我們會(huì)發(fā)現(xiàn)lowLevelReboot分支和最后的lowLevelShutdown函數(shù),這兩個(gè)函數(shù)里面做的工作十分類似,都是去設(shè)置相應(yīng)的Prop項(xiàng)。在這里我們只看lowLevelReboot分支。該函數(shù)中有詳細(xì)說明:
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函數(shù)。在該函數(shù)中最終還是調(diào)用了JNI的東西(Android4.4之前是在ShutdownThread.java文件中的lowLevelReboot函數(shù)中直接調(diào)用的):?
?
僅從函數(shù)名判斷不出什么,再看native_set函數(shù)的定義:?
?
這里申明了一個(gè)native類型的函數(shù),但怎么去找到其native函數(shù)的具體實(shí)現(xiàn)呢?觀其文件名SystemProperties.java可以得知其對(duì)應(yīng)注冊(cè)的native函數(shù)命中應(yīng)該是SystemProperties開頭的。我們找到AndroidRuntime.cpp文件,里面有大量的native函數(shù)的注冊(cè)(這里為什么直接定位到了AndroidRuntime.cpp文件?也跟Android啟動(dòng)流程中涉及到的systemmanager注冊(cè)服務(wù)機(jī)制有關(guān),具體需要研究下這塊)。從中可以看到register_android_os_SystemProperties()函數(shù)的聲明。點(diǎn)擊去會(huì)發(fā)現(xiàn)里面也是調(diào)用了AndroidRuntime::registerNativeMethods()函數(shù)(該函數(shù)是native函數(shù)注冊(cè)的一個(gè)工具函數(shù),很多函數(shù)注冊(cè)時(shí)都是通過它)。再看其參數(shù)method_table,該變量是一JNINativeMethod類型的數(shù)組,里面盛放的就是需要注冊(cè)的函數(shù)列表:?
?
從中可以找到SystemProperties_set函數(shù),它就是我們要找的對(duì)應(yīng)java側(cè)SystemProperties.java文件中native_set()函數(shù)的native實(shí)體。進(jìn)入到該函數(shù)會(huì)發(fā)現(xiàn)其最終也是通過property_set(key, val)系統(tǒng)函數(shù)將”ctl.start”, “pre-recovery”key, val或”sys.powerctl”, “reboot,”設(shè)置下去的。至此,在device重啟之前的factory reset流程我們便走完了(具體設(shè)置完這些屬性后,device為什么就會(huì)重啟了呢?需要深入研究下device的電源管理或Android的關(guān)機(jī)流程這塊了,這里不做分析)。至此,整個(gè)factory reset流程,我們才分析完一半,而另一半分布在device重啟后的過程中,下面展開分析。?
二、Device重啟之后?
提到重啟,就不得不提bootloader。它是系統(tǒng)剛啟動(dòng)時(shí)運(yùn)行的一段或幾段程序,主要用來初始化硬件設(shè)備、引導(dǎo)系統(tǒng)內(nèi)核啟動(dòng)。下面簡(jiǎn)單介紹下bootloader文件的一般組成:?
bootloader一般有好多個(gè)文件組成,如Android手機(jī)一般會(huì)有:PBL(Prime Bootloader), SBL1/2/3(Second Bootloader), APPSBL(有的也稱為aboot、hboot), HLOS(基帶baseband相關(guān))和TZ(TrustZone相關(guān)的鏡像)。而iphone手機(jī)一般是:BootRom(PBL, SecureROM), LLB(Low Level Bootloader),iBoot(stage 2 bootloader,常用于recovery模式), iBBS(精簡(jiǎn)版的ibOOT)和iBEC(用于從DFU-Device Firmware Upgrade模式恢復(fù))。對(duì)于我的Exynos板子,由于其并非手機(jī)設(shè)備,包含的bootloader相對(duì)較少,有:PBL( 也叫bl0,燒在iROM的只讀代碼), BL1(stage 1 bootloader), BL2(stage 2 bootloader,就是uboot中的spl), tzsw(trustzone firmware)和uboot。Bootloader分為多階段的引導(dǎo),這部分除了正常的硬件初始化工作外,還有我們更關(guān)注的一點(diǎn)是簽名驗(yàn)證。每一階段都先驗(yàn)證下一階段的鏡像病驗(yàn)證通過后才加載,形成一個(gè)安全信任鏈,保證這些bootloader和后面的內(nèi)核的完整性。這里之根據(jù)factory reset中涉及到的流程做淺嘗解析。?
bootloader啟動(dòng)時(shí)匯編中入口文件為arch\arm\crt0.S,忽略其前期對(duì)硬件和環(huán)境的初始化,直接看跳往c語(yǔ)言的函數(shù)kmain:”bl Kmain“,該函數(shù)位于main.c文件中。?
進(jìn)入到kmain函數(shù)中,會(huì)發(fā)現(xiàn)函數(shù)體中調(diào)用的大多數(shù)都是”_init”結(jié)尾的函數(shù),顧名思義,他們都是為了初始化環(huán)境而存在的(該部分省略不議)。我們直接看到該函數(shù)最后,在快結(jié)束的地方發(fā)現(xiàn)它thread_create了一個(gè)線程,該線程的名字就叫bootstrap2,點(diǎn)擊bootstrap2函數(shù)進(jìn)入。與前面kmain函數(shù)類似,一直都是*_init(該部分見名知意,都是平臺(tái)相關(guān)的初始化環(huán)節(jié)),我們忽略前面的,只看最后一個(gè)apps_init()。這里apps_init 是關(guān)鍵,對(duì) LK 中所謂app 初始化并運(yùn)行起來,而 aboot_init 就將在這里開始被運(yùn)行,android linux 內(nèi)核的加載工作就在 aboot_init 中完成的 。該函數(shù)中包含兩個(gè)for函數(shù),且循環(huán)條件一致:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
該循環(huán)條件到底是什么意思呢?本人也是在網(wǎng)上大量的搜索,問google,求度娘,然而得到的最多的就是這么一句話:“至于會(huì)有那些 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
一直不明白其中原理。直到找到對(duì)應(yīng)的system-onesegment.ld文件(該文件在”bootable\bootloader\lk\ build-目標(biāo)平臺(tái)”目錄下),該問題才有了眉目:
.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
原來,在其最終的連接文件里,是將需要啟動(dòng)的apps括在了SECTIONS下的.rodata段中,且以__apps_start為開頭,以__apps_end標(biāo)志結(jié)束(這里涉及到文件結(jié)構(gòu)的部分內(nèi)容,內(nèi)容拓展可以看《程序員的自我修養(yǎng)—鏈接、裝載與庫(kù)》一書)。此時(shí)再結(jié)合網(wǎng)上所說的app.h中的那句話也就明了了許多了。正如網(wǎng)上所說“在 app 中只要像 app/aboot/aboot.c 指定就會(huì)在 bootloader bootup 時(shí)放入 thread section 中被執(zhí)行”。這點(diǎn)我們可以直接在整個(gè)lk中搜索關(guān)鍵字“APP_START”會(huì)發(fā)現(xiàn)我們的bootloader中到底有多少個(gè)類似這樣的app(不同的bootloader情況有所不同):?
?
如上圖,我們可以得知滿足條件的app有aboot、clocktests、pcitests、shell、stringtests和tests,我們這里只關(guān)注aboot。?
我們找到aboot.c文件,找到aboot_init()函數(shù)。根據(jù)源碼注釋,依次實(shí)現(xiàn)了:?
1、設(shè)置NAND/EMMC讀取信息頁(yè)面大小:
- 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、讀取按鍵信息,判斷是正常開機(jī),還是進(jìn)入 fastboot ,還是進(jìn)入recovery 模式:?
在函數(shù)體內(nèi),除了上半部分對(duì)按鍵進(jìn)行判斷以確定模式走向外,還有對(duì)BCB區(qū)域中command指令的讀取來判斷:init.c文件中的check_hard_reboot_mode():
- 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
從上面代碼會(huì)發(fā)現(xiàn)最終的返回值restart_reason_addr是從RESTART_REASON_ADDR或者RESTART_REASON_ADDR_V2中取得的,這里就是BCB中的上個(gè)command里的內(nèi)容了。也是在這里開始決定接下來系統(tǒng)會(huì)走向main system、Recovery還是fastboot模式的。如果啟動(dòng)的是main system或者Recovery模式,則會(huì)執(zhí)行下文的后續(xù)步驟;如果是fastboot模式,則通過fastboot_init()直接初始化并啟動(dòng)到fastboot階段,該模式下沒有內(nèi)核的加載和啟動(dòng)。?
3、加載內(nèi)核:如果是啟動(dòng)main system則執(zhí)行boot_linux_from_mmc()進(jìn)行加載,如果是啟動(dòng)Recovery模式則通過boot_linux_from_flash()加載,?
4、啟動(dòng)內(nèi)核:
- 1
- 2
- 3
- 1
- 2
- 3
這里linux kernel起來之后,會(huì)初始化init進(jìn)程,并布置ramdisk文件系統(tǒng)等等后續(xù)應(yīng)用工作,這里具體流程請(qǐng)參看linux kernel啟動(dòng)流程。?
三、結(jié)束語(yǔ)?
至此,從main system到bootloader然后再次到模式選擇分析結(jié)束,接下來我們可以選擇具體的main system 、recovery或者fastboot模式繼續(xù)往下分析,具體分析這里不再繼續(xù)。main system模式具體見網(wǎng)上Android或linux的啟動(dòng)流程。而recovery模式接下的具體流程可以參見recovery源碼流程分析。
總結(jié)
以上是生活随笔為你收集整理的平述factory reset——从main system到重引导流程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android LK Bootlaode
- 下一篇: msm8974 camera drive