Recovery模式下的文本显示

4330阅读 0评论2017-03-29 UC_JonLee
分类:Android平台

本文转载址:http://blog.csdn.net/a_flying_bird/article/details/18669783


概要

本文讨论在recovery模式如何支持多语言的文本显示。涉及如下几个要点:


问题

手机进入Recovery模式之后,会有安装的小动画、以及文本提示信息,如:


而这个信息并不是直接用C语言输出的字符串。因为在recovery模式下,Android并没有加载语言库。那么Android如何实现多语言的文本显示呢?


总体思路

事实上,Android预先绘制了包括各种语言(严格来讲并不完备)的文本图片,做成一个png文件。然后在recovery初始化的时候,根据当前“语言”而从图片文件中提取出对应该语言的提示信息。


比如,“正在安装系统更新...”就是\bootable\recovery\res\images\installing_text.png中的一部分:


接下来就分析如何从一个完整的png文件中生成“当前语言”所需要的那部分。


Locale

首先谈语言的问题。


在recovery.cpp中有如下一部分代码:


[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int arg;  
  2. while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {  
  3.     switch (arg) {  
  4.     case 'p': previous_runs = atoi(optarg); break;  
  5.     case 's': send_intent = optarg; break;  
  6.     case 'u': update_package = optarg; break;  
  7.     case 'w': wipe_data = wipe_cache = 1; break;  
  8.     case 'c': wipe_cache = 1; break;  
  9.     case 't': show_text = 1; break;  
  10.     case 'x': just_exit = truebreak;  
  11.     case 'l': locale = optarg; break;  
  12.     case '?':  
  13.         LOGE("Invalid command argument\n");  
  14.         continue;  
  15.     }  
  16. }  
  17.   
  18. if (locale == NULL) {  
  19.     load_locale_from_cache();  
  20. }  
  21. printf("locale is [%s]\n", locale);  

注意其中的locale变量,它就是用于做本地化处理的。——可以简单地认为就是“当前语言”。这个locale是需要从Android应用层作为参数传进来,如“--locale=en_GB”。据此,UI部分根据这个locale从png中加载对应的部分。



UI对应的部分如下,即LoadLocalizedBitmap():


[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // \bootable\recovery\screen_ui.cpp  
  2.   
  3. void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, gr_surface* surface) {  
  4.     int result = res_create_localized_surface(filename, surface);  
  5.     if (result < 0) {  
  6.         LOGE("missing bitmap %s\n(Code %d)\n", filename, result);  
  7.     }  
  8. }  
  9.   
  10. void ScreenRecoveryUI::Init()  
  11. {  
  12.     gr_init();  
  13.   
  14.     text_col = text_row = 0;  
  15.     text_rows = gr_fb_height() / CHAR_HEIGHT;  
  16.     if (text_rows > kMaxRows) text_rows = kMaxRows;  
  17.     text_top = 1;  
  18.   
  19.     text_cols = gr_fb_width() / CHAR_WIDTH;  
  20.     if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;  
  21.   
  22.     LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);  
  23.     backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];  
  24.     LoadBitmap("icon_error", &backgroundIcon[ERROR]);  
  25.     backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];  
  26.   
  27.     LoadBitmap("progress_empty", &progressBarEmpty);  
  28.     LoadBitmap("progress_fill", &progressBarFill);  
  29.   
  30.     LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]);  
  31.     LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]);  
  32.     LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]);  
  33.     LoadLocalizedBitmap("error_text", &backgroundText[ERROR]);  

进一步调用:


