Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1385626
  • 博文数量: 478
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4833
  • 用 户 组: 普通用户
  • 注册时间: 2014-06-28 11:12
文章分类

全部博文(478)

文章存档

2019年(1)

2018年(27)

2017年(21)

2016年(171)

2015年(258)

我的朋友

分类: Android平台

2015-08-18 09:39:17

/> 在android系统的手机启动时,按下 (音量下+power) 组合键(大多数如此,也有例外)可进入recovery模式。此recovery模式一个重要的功能便是进行系统升级,这是OTA功能实现的基础和关键。由于前段时间一直在进行OTA项目的开发,因此将recovery模式下代码分析整理出来,以备不时之须。

        recovery模式的代码存在于源码目录下的bootable目录中,主函数位于./bootable/recovery/recovery.c文件中。我们将主函数的代码复制如下,并对其中核心代码进行分析。

  1. int  
  2. main(int argc, char **argv) {  
  3.     time_t start = time(NULL);  
  4.   
  5.     // If these fail, there's not really anywhere to complain...  
  6.     freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);  
  7.     freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);  
  8.     printf("Starting recovery on %s", ctime(&start));  
  9.   
  10.     device_ui_init(&ui_parameters);  
  11.     ui_init();  
  12.     ui_set_background(BACKGROUND_ICON_INSTALLING);  
  13.     load_volume_table();    //加载系统分区表  
  14.     get_args(&argc, &argv);     //获取参数信息,包括需要执行的动作(升级、清除/data、清除cache等)及升级包路径  
  15.   
  16.     int previous_runs = 0;  
  17.     const char *send_intent = NULL;    //通过finish_recovery函数传递到main system下的信息  
  18.     const char *update_package = NULL;    //升级包路径  
  19.     int wipe_data = 0, wipe_cache = 0;  
  20.   
  21.     int arg;  
  22.     while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {  
  23.         switch (arg) {  
  24.         case 'p': previous_runs = atoi(optarg); break;  
  25.         case 's': send_intent = optarg; break;  
  26.         case 'u': update_package = optarg; break;    //解析获得升级包路径  
  27.         case 'w': wipe_data = wipe_cache = 1; break;  
  28.         case 'c': wipe_cache = 1; break;  
  29.         case 't': ui_show_text(1); break;  
  30.         case '?':  
  31.             LOGE("Invalid command argument\n");  
  32.             continue;  
  33.         }  
  34.     }  
  35.   
  36. #ifdef HAVE_SELINUX   
  37.     struct selinux_opt seopts[] = {  
  38.       { SELABEL_OPT_PATH, "/file_contexts" }  
  39.     };  
  40.   
  41.     sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);  
  42.   
  43.     if (!sehandle) {  
  44.         fprintf(stderr, "Warning: No file_contexts\n");  
  45.         ui_print("Warning:  No file_contexts\n");  
  46.     }  
  47. #endif   
  48.   
  49.     device_recovery_start();  
  50.   
  51.     printf("Command:");  
  52.     for (arg = 0; arg < argc; arg++) {  
  53.         printf(" \"%s\"", argv[arg]);  
  54.     }  
  55.     printf("\n");  
  56.   
  57.     if (update_package) {  
  58.         // For backwards compatibility on the cache partition only, if  
  59.         // we're given an old 'root' path "CACHE:foo", change it to  
  60.         // "/cache/foo".   
  61.         if (strncmp(update_package, "CACHE:", 6) == 0) {  
  62.             int len = strlen(update_package) + 10;  
  63.             char* modified_path = malloc(len);  
  64.             strlcpy(modified_path, "/cache/", len);  
  65.             strlcat(modified_path, update_package+6, len);  
  66.             printf("(replacing path \"%s\" with \"%s\")\n",  
  67.                    update_package, modified_path);  
  68.             update_package = modified_path;  
  69.         }  
  70.     }  
  71.     printf("\n");  
  72.   
  73.     property_list(print_property, NULL);  
  74.     printf("\n");  
  75.   
  76.     int status = INSTALL_SUCCESS;  
  77.   
  78.     if (update_package != NULL) {  
  79.         status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE);  
  80.         if (status == INSTALL_SUCCESS && wipe_cache) {  
  81.             if (erase_volume("/cache")) {  
  82.                 LOGE("Cache wipe (requested by package) failed.");  
  83.             }  
  84.         }  
  85.         if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");  
  86.     } else if (wipe_data) {  
  87.         if (device_wipe_data()) status = INSTALL_ERROR;  
  88.         if (erase_volume("/data")) status = INSTALL_ERROR;  
  89.         if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;  
  90.         if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");  
  91.     } else if (wipe_cache) {  
  92.         if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;  
  93.         if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");  
  94.     } else {  
  95.         status = INSTALL_ERROR;  // No command specified  
  96.     }  
  97.   
  98.     if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);  
  99.     if (status != INSTALL_SUCCESS || ui_text_visible()) {  
  100.         prompt_and_wait();  
  101.     }  
  102.   
  103.     // Otherwise, get ready to boot the main system...  
  104.     finish_recovery(send_intent);  
  105.     ui_print("Rebooting...\n");  
  106.     android_reboot(ANDROID_RB_RESTART, 0, 0);  
  107.     return EXIT_SUCCESS;  
  108. }  


