国内最全IT社区平台 联系我们 | 收藏本站
华晨云阿里云优惠2
您当前位置:首页 > php开源 > 综合技术 > Recovery模式本地化文本显示

Recovery模式本地化文本显示

来源:程序员人生   发布时间:2015-04-29 08:00:49 阅读次数:3207次

写在前面:

本篇博文所讨论的内容主要是与大家1起讨论Recovery模式本地化显示文本的原理,和如何使用谷歌提供的recovery_l10n工具实现定制本地化显示的文本。

导读:

首先我们来讨论Recovery模式下本地化文本的显示是如何实现的。

先看两张图,相信很多人都很熟习,第1张是我们恢复出厂设置操作,关机重启进入recovery模式以后所看到的界面,第2张是通过按键进入recovery模式,带有选项菜单的主界面。1般来讲普通用户正常的操作是不会看到第2个界面的,而在第1张图片中我们看到,在绿色小机器人下面有1行字符,这行字符就是本文的关键。

 

1:恢复出厂设置-擦除数据

 

2recovery模式主界面-选项菜单

其实上面这行文本内容其实不是以字符的情势显示的,而是用图片代替,以下图:

 

3:本地化文本图片合成

补充1下,就是说当Recovery模式下需要显示这些文本信息的时候,会根据进入recovery模式前的系统语言来从上面这张图片中截取对应语言的文本信息,也就是说这个信息其实不是直接用C语言打印输出到屏幕上的。

 

Recovery模式下是不支持系统语言库的,但是recovery中文本信息本地化又是与主系统当前语言环境保持同步的,那末,在recovery模式是如何与主系统进行交互的呢?

主系统与recovery通过command文件中特定的参数进行交互的。

1、Framework

        首先来看framework/base/core/java/android/os/RecoverySystem.java中的代码片断:

/** RECOVERY_DIR是用来与recovery系统交互的目录,也就是说主系统与recovery系统是通过文件进行交互的. 详情可了解 bootable/recovery/recovery.c. */ private static File RECOVERY_DIR = new File("/cache/recovery"); private static File COMMAND_FILE = new File(RECOVERY_DIR, "command"); /* 安装指定的更新包并进行重启*/ public static void installPackage(Context context, File packageFile) throws IOException { String filename = packageFile.getCanonicalPath()//得到更新包路径 ...... final String filenameArg = "--update_package=" + filename;//将更新包路径作为参数传递写入Command文件 final String localeArg = "--locale=" + Locale.getDefault().toString();//本地化参数 bootCommand(context, filenameArg, localeArg);//重启,并将参数写入command文件 } /*擦除data和cache分区的数据并重启*/ public static void rebootWipeUserData(Context context, boolean shutdown, String reason) throws IOException { ...... String shutdownArg = null; if (shutdown) { shutdownArg = "--shutdown_after"; } String reasonArg = null; if (!TextUtils.isEmpty(reason)) { reasonArg = "--reason=" + sanitizeArg(reason); } final String localeArg = "--locale=" + Locale.getDefault().toString();//本地化参数 bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg); } /*擦除cache分区的数据并重启*/ public static void rebootWipeCache(Context context, String reason) throws IOException { ...... final String localeArg = "--locale=" + Locale.getDefault().toString();//本地化参数 bootCommand(context, "--wipe_cache", reasonArg, localeArg); } /*重启进入recovery模式,并根据指定的参数指定相对应的操作,如安装更新,擦除用户数据等*/ private static void bootCommand(Context context, String... args) throws IOException { RECOVERY_DIR.mkdirs(); // In case we need it COMMAND_FILE.delete(); // In case it's not writable LOG_FILE.delete(); /*向command文件中写入指定的参数*/ FileWriter command = new FileWriter(COMMAND_FILE); try { for (String arg : args) { if (!TextUtils.isEmpty(arg)) { command.write(arg); command.write(" "); } } } finally { command.close(); } // Having written the command file, go ahead and reboot PowerManager pm=(PowerManager)context.getSystemService(Context.POWER_SERVICE); pm.reboot(PowerManager.REBOOT_RECOVERY); throw new IOException("Reboot failed (no permissions?)"); }

从上面代码告知我们,主系统是通过COMMAND_FILE文件的情势与recovery进行交互,根据不同的命令行参数履行不同的操作,如系统升级、恢复出厂设置等。

2、BCB (bootloader control block)bootloader_message

    BCB主要用来加载和启动系统,并且通过读取flashMISC分区中主系统和recovery的信息,并做出相应处理。BCB既是BootloaderRecovery的通讯接口,也是Bootloader与主系统的接口,为何呢?我们来了解下BCB的结构体:

struct bootloader_message {

    char command[32];

    char status[32];

    char recovery[1024];

};

从上面1部份我们了解到当系统进行恢复出厂设置操作时会将主系统当前的语言环境作为参数写入/bootable/recovery/command中,而上面结构体中的command区域就是主系统与recovery交互式所操作的区域,当主系统想要进入recovery模式时,会修改MISC分区中command区域并重启,而Bootloader会根据command区域中的信息来决定是进入主系统还是recovery系统。

    /system/core/init/signal_handler.c里的wait_for_one_process函数中有以下代码:

 

    android_reboot(ANDROID_RB_RESTART2, 0, "recovery")->

 

        __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,

 

        LINUX_REBOOT_CMD_RESTART2, recovery);

 