[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // \bootable\recovery\minui\resources.c  
  2.   
  3. int res_create_localized_surface(const char* name, gr_surface* pSurface) {  
  4.     char resPath[256];  
  5.     GGLSurface* surface = NULL;  
  6.     int result = 0;  
  7.     unsigned char header[8];  
  8.     png_structp png_ptr = NULL;  
  9.     png_infop info_ptr = NULL;  
  10.   
  11.     *pSurface = NULL;  
  12.   
  13.     snprintf(resPath, sizeof(resPath)-1"/res/images/%s.png", name);  
  14.     resPath[sizeof(resPath)-1] = '\0';  
  15.     FILE* fp = fopen(resPath, "rb");  
  16.     if (fp == NULL) {  
  17.         result = -1;  
  18.         goto exit;  
  19.     }  
  20.   
  21.     size_t bytesRead = fread(header, 1sizeof(header), fp);  
  22.     if (bytesRead != sizeof(header)) {  
  23.         result = -2;  
  24.         goto exit;  
  25.     }  
  26.   
  27.     if (png_sig_cmp(header, 0sizeof(header))) {  
  28.         result = -3;  
  29.         goto exit;  
  30.     }  
  31.   
  32.     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULLNULLNULL);  
  33.     if (!png_ptr) {  
  34.         result = -4;  
  35.         goto exit;  
  36.     }  
  37.   
  38.     info_ptr = png_create_info_struct(png_ptr);  
  39.     if (!info_ptr) {  
  40.         result = -5;  
  41.         goto exit;  
  42.     }  
  43.   
  44.     if (setjmp(png_jmpbuf(png_ptr))) {  
  45.         result = -6;  
  46.         goto exit;  
  47.     }  
  48.   
  49.     png_init_io(png_ptr, fp);  
  50.     png_set_sig_bytes(png_ptr, sizeof(header));  
  51.     png_read_info(png_ptr, info_ptr);  
  52.   
  53.     size_t width = info_ptr->width;  
  54.     size_t height = info_ptr->height;  
  55.     size_t stride = 44 * width;  
  56.   
  57.     int color_type = info_ptr->color_type;  
  58.     int bit_depth = info_ptr->bit_depth;  
  59.     int channels = info_ptr->channels;  
  60.   
  61.     if (!(bit_depth == 8 &&  
  62.           (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) {  
  63.         return -7;  
  64.         goto exit;  
  65.     }  
  66.   
  67.     unsigned char* row = malloc(width);  
  68.     int y;  
  69.     for (y = 0; y < height; ++y) {  
  70.         png_read_row(png_ptr, row, NULL);  
  71.         int w = (row[1] << 8) | row[0];  
  72.         int h = (row[3] << 8) | row[2];  
  73.         int len = row[4];  
  74.         char* loc = row+5;  
  75.   
  76.         if (y+1+h >= height || matches_locale(loc)) {  
  77.             printf("  %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);  
  78.   
  79.             surface = malloc(sizeof(GGLSurface));  
  80.             if (surface == NULL) {  
  81.                 result = -8;  
  82.                 goto exit;  
  83.             }  
  84.             unsigned char* pData = malloc(w*h);  
  85.   
  86.             surface->version = sizeof(GGLSurface);  
  87.             surface->width = w;  
  88.             surface->height = h;  
  89.             surface->stride = w; /* Yes, pixels, not bytes */  
  90.             surface->data = pData;  
  91.             surface->format = GGL_PIXEL_FORMAT_A_8;  
  92.   
  93.             int i;  
  94.             for (i = 0; i < h; ++i, ++y) {  
  95.                 png_read_row(png_ptr, row, NULL);  
  96.                 memcpy(pData + i*w, row, w);  
  97.             }  
  98.   
  99.             *pSurface = (gr_surface) surface;  
  100.             break;  
  101.         } else {  
  102.             int i;  
  103.             for (i = 0; i < h; ++i, ++y) {  
  104.                 png_read_row(png_ptr, row, NULL);  
  105.             }  
  106.         }  
  107.     }  
  108.   
  109. exit:  
  110.     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);  
  111.   
  112.     if (fp != NULL) {  
  113.         fclose(fp);  
  114.     }  
  115.     if (result < 0) {  
  116.         if (surface) {  
  117.             free(surface);  
  118.         }  
  119.     }  
  120.     return result;  
  121. }  

其中有这么一句:


[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. if (y+1+h >= height || matches_locale(loc)) {  

这里是读取预置的png图片中的每一部分,然后判断该部分的locale是否和当前locale一致。matches_locale()函数就在刚才res_create_localized_surface()函数的前面,的代码如下:



[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // \bootable\recovery\minui\resources.c  
  2.   
  3. static int matches_locale(const char* loc) {  
  4.     if (locale == NULL) return 0;  
  5.   
  6.     if (strcmp(loc, locale) == 0) return 1;  
  7.   
  8.     // if loc does *not* have an underscore, and it matches the start  
  9.     // of locale, and the next character in locale *is* an underscore,  
  10.     // that's a match.  For instance, loc == "en" matches locale ==  
  11.     // "en_US".  
  12.   
  13.     int i;  
  14.     for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i);  
  15.     if (loc[i] == '_'return 0;  
  16.   
  17.     return (strncmp(locale, loc, i) == 0 && locale[i] == '_');  
  18. }  

在匹配的时候,会比较字符串。需要说明的是,“语言”是一个概念,但国家/地区是另外一个概念,正如English是英语,但又区分美国英语、英国英语,等等。为此Locale包括了getLanguage()、getCountry()等方法,也包括toString()方法。——后者就是language和country的合一。



在Android预置的图片中,每一个小部分图片数据都包括了locale信息(后面会提到),其中一些locale的字符串只有语言部分,有些就是语言+国家代码,从而区分同一语言的不同国家。再比如一个简单的例子,同为中文,却有简体和繁体之分。


这同时也说明了,在Android应用层让手机进入Recovery模式的时候,传入locale要用“语言+国家”的形式,及Locale.toString()方法;否则recovery时显示的字符串可能就不是预期的效果。


在描述了这些概念之后,可以注意以上代码中locale变量的定义&声明方式。该变量在recovery.cpp中定义为全局变量:


[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. char* locale = NULL;  

在resources.c中,通过extern声明来使用它:



[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. extern char* locale;  

而在screen_ui.cpp中,则局部化了:



[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void SetLocale(const char* locale);  

recovery.cpp中,通过该成员函数来设置locale:



[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Device* device = make_device();  
  2. ui = device->GetUI();  
  3.   
  4. ui->Init();  
  5. ui->SetLocale(locale);  



提取特定Locale的文本图片

接下来再分析如何从Android预置的包括了“各种”locale的字符串中提取出当前locale的字符串。这就是前面提到的res_create_localized_surface()函数。为便于阅读,重复如下:


[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int res_create_localized_surface(const char* name, gr_surface* pSurface) {  
  2.     char resPath[256];  
  3.     GGLSurface* surface = NULL;  
  4.     int result = 0;  
  5.     unsigned char header[8];  
  6.     png_structp png_ptr = NULL;  
  7.     png_infop info_ptr = NULL;  
  8.   
  9.     *pSurface = NULL;  
  10.   
  11.     snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);  
  12.     resPath[sizeof(resPath)-1] = '\0';  
  13.     FILE* fp = fopen(resPath, "rb");  
  14.     if (fp == NULL) {  
  15.         result = -1;  
  16.         goto exit;  
  17.     }  
  18.   
  19.     size_t bytesRead = fread(header, 1, sizeof(header), fp);  
  20.     if (bytesRead != sizeof(header)) {  
  21.         result = -2;  
  22.         goto exit;  
  23.     }  
  24.   
  25.     if (png_sig_cmp(header, 0, sizeof(header))) {  
  26.         result = -3;  
  27.         goto exit;  
  28.     }  
  29.   
  30.     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);  
  31.     if (!png_ptr) {  
  32.         result = -4;  
  33.         goto exit;  
  34.     }  
  35.   
  36.     info_ptr = png_create_info_struct(png_ptr);  
  37.     if (!info_ptr) {  
  38.         result = -5;  
  39.         goto exit;  
  40.     }  
  41.   
  42.     if (setjmp(png_jmpbuf(png_ptr))) {  
  43.         result = -6;  
  44.         goto exit;  
  45.     }  
  46.   
  47.     png_init_io(png_ptr, fp);  
  48.     png_set_sig_bytes(png_ptr, sizeof(header));  
  49.     png_read_info(png_ptr, info_ptr);  
  50.   
  51.     size_t width = info_ptr->width;  
  52.     size_t height = info_ptr->height;  
  53.     size_t stride = 4 * width;  
  54.   
  55.     int color_type = info_ptr->color_type;  
  56.     int bit_depth = info_ptr->bit_depth;  
  57.     int channels = info_ptr->channels;  
  58.   
  59.     if (!(bit_depth == 8 &&  
  60.           (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) {  
  61.         return -7;  
  62.         goto exit;  
  63.     }  
  64.   
  65.     unsigned char* row = malloc(width);  
  66.     int y;  
  67.     for (y = 0; y < height; ++y) {  
  68.         png_read_row(png_ptr, row, NULL);  
  69.         int w = (row[1] << 8) | row[0];  
  70.         int h = (row[3] << 8) | row[2];  
  71.         int len = row[4];  
  72.         char* loc = row+5;  
  73.   
  74.         if (y+1+h >= height || matches_locale(loc)) {  
  75.             printf("  %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);  
  76.   
  77.             surface = malloc(sizeof(GGLSurface));  
  78.             if (surface == NULL) {  
  79.                 result = -8;  
  80.                 goto exit;  
  81.             }  
  82.             unsigned char* pData = malloc(w*h);  
  83.   
  84.             surface->version = sizeof(GGLSurface);  
  85.             surface->width = w;  
  86.             surface->height = h;  
  87.             surface->stride = w; /* Yes, pixels, not bytes */  
  88.             surface->data = pData;  
  89.             surface->format = GGL_PIXEL_FORMAT_A_8;  
  90.   
  91.             int i;  
  92.             for (i = 0; i < h; ++i, ++y) {  
  93.                 png_read_row(png_ptr, row, NULL);  
  94.                 memcpy(pData + i*w, row, w);  
  95.             }  
  96.   
  97.             *pSurface = (gr_surface) surface;  
  98.             break;  
  99.         } else {  
  100.             int i;  
  101.             for (i = 0; i < h; ++i, ++y) {  
  102.                 png_read_row(png_ptr, row, NULL);  
  103.             }  
  104.         }  
  105.     }  
  106.   
  107. exit:  
  108.     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);  
  109.   
  110.     if (fp != NULL) {  
  111.         fclose(fp);  
  112.     }  
  113.     if (result < 0) {  
  114.         if (surface) {  
  115.             free(surface);  
  116.         }  
  117.     }  
  118.     return result;  
  119. }  



这段代码看起来很长,实际上一半的代码都是用于png的图片的通常的调用方法。——这在另外一篇文章中介绍。


简单来讲,分为3个部分:

前面两种可以在另外一篇文章中找到相关信息,这里单说IDAT。通过代码可以看到,除了IDAT的Type和CRC部分,剩下的就是每个Locale的子信息。而每个Local的构成又分成两部分:

其中,width和height是图片数据对应的图片的宽度和高度(pixel),而length是指后面locale字符串的长度。可以看到代码中并没有真正用到len这个信息,因为实际的locale字符串很短,通常最多就5个字符串(再加一个\0结束符)。因此,width, height, length, locale共4种信息放在一个row中绰绰有余。


代码通过循环,遍历每一种locale,直到找到匹配项。如果都不匹配,即查找失败,此时recovery就不会显示对应的文本,即pSurface=NULL。——为此只要注意到函数一开始即设置为NULL即可。


[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int res_create_localized_surface(const char* name, gr_surface* pSurface) {  
上一篇:Recovery模式本地化文本显示
下一篇:android recovery模式支持adb shell