代码段1 ./bootable/recovery/recovery.c文件中的main函数,即系统进入到recovery模式执行的主流程

        代码分析如下:

代码06-08:打开临时日志文件TEMPORARY_LOG_FILE(定义在recovery.c的开始部分),并写入信息。注意:此文件里的信息最终会被写入到日志文件LAST_LOG_FILE

                       中,LAST_LOG_FILE的定义如下:static const char *LAST_LOG_FILE = "/cache/recovery/last_log";这就意味着可以获得系统升级时详细的LOG信息,这对于

                      OTA项目的开发具有相当重要的作用;

代码10-12:UI相关的操作,未研究,略去;

代码      13:加载recovery模式下系统分区信息,由于需要对某些分区进行读写操作,故需此步。此分区信息以文件形式存在,路径为:./device/<生产厂商>/<产品型号>/

                       recovery.fstab;比如三星crespo4g系列此文件在源码下的全路径为:./device/samsung/crespo4g/recovery.fstab,其内容如下所示:

                      

  1. # mount point   fstype      device  
  2.   
  3. /sdcard     vfat        /dev/block/platform/s3c-sdhci.0/by-name/media  
  4. /system     ext4        /dev/block/platform/s3c-sdhci.0/by-name/system  
  5. /cache      yaffs2      cache  
  6. /data       ext4        /dev/block/platform/s3c-sdhci.0/by-name/userdata  
  7. /misc       mtd         misc  
  8. /boot       mtd     boot  
  9. /recovery   mtd     recovery  
  10. /bootloader mtd     bootloader  
  11. /radio      mtd     radio  

代码段2 recovery.fstab示例

代码      14:调用get_args()获取由常规模式下传入到recovery模式下的参数,这些参数包括执行动作(系统升级、清空data分区、清空cache分区等)及升级包路径,获取来源为

                       BCB(结构体bootloader_message)或COMMAND_FILE(/cache/recovery/command),详细信息将在后文介绍;

代码21-34:解析get_args所获得的参数,如果是在进行OTA操作,我们将通过第26行获取升级包的路径update_package;

 

代码57-93中,系统将根据不同的指令进行不同的操作,包括升级、清除/data分区、清除/cache分区等。

代码57-70:对update_package的预处理,本人所遇到的情况是:在main system传入的路径为/mnt/sdcard/update.zip,而在recovery下需将其转换为/sdcard/update.zip,相

                      关的操作便在此处进行(当然本文使用的为android源码,并未加入自己改动的代码);

代码78-85:升级操作。其实核心代码只有一句,第79行调用install_package函数将升级包中的数据写入到系统相应分区中,我们将在后文中详细介绍此函数;

代码86-90:清空/data分区(貌似就是恢复出厂设置吧?我没详细研究,不敢妄言);

代码91-93:清空/cache分区;

代码99-101:当升级失败或者我们是手动进入recovery模式时,将调用prompt_and_wait(),显示recovery模式下的交互界面,我们在后文中再详细介绍;

代码   104: 调用finish_recovery函数清空BCB块(防止重复进入recovery模式)、将send_intent写入文件(传递到main system)、将日志写入到LAST_LOG_FILE中等;

代码   106: 系统重启。

        以上即系统进入recovery模式下的执行流程,其中涉及到的关键步骤:如何获取参数(get_args()),系统升级(install_package())及交互界面的显示与操作(prompt_and_w-

ait())等将在下一篇文章中进行分析。

阅读(828) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~