Android OTA 升级之四:进入根文件系统
2013-01-16 16:58
453 查看
前言
从bootloader进入Recovery模式后,首先也是运行Linux内核,该内核跟普通模式没有区别(减轻了BSP开发者的任务)。区别从执行文件系统开始。Recovery模式的细节就隐藏在其根文件系统中。下面,我们就看看进入Recovery根文件系统都干些啥。
init.rc
和正常启动一样,内核进入文件系统会执行/init,init的配置文件就是/init.rc,前面文章讲过,这个文件来自:bootable/recovery/etc/init.rc,下面,我们看看它的内容。1
2oninit
3exportPATH/sbin
4exportANDROID_ROOT/system
5exportANDROID_DATA/data
6exportEXTERNAL_STORAGE/sdcard
7
8symlink/system/etc/etc
9
10mkdir/sdcard
11mkdir/system
12mkdir/data
13mkdir/cache
14mount/tmp/tmptmpfs
15
16onboot
17
18ifuplo
19hostnamelocalhost
20domainnamelocaldomain
21
22class_startdefault
23
24
25servicerecovery/sbin/recovery
26
27serviceadbd/sbin/adbdrecovery
28disabled
29
30onproperty:persist.service.adb.enable=1
31startadbd
32
33onproperty:persist.service.adb.enable=0
34stopadbd
可以看到,它很非常简单:
1)设置几个环境变量。备用。
2)建立etc链接。
3)造几个目录。备用。
4)Mount/tmp目录为内存文件系统tmpfs,后面会用到。
5)Trival设置,不必关心。
6)启动recovery主程序。
7)如果是eng模式(此时persist.service.adb.enable),启动adb
当然,init主程序还会装载属性配置文件/default.prop,它包含了很多系统属性设置,比如,ro.build.*,等等。
很明显,这里最重要的就是recovery主程序,下面,我们分析它。
先看一段注释
Recovery.c中,作者写了一段注释,对我们理解recovery的实现很有帮助,下面看一下:(我就不翻译了)89/*
90*Therecoverytoolcommunicateswiththemainsystemthrough/cachefiles.
91*/cache/recovery/command-INPUT-commandlinefortool,oneargperline
92*/cache/recovery/log-OUTPUT-combinedlogfilefromrecoveryrun(s)
93*/cache/recovery/intent-OUTPUT-intentthatwaspassedin
94*
95*Theargumentswhichmaybesuppliedintherecovery.commandfile:
96*--send_intent=anystring-writethetextouttorecovery.intent
97*--update_package=root:path-verifyinstallanOTApackagefile
98*--wipe_data-eraseuserdata(andcache),thenreboot
99*--wipe_cache-wipecache(butnotuserdata),thenreboot
100*--set_encrypted_filesystem=on|off-enables/diasablesencryptedfs
101*
102*Aftercompleting,weremove/cache/recovery/commandandreboot.
103*Argumentsmayalsobesuppliedinthebootloadercontrolblock(BCB).
104*Theseimportantscenariosmustbesafelyrestartableatanypoint:
105*
106*FACTORYRESET
107*1.userselects"factoryreset"
108*2.mainsystemwrites"--wipe_data"to/cache/recovery/command
109*3.mainsystemrebootsintorecovery
110*4.get_args()writesBCBwith"boot-recovery"and"--wipe_data"
111*--afterthis,rebootingwillrestarttheerase--
112*5.erase_root()reformats/data
113*6.erase_root()reformats/cache
114*7.finish_recovery()erasesBCB
115*--afterthis,rebootingwillrestartthemainsystem--
116*8.main()callsreboot()tobootmainsystem
117*
118*OTAINSTALL
119*1.mainsystemdownloadsOTApackageto/cache/some-filename.zip
120*2.mainsystemwrites"--update_package=CACHE:some-filename.zip"
121*3.mainsystemrebootsintorecovery
122*4.get_args()writesBCBwith"boot-recovery"and"--update_package=..."
123*--afterthis,rebootingwillattempttoreinstalltheupdate--
124*5.install_package()attemptstoinstalltheupdate
125*NOTE:thepackageinstallmustitselfberestartablefromanypoint
126*6.finish_recovery()erasesBCB
127*--afterthis,rebootingwill(tryto)restartthemainsystem--
128*7.**ifinstallfailed**
129*7a.prompt_and_wait()showsanerroriconandwaitsfortheuser
130*7b;theuserreboots(pullingthebattery,etc)intothemainsystem
131*8.main()callsmaybe_install_firmware_update()
132***iftheupdatecontainedradio/hbootfirmware**:
133*8a.m_i_f_u()writesBCBwith"boot-recovery"and"--wipe_cache"
134*--afterthis,rebootingwillreformatcache&restartmainsystem--
135*8b.m_i_f_u()writesfirmwareimageintorawcachepartition
136*8c.m_i_f_u()writesBCBwith"update-radio/hboot"and"--wipe_cache"
137*--afterthis,rebootingwillattempttoreinstallfirmware--
138*8d.bootloadertriestoflashfirmware
139*8e.bootloaderwritesBCBwith"boot-recovery"(keeping"--wipe_cache")
140*--afterthis,rebootingwillreformatcache&restartmainsystem--
141*8f.erase_root()reformats/cache
142*8g.finish_recovery()erasesBCB
143*--afterthis,rebootingwill(tryto)restartthemainsystem--
144*9.main()callsreboot()tobootmainsystem
145*
146*ENCRYPTEDFILESYSTEMSENABLE/DISABLE
147*1.userselects"enableencryptedfilesystems"
148*2.mainsystemwrites"--set_encrypted_filesystem=on|off"to
149*/cache/recovery/command
150*3.mainsystemrebootsintorecovery
151*4.get_args()writesBCBwith"boot-recovery"and
152*"--set_encrypted_filesystems=on|off"
153*--afterthis,rebootingwillrestartthetransition--
154*5.read_encrypted_fs_info()retrievesencryptedfilesystemssettingsfrom/data
155*Settingsinclude:propertytospecifytheEncryptedFSistatusand
156*FSencryptionkeyifenabled(notyetimplemented)
157*6.erase_root()reformats/data
158*7.erase_root()reformats/cache
159*8.restore_encrypted_fs_info()writesrequiredencryptedfilesystemssettingsto/data
160*Settingsinclude:propertytospecifytheEncryptedFSstatusand
161*FSencryptionkeyifenabled(notyetimplemented)
162*9.finish_recovery()erasesBCB
163*--afterthis,rebootingwillrestartthemainsystem--
164*10.main()callsreboot()tobootmainsystem
165*/
recovery主程序
559int
560 main(int argc,char** argv)
561{
562 time_t start= time( NULL);
563
564//Ifthesefail,there'snotreallyanywheretocomplain...
565 freopen( TEMPORARY_LOG_FILE,"a", stdout); setbuf( stdout, NULL);
566 freopen( TEMPORARY_LOG_FILE,"a", stderr); setbuf( stderr, NULL);
567 fprintf( stderr,"Startingrecoveryon%s", ctime(& start));
568
将标准输出和标准错误输出重定位到"/tmp/recovery.log",如果是eng模式,就可以通过adbpull/tmp/recovery.log,看到当前的log信息,这为我们提供了有效的调试手段。后面还会看到,recovery模式运行完毕后,会将其拷贝到cache分区,以便后续分析。
569 ui_init();
Recovery使用了一个简单的基于framebuffer的ui系统,叫miniui,这里,进行了简单的初始化(主要是图形部分以及事件部分),并启动了一个event线程用于响应用户按键。
570 get_args(& argc,& argv);
从misc分区以及CACHE:recovery/command文件中读入参数,写入到argc,argv,并且,如果有必要,回写入misc分区。这样,如果recovery没有操作成功(比如,升级还没有结束,就拔电池),系统会一直进入recovery模式。提醒用户继续升级,直到成功。
572intprevious_runs=0;
573constchar*send_intent= NULL;
574constchar*update_package= NULL;
575int wipe_data=0,wipe_cache=0;
576
577int arg;
578while(( arg= getopt_long( argc, argv,"", OPTIONS, NULL))!=-1){
579switch( arg){
580case'p':previous_runs= atoi( optarg);break;
581case's':send_intent= optarg;break;
582case'u':update_package= optarg;break;
583case'w': wipe_data=wipe_cache=1;break;
584case'c':wipe_cache=1;break;
585case'?':
586 LOGE("Invalidcommandargument/n");
587continue;
588}
589}
590
解析参数,p:previous_runs没有用到,其它含义见前面注释。
591 device_recovery_start();
这个函数没干什么。看名字,它給设备制造商提供了一个调用机会,可写入设备相关初始化代码。
592
593 fprintf( stderr,"Command:");
594for( arg=0; arg< argc; arg++){
595 fprintf( stderr,"/"%s/"", argv[ arg]);
596}
597 fprintf( stderr,"/n/n");
598
打印出命令,比如,正常启动进入recovery模式,会打印:Command:"/sbin/recovery"
599 property_list( print_property, NULL);
600 fprintf( stderr,"/n");
601
打印出所有的系统属性(fromdefault.prop)到log文件。
602int status=INSTALL_SUCCESS;
603
604if(update_package!= NULL){
605 status= install_package(update_package);
606if( status!=INSTALL_SUCCESS) ui_print("Installationaborted./n");
607}elseif( wipe_data){
608if( device_wipe_data()) status=INSTALL_ERROR;
609if( erase_root("DATA:")) status=INSTALL_ERROR;
610if(wipe_cache&& erase_root("CACHE:")) status=INSTALL_ERROR;
611if( status!=INSTALL_SUCCESS) ui_print("Datawipefailed./n");
612}elseif(wipe_cache){
613if(wipe_cache&& erase_root("CACHE:")) status=INSTALL_ERROR;
614if( status!=INSTALL_SUCCESS) ui_print("Cachewipefailed./n");
615}else{
616 status=INSTALL_ERROR;//Nocommandspecified
617}
根据用户提供参数,调用各项功能,比如,安装一个升级包,擦除cache分区,擦除userdata分区,install_package比较复杂,后面专门分析,其它都很简单。忽略。
618
619if( status!=INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
622if( status!=INSTALL_SUCCESS) prompt_and_wait();
如果前面已经做了某项操作并且成功,则进入重启流程。否则,等待用户选择具体操作。
而用户可选操作为:reboot,安装update.zip,除cache分区,擦除userdata分区,如前所述,只有安装package比较复杂,其它简单。
623
624//Otherwise,getreadytobootthemainsystem...
625 finish_recovery(send_intent);
它的功能如下:
1)将前面定义的intent字符串写入(如果有的话):CACHE:recovery/command
2)将/tmp/recovery.log复制到"CACHE:recovery/log";
3)清空misc分区,这样重启就不会进入recovery模式
4)删除command文件:CACHE:recovery/command;
626 ui_print("Rebooting.../n");
627 sync();
628 reboot( RB_AUTOBOOT);
629return EXIT_SUCCESS;
630}
重启。
下面我们分析核心函数
install_package
289int
290 install_package(constchar*root_path)
291{
292 ui_set_background(BACKGROUND_ICON_INSTALLING);
294ui_print("Findingupdatepackage.../n");
295 LOGI("Findingupdatepackage.../n");
296 ui_show_indeterminate_progress();
297 LOGI("Updatelocation:%s/n",root_path);
298
更新UI显示
299if( ensure_root_path_mounted(root_path)!=0){
300 LOGE("Can'tmount%s/n",root_path);
301 reset_mark_block();
302returnINSTALL_CORRUPT;
303}
304
确保升级包所在分区已经mount,通常为cache分区或者SD分区
305char path[ PATH_MAX]="";
306if( translate_root_path(root_path, path,sizeof( path))== NULL){
307 LOGE("Badpath%s/n",root_path);
308 reset_mark_block();
309returnINSTALL_CORRUPT;
310}
将根分区转化为具体分区信息.这些信息来自:全局数组:g_roots
313ui_print("Openingupdatepackage.../n");
314 LOGI("Openingupdatepackage.../n");
315 LOGI("Updatefilepath:%s/n", path);
316
317intnumKeys;
318RSAPublicKey*loadedKeys= load_keys( PUBLIC_KEYS_FILE,&numKeys);
319if(loadedKeys== NULL){
320 LOGE("Failedtoloadkeys/n");
321 reset_mark_block();
322returnINSTALL_CORRUPT;
323}
324 LOGI("%dkey(s)loadedfrom%s/n",numKeys, PUBLIC_KEYS_FILE);
从/res/keys中装载公钥。
326//Giveverificationhalftheprogressbar...
328ui_print("Verifyingupdatepackage.../n");
329 LOGI("Verifyingupdatepackage.../n");
330 ui_show_progress(
331 VERIFICATION_PROGRESS_FRACTION,
332 VERIFICATION_PROGRESS_TIME);
333
334int err;
335 err= verify_file( path,loadedKeys,numKeys);
336 free(loadedKeys);
337 LOGI("verify_filereturned%d/n", err);
338if( err!= VERIFY_SUCCESS){
339 LOGE("signatureverificationfailed/n");
340 reset_mark_block();
341returnINSTALL_CORRUPT;
342}
根据公钥验证升级包verify_file的注释说的很明白:
//LookforanRSAsignatureembeddedinthe.ZIPfilecommentgiven
//thepathtothezip.Verifyitmatchesoneofthegivenpublic
//keys.
344/*Trytoopenthepackage.
345*/
346 ZipArchivezip;
347 err= mzOpenZipArchive( path,&zip);
348if( err!=0){
349 LOGE("Can'topen%s/n(%s)/n", path, err!=-1? strerror( err):"bad");
350 reset_mark_block();
351returnINSTALL_CORRUPT;
352}
打开升级包,将相关信息存到ZuoArchive数据机构中,便于后面处理。
354/*Verifyandinstallthecontentsofthepackage.
355*/
356int status= handle_update_package( path,&zip);
处理函数,我们后面继续分析。
357 mzCloseZipArchive(&zip);
358return status;
359}
关闭zip包,结束处理。
handle_update_package
204staticint
205 handle_update_package(constchar* path, ZipArchive*zip)
206{
207//Updateshouldtaketherestoftheprogressbar.
208 ui_print("Installingupdate.../n");
209
210int result= try_update_binary( path,zip);
211 register_package_root( NULL, NULL);//Unregisterpackageroot
212return result;
213}
它主要调用函数
try_update_binary
//Ifthepackagecontainsanupdatebinary,extractitandrunit.
creat(
ok=
mzExtractZipEntryToFile(zip,binary_entry,
fd);
将升级包内文件META-INF/com/google/android/update-binary复制为/tmp/update_binary
//Whenexecutingtheupdatebinarycontainedinthepackage,the
//argumentspassedare:
//
//-theversionnumberforthisinterface
//
//-anfdtowhichtheprogramcanwriteinordertoupdatethe
//progressbar.Theprogramcanwritesingle-linecommands:
//
//progress<frac><secs>
//fillupthenext<frac>partofoftheprogressbar
//over<secs>seconds.If<secs>iszero,use
//set_progresscommandstomanuallycontrolthe
//progressofthissegmentofthebar
//
//set_progress<frac>
//<frac>shouldbebetween0.0and1.0;setsthe
//progressbarwithinthesegmentdefinedbythemost
//recentprogresscommand.
//
//firmware<"hboot"|"radio"><filename>
//arrangetoinstallthecontentsof<filename>inthe
//givenpartitiononreboot.
//
//(APIv2:<filename>maystartwith"PACKAGE:"to
//indicatetakingafilefromtheOTApackage.)
//
//(APIv3:thiscommandnolongerexists.)
//
//ui_print<string>
//display<string>onthescreen.
//
//-thenameofthepackagezipfile.
//
注意看这段注释,它解释了以下代码的行为。结合代码,可知:
1)将会创建新的进程,执行:/tmp/update_binary
2)同时,会给该进程传入一些参数,其中最重要的就是一个管道fd,供新进程与原进程通信。
3)新进程诞生后,原进程就变成了一个服务进程,它提供若干UI更新服务:
a)progress
b)set_progress
c)ui_print
这样,新进程就可以通过老进程的UI系统完成显示任务。而其他功能就靠它自己了。
malloc(sizeof(char*)*5);
binary;
EXPAND(RECOVERY_API_VERSION);//definedinAndroid.mk
malloc(10);
"%d",pipefd[1]);
NULL;
pid=
"E:Can'trun%s(%s)/n",
binary,
fdopen(pipefd[0],"r");
from_child)!=
strtok(
"/n");
"progress")==0){
"/n");
"/n");
NULL);
strtol(seconds_s,
"set_progress")==0){
"/n");
NULL);
"ui_print")==0){
strtok(
||
WEXITSTATUS(
这样,我们又回到了升级包中的文件:META-INF/com/google/android/update-binary,下回继续研究。
相关文章推荐
- Android OTA 升级(四):进入根文件系统
- Android OTA 升级之四:进入根文件系统
- Android OTA 升级之四:进入根文件系统
- Android OTA 升级之四:进入根文件系统
- Android OTA 升级之四:进入根文件系统
- Android OTA 升级(四):进入根文件系统
- Android OTA 升级之四:进入根文件系统
- Android OTA 升级之四:进入根文件系统
- Android OTA 升级(四):进入根文件系统
- Android OTA 升级之四:进入根文件系统
- Android OTA 升级(四):进入根文件系统
- Android OTA 升级(四):进入根文件系统 .
- Android OTA 升级(四):进入根文件系统
- Android OTA 升级之四:进入根文件系统
- Android OTA 升级之四:进入根文件系统
- 解决android系统进行OTA升级失败时进入recovery界面不能自动重启问题
- Android OTA 升级(四):进入根文件系统
- Android A/B System OTA分析(四)系统的启动和升级
- Android 系统 '七夕'巨献 VIVO Xplay 基于ViVo官方稳定内核,完美root,适度美化,降噪点,完美支持官方OTA升级
- Android A/B System OTA分析(四)系统的启动和升级