您的位置:首页 > 移动开发 > Android开发

Android OTA 升级之四:进入根文件系统

2012-06-04 11:03 363 查看
前言
从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

560main(intargc,char**argv)

561{

562time_tstart=time(NULL);

563

564//Ifthesefail,there'snotreallyanywheretocomplain...

565freopen(TEMPORARY_LOG_FILE,"a",stdout);setbuf(stdout,NULL);

566freopen(TEMPORARY_LOG_FILE,"a",stderr);setbuf(stderr,NULL);

567fprintf(stderr,"Startingrecoveryon%s",ctime(&start));

568


将标准输出和标准错误输出重定位到"/tmp/recovery.log",如果是eng模式,就可以通过adbpull/tmp/recovery.log,看到当前的log信息,这为我们提供了有效的调试手段。后面还会看到,recovery模式运行完毕后,会将其拷贝到cache分区,以便后续分析。


569ui_init();


Recovery使用了一个简单的基于framebuffer的ui系统,叫miniui,这里,进行了简单的初始化(主要是图形部分以及事件部分),并启动了一个event线程用于响应用户按键。


570get_args(&argc,&argv);

从misc分区以及CACHE:recovery/command文件中读入参数,写入到argc,argv,并且,如果有必要,回写入misc分区。这样,如果recovery没有操作成功(比如,升级还没有结束,就拔电池),系统会一直进入recovery模式。提醒用户继续升级,直到成功。


572intprevious_runs=0;

573constchar*send_intent=NULL;

574constchar*update_package=NULL;

575intwipe_data=0,wipe_cache=0;

576

577intarg;

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'?':

586LOGE("Invalidcommandargument/n");

587continue;

588}

589}

590

解析参数,p:previous_runs没有用到,其它含义见前面注释。


591device_recovery_start();


这个函数没干什么。看名字,它給设备制造商提供了一个调用机会,可写入设备相关初始化代码。

592

593fprintf(stderr,"Command:");

594for(arg=0;arg<argc;arg++){

595fprintf(stderr,"/"%s/"",argv[arg]);

596}

597fprintf(stderr,"/n/n");

598

打印出命令,比如,正常启动进入recovery模式,会打印:Command:"/sbin/recovery"

599property_list(print_property,NULL);

600fprintf(stderr,"/n");

601

打印出所有的系统属性(fromdefault.prop)到log文件。


602intstatus=INSTALL_SUCCESS;

603

604if(update_package!=NULL){

605status=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{

616status=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...

625finish_recovery(send_intent);


它的功能如下:

1)将前面定义的intent字符串写入(如果有的话):CACHE:recovery/command

2)将/tmp/recovery.log复制到"CACHE:recovery/log";

3)清空misc分区,这样重启就不会进入recovery模式

4)删除command文件:CACHE:recovery/command;


626ui_print("Rebooting.../n");

627sync();

628reboot(RB_AUTOBOOT);

629returnEXIT_SUCCESS;

630}


重启。

下面我们分析核心函数install_package

install_package

289int

290install_package(constchar*root_path)

291{

292ui_set_background(BACKGROUND_ICON_INSTALLING);

294ui_print("Findingupdatepackage.../n");

295LOGI("Findingupdatepackage.../n");

296ui_show_indeterminate_progress();

297LOGI("Updatelocation:%s/n",root_path);

298

更新UI显示

299if(ensure_root_path_mounted(root_path)!=0){

300LOGE("Can'tmount%s/n",root_path);

301reset_mark_block();

302returnINSTALL_CORRUPT;

303}

304


确保升级包所在分区已经mount,通常为cache分区或者SD分区


305charpath[PATH_MAX]="";

306if(translate_root_path(root_path,path,sizeof(path))==NULL){

307LOGE("Badpath%s/n",root_path);

308reset_mark_block();

309returnINSTALL_CORRUPT;

310}


将根分区转化为具体分区信息.这些信息来自:全局数组:g_roots


313ui_print("Openingupdatepackage.../n");

314LOGI("Openingupdatepackage.../n");

315LOGI("Updatefilepath:%s/n",path);

316

317intnumKeys;

318RSAPublicKey*loadedKeys=load_keys(PUBLIC_KEYS_FILE,&numKeys);

319if(loadedKeys==NULL){

320LOGE("Failedtoloadkeys/n");

321reset_mark_block();

322returnINSTALL_CORRUPT;

323}

324LOGI("%dkey(s)loadedfrom%s/n",numKeys,PUBLIC_KEYS_FILE);


从/res/keys中装载公钥。


326//Giveverificationhalftheprogressbar...

328ui_print("Verifyingupdatepackage.../n");

329LOGI("Verifyingupdatepackage.../n");

330ui_show_progress(

331VERIFICATION_PROGRESS_FRACTION,

332VERIFICATION_PROGRESS_TIME);

333

334interr;

335err=verify_file(path,loadedKeys,numKeys);

336free(loadedKeys);

337LOGI("verify_filereturned%d/n",err);

338if(err!=VERIFY_SUCCESS){

339LOGE("signatureverificationfailed/n");

340reset_mark_block();

341returnINSTALL_CORRUPT;

342}


根据公钥验证升级包verify_file的注释说的很明白:

//LookforanRSAsignatureembeddedinthe.ZIPfilecommentgiven

//thepathtothezip.Verifyitmatchesoneofthegivenpublic

//keys.


344/*Trytoopenthepackage.

345*/

346ZipArchivezip;

347err=mzOpenZipArchive(path,&zip);

348if(err!=0){

349LOGE("Can'topen%s/n(%s)/n",path,err!=-1?strerror(err):"bad");

350reset_mark_block();

351returnINSTALL_CORRUPT;

352}


打开升级包,将相关信息存到ZuoArchive数据机构中,便于后面处理。


354/*Verifyandinstallthecontentsofthepackage.

355*/

356intstatus=handle_update_package(path,&zip);


处理函数,我们后面继续分析。


357mzCloseZipArchive(&zip);

358returnstatus;

359}