因此BCB就成了主系统与recovery进行交互的桥梁。

3、Recovery模式

BCB加载recovery.img来启动recovery模式时,会在recoveryinitrckernel启动完成后启动第1个recovery服务,也就是recovery.cpp,位于/bootable/recovery/目录下。

recovery.c中会首先履行get_args函数(如,get_args(&argc, &argv);),用来获得命令行参数,也就是读取/cache/recovery/command中的命令行参数。get_args不但会获得到命令行参数也会将获得到的参数写入到MISC分区中,这样1旦在履行升级或恢复出厂设置进程中失败,重启以后照旧会进入recovery模式重新履行之前失败的动作。

Recovery模式是支持command文件中特定参数的,如OPTIONS[]中的定义:

static const struct option OPTIONS[] = {

  { "send_intent", required_argument, NULL, 's' }//intent文件中写入数据

  { "update_package", required_argument, NULL, 'u' },//验证ota package路径下更新包文件

  { "wipe_data", no_argument, NULL, 'w' },//擦除data分区

  { "wipe_cache", no_argument, NULL, 'c' },//擦除cache分区

  { "show_text", no_argument, NULL, 't' },//显示主菜单

  { "just_exit", no_argument, NULL, 'x' },//退出并重启

  { "locale", required_argument, NULL, 'l' },//本地化

  { NULL, 0, NULL, 0 },

};

    当我们恢复出厂设置重启进入recovery模式时会首先履行recovery.cppmain函数,recovery系统会将/cache/recovery/command中的内容作为命令行参数传递给main函数:

/bootable/recovery/recovery.cpp

    int arg;

    while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != ⑴) {

        switch (arg) {

        case 'p': previous_runs = atoi(optarg); break;

        case 's': send_intent = optarg; break;

        case 'u': update_package = optarg; break;

        case 'w': wipe_data = wipe_cache = 1; break;

        case 'c': wipe_cache = 1; break;

        case 't': show_text = 1; break;

        case 'x': just_exit = true; break;

        case 'l': locale = optarg; break;

        case '?':

            LOGE("Invalid command argument ");

            continue;

        }

}

if (locale == NULL) {

    load_locale_from_cache();

}

printf("locale is [%s] ", locale);

Device* device = make_device();

ui = device->GetUI();

ui->Init();

ui->SetLocale(locale);

ui->SetBackground(RecoveryUI::NONE);

if (show_text) ui->ShowText(true);

上面getopt被用来解析命令行选项参数,而getopt_long支持长选项的命令行解析,第3个参数指的就是命令行选项参数所组成的字符串,如果单个字符后面跟了1个冒号则表示该选项后面必须跟1个参数,而参数的指针会复制给optarg。在上面这段代码中,参数“l”指的就是本地化。Recovery系统会根据这个参数(如--local=en)来决定第1张图中的文字对应的时第3张图的哪1部份的。那末我们恢复出厂设置操作的时,command中所对应的参数是甚么模样的呢,看下面:

--wipe_data

--locale=en

这样的话,也就解释了case 'l': locale = optarg; break;中变量为什么从optarg中取值了。

然后recovery会通过下面1系列的函数调用来实现对预制png图片中对应的文本信息进行截取,以下:

bootable/recovery/screen_ui.cpp)ScreenRecoveryUI::Init()-->ScreenRecoveryUI::LoadLocalizedBitmap()-->(bootable/recovery/minui/resources.c)res_create_localized_surface();

int res_create_localized_surface(const char* name, gr_surface* pSurface) {

    char resPath[256];

    GGLSurface* surface = NULL;

    int result = 0;

    unsigned char header[8];

    png_structp png_ptr = NULL;

    png_infop info_ptr = NULL;

    *pSurface = NULL;

    snprintf(resPath, sizeof(resPath)⑴, "/res/images/%s.png", name);

    resPath[sizeof(resPath)⑴] = '';

    FILE* fp = fopen(resPath, "rb");

    if (fp == NULL) {

        result = ⑴;

        goto exit;

    }

    size_t bytesRead = fread(header, 1, sizeof(header), fp);

    if (bytesRead != sizeof(header)) {

        result = ⑵;

        goto exit;

    }

    if (png_sig_cmp(header, 0, sizeof(header))) {

        result = ⑶;

        goto exit;

    }

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

    if (!png_ptr) {

        result = ⑷;

        goto exit;

    }

    info_ptr = png_create_info_struct(png_ptr);

    if (!info_ptr) {

        result = ⑸;

        goto exit;

    }

    if (setjmp(png_jmpbuf(png_ptr))) {

        result = ⑹;

        goto exit;

    }

    png_init_io(png_ptr, fp);

    png_set_sig_bytes(png_ptr, sizeof(header));

    png_read_info(png_ptr, info_ptr);

    size_t width = info_ptr->width;

    size_t height = info_ptr->height;

    size_t stride = 4 * width;

    int color_type = info_ptr->color_type;

    int bit_depth = info_ptr->bit_depth;

    int channels = info_ptr->channels;

    if (!(bit_depth == 8 &&

          (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) {

        return ⑺;

        goto exit;

    }

    unsigned char* row = malloc(width);

    int y;

    for (y = 0; y < height; ++y) {

        png_read_row(png_ptr, row, NULL);

        int w = (row[1] << 8) | row[0];

        int h = (row[3] << 8) | row[2];

        int len = row[4];

        char* loc = row+5;

        if (y+1+h >= height || matches_locale(loc)) {//匹配字符

            printf("  %20s: %s (%d x %d @ %d) ", name, loc, w, h, y);

            surface = malloc(sizeof(GGLSurface));

            if (surface == NULL) {

                result = ⑻;

                goto exit;

            }

            unsigned char* pData = malloc(w*h);

            surface->version = sizeof(GGLSurface);

            surface->width = w;

            surface->height = h;

            surface->stride = w; /* Yes, pixels, not bytes */

            surface->data = pData;

            surface->format = GGL_PIXEL_FORMAT_A_8;

            int i;

            for (i = 0; i < h; ++i, ++y) {

                png_read_row(png_ptr, row, NULL);

                memcpy(pData + i*w, row, w);

            }

            *pSurface = (gr_surface) surface;

            break;

        } else {

            int i;

            for (i = 0; i < h; ++i, ++y) {

                png_read_row(png_ptr, row, NULL);

            }

        }

    }

exit:

    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

    if (fp != NULL) {

        fclose(fp);

    }

    if (result < 0) {

        if (surface) {

            free(surface);

        }

    }

    return result;

}


生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生