关闭zip包,结束处理。

handle_update_package

204staticint

205handle_update_package(constchar*path,ZipArchive*zip)

206{

207//Updateshouldtaketherestoftheprogressbar.

208ui_print("Installingupdate.../n");

209

210intresult=try_update_binary(path,zip);

211register_package_root(NULL,NULL);//Unregisterpackageroot

212returnresult;

213}


它主要调用函数try_update_binary完成功能。

try_update_binary

84
//Ifthepackagecontainsanupdatebinary,extractitandrunit.
85staticint
86
try_update_binary(constchar*path,
ZipArchive*zip){
87const
ZipEntry*binary_entry=
88
mzFindZipEntry(zip,
ASSUMED_UPDATE_BINARY_NAME);
89if(binary_entry==
NULL){
90returnINSTALL_CORRUPT;
91}
92

93char*
binary="/tmp/update_binary";
94
unlink(binary);
95int
fd=
creat(binary,0755);
96if(fd<0){
97
LOGE("Can'tmake%s/n",
binary);
98return1;
99}
100
bool
ok=
mzExtractZipEntryToFile(zip,binary_entry,
fd);
101
close(fd);
102

103if(!ok){
104
LOGE("Can'tcopy%s/n",
ASSUMED_UPDATE_BINARY_NAME);
105return1;
106}

将升级包内文件META-INF/com/google/android/update-binary复制为/tmp/update_binary


108intpipefd[2];
109
pipe(pipefd);
110

111
//Whenexecutingtheupdatebinarycontainedinthepackage,the
112
//argumentspassedare:
113
//
114
//-theversionnumberforthisinterface
115
//
116
//-anfdtowhichtheprogramcanwriteinordertoupdatethe
117
//progressbar.Theprogramcanwritesingle-linecommands:
118
//
119
//progress<frac><secs>
120
//fillupthenext<frac>partofoftheprogressbar
121
//over<secs>seconds.If<secs>iszero,use
122
//set_progresscommandstomanuallycontrolthe
123
//progressofthissegmentofthebar
124
//
125
//set_progress<frac>
126
//<frac>shouldbebetween0.0and1.0;setsthe
127
//progressbarwithinthesegmentdefinedbythemost
128
//recentprogresscommand.
129
//
130
//firmware<"hboot"|"radio"><filename>
131
//arrangetoinstallthecontentsof<filename>inthe
132
//givenpartitiononreboot.
133
//
134
//(APIv2:<filename>maystartwith"PACKAGE:"to
135
//indicatetakingafilefromtheOTApackage.)
136
//
137
//(APIv3:thiscommandnolongerexists.)
138
//
139
//ui_print<string>
140
//display<string>onthescreen.
141
//
142
//-thenameofthepackagezipfile.
143
//
144

注意看这段注释,它解释了以下代码的行为。结合代码,可知:
1)将会创建新的进程,执行:/tmp/update_binary
2)同时,会给该进程传入一些参数,其中最重要的就是一个管道fd,供新进程与原进程通信。
3)新进程诞生后,原进程就变成了一个服务进程,它提供若干UI更新服务:
a)progress
b)set_progress
c)ui_print
这样,新进程就可以通过老进程的UI系统完成显示任务。而其他功能就靠它自己了。

145char**
args=
malloc(sizeof(char*)*5);
146
args[0]=
binary;
147
args[1]=
EXPAND(RECOVERY_API_VERSION);//definedinAndroid.mk
148
args[2]=
malloc(10);
149
sprintf(args[2],
"%d",pipefd[1]);
150
args[3]=(char*)path;
151
args[4]=
NULL;
152

153
pid_t
pid=fork();
154if(pid==0){
155
close(pipefd[0]);
156
execv(binary,
args);
157
fprintf(stderr,
"E:Can'trun%s(%s)/n",
binary,strerror(errno));
158
_exit(-1);
159}
160
close(pipefd[1]);
161

162char
buffer[1024];
163
FILE*from_child=
fdopen(pipefd[0],"r");
164while(fgets(buffer,sizeof(buffer),
from_child)!=NULL){
165char*
command=
strtok(buffer,
"/n");
166if(command==
NULL){
167continue;
168}elseif(strcmp(command,
"progress")==0){
169char*fraction_s=
strtok(NULL,
"/n");
170char*seconds_s=
strtok(NULL,
"/n");
171

172floatfraction=
strtof(fraction_s,
NULL);
173int
seconds=
strtol(seconds_s,NULL,10);
174

175
ui_show_progress(fraction*(1-VERIFICATION_PROGRESS_FRACTION),
176
seconds);
177}elseif(strcmp(command,
"set_progress")==0){
178char*fraction_s=
strtok(NULL,
"/n");
179floatfraction=
strtof(fraction_s,
NULL);
180
ui_set_progress(fraction);
181}elseif(strcmp(command,
"ui_print")==0){
182char*
str=
strtok(NULL,"/n");
183if(str){
184
ui_print(str);
185}else{
186
ui_print("/n");
187}
188}else{
189
LOGE("unknowncommand[%s]/n",
command);
190}
191}
192
fclose(from_child);
193

194int
status;
195
waitpid(pid,&status,0);
196if(!WIFEXITED(status)
||WEXITSTATUS(status)!=0){
197
LOGE("Errorin%s/n(Status%d)/n",
path,
WEXITSTATUS(status));
198returnINSTALL_ERROR;
199}
200

201returnINSTALL_SUCCESS;
202}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: