2014年3月20日星期四

Android 入门 - 定制开机画面

本文并不准备介绍Android的开机画面的显示原理,只从实例的角度介绍如何快速定制Android开机画面。本文假定:
内核源代码目录:/Volumes/android-kernel/source/goldfish
Android源代码目录:/Volumes/android/source

一、开机画面的简单介绍

网上有许多资料都提到,Android设备的开机画面分为三个画面,即:Linux内核Logo,Android启动Logo,Android动态画面。经过研究分析,Android设备默认只显示Android动画,那么如何显示内核Logo和启动Logo呢?和两条编译配置有关:
  1. CONFIG_FRAMEBUFFER_CONSOLE:打开该配置选项,就可以显示Android启动Logo。但出现问题:默认情况下是Android文字和Android动画交替闪烁。
  2. CONFIG_LOGO:打开该配置选项,并且CONFIG_FRAMEBUFFER_CONSOLE也开启的情况下,可以显示内核Logo。此项无问题。
这样的情况下,制作出来的Android内核不只是在启动时出现闪烁,而且运行后也会么一直这么闪烁,本文后面将介绍如何解决这个问题。

二、定制内核Logo

内核Logo默认是一个Linux企鹅图标,该图标只支持224种颜色,图标格式为:ppm。在Android内核源代码中的路径为:drivers/video/logo/logo_linux_clut224.ppm。所以,我们只需要替换此图标,则重新编译即可。

2.1、安装netpbm工具

Mac OS X下没有直接生成PPM格式图片的工具,可用netpbm转换。要安装netpbm工具,请先安装MacPorts,本文不描述如何安装和使用MacPorts,可在网上搜索安装和使用教程。
# sudo port selfupdate
# sudo port install netpbm
sudo port selfupdtae是让MacPorts先自我更新,sudo port install netpbm安装netpbm。

2.2、PPM图片转换

将预先做好的图片(假定为:MyLinux.png),转换为logo_linux_clut224.ppm格式:
# pngtopnm MyLinux.png > logo_linux_clut.pnm
# pnmquant 224 logo_linux_clut.pnm > logo_linux_clut224.pnm
# pnmtoplainpnm logo_linux_clut224.pnm > logo_linux_clut224.ppm
# cp logo_linux_clut224.ppm /Volumes/android-kernel/source/goldfish/drivers/video/logo/

2.3、配置

许多教程中描述了使用make menuconfig来定制CONFIG_FRAMEBUFFER_CONSOLE和CONFIG_LOGO两个变量,这是一种方法,但在Mac环境中,make menuconfig不能正常运行,总是出现ncurses的相关库找不到,折腾了很久之后,放弃使用make menuconfig。
还有一种方法是使用make config,但这种方法要按N次回车,并仔细对比选项,太费事了。这里介绍一种快速的方法:
在编译内核的时候,我们使用了make goldfish_armv7_defconfig来配置的,只要将该默认配置修改一下,加入上面两个配置项至这个默认配置中,重新运行即可。命令如下:
# cd /Volumes/android-kernel/source/goldfish
# vi arch/arm/configs/goldfish_armv7_defconfig
在文件末尾加入:
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_LOGO=y

按 wq 保存退出。

2.4、去掉光标

开启FRAME_BUFFER_CONSOLE后,会显示光标,需要修改源码解决,网上的方案比较复杂,经研究,这里介绍一个简单方法:
# vi drivers/video/console/softcursor.c
找到soft_cursor(...)函数,只留最后一句:return 0; 其他行注释掉:
int soft_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
/*      struct fbcon_ops *ops = info->fbcon_par;
        unsigned int scan_align = info->pixmap.scan_align - 1;
        unsigned int buf_align = info->pixmap.buf_align - 1;
        unsigned int i, size, dsize, s_pitch, d_pitch;
        struct fb_image *image;
        u8 *src, *dst;
...
        fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, image->height);
        image->data = dst;
        info->fbops->fb_imageblit(info, image);
*/      return 0;
}

2.5、编译

# export PATH=/Volumes/android/source/prebuilts/gcc/darwin-x86/arm/arm-eabi-4.7/bin/:$PATH
# make goldfish_armv7_defconfig
# make -j16
编译完成后,会在最后显示如下内容:
...
  OBJCOPY arch/arm/boot/zImage
  Kernel: arch/arm/boot/zImage is ready

2.6、运行

运行如下指令:
# emulator -kernel arch/arm/boot/zImage
运行可以成功显示开机Logo,但出现Android文字和Android动画交替闪烁的情况。

这个问题是由于启用CONFIG_FRAMEBUFFER_CONSOLE编译项后,init进程如果找不到“initlogo.rle”,就会进入text console模式,而后面的第三屏为动画的图形模式,从而造成两个界面交替闪烁的情况。分析过程如下:
先进入Android源代码根目录,注意不是内核目录。
# vi /Volumes/android/source/system/core/init/init.c
找到console_init_action函数,这个是console启动函数,代码如下:
...
static int console_init_action(int nargs, char **args)
{
    int fd;

    if (console[0]) {
        snprintf(console_name, sizeof(console_name), "/dev/%s", console);
    }

    fd = open(console_name, O_RDWR);
    if (fd >= 0)
        have_console = 1;
    close(fd);

    if( load_565rle_image(INIT_IMAGE_FILE) ) {
        fd = open("/dev/tty0", O_WRONLY);
        if (fd >= 0) {
            const char *msg;
                msg = "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"  // console is 40 cols x 30 lines
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "             A N D R O I D ";
            write(fd, msg, strlen(msg));
            close(fd);
        }
    }
    return 0;
}
可以看到console_init_action函数会调用load_565rle_image函数来装入initlogo.rle文件,load_565rle_image函数在system/core/init/logo.c中。打开后的代码如下:
...
/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */

int load_565rle_image(char *fn)
{
    struct FB fb;
    struct stat s;
    unsigned short *data, *bits, *ptr;
    unsigned count, max;
    int fd;

    if (vt_set_mode(1))
        return -1;

    fd = open(fn, O_RDONLY);
    if (fd < 0) {
        ERROR("cannot open '%s'\n", fn);
        goto fail_restore_text;
    }

...

    munmap(data, s.st_size);
    fb_update(&fb);
    fb_close(&fb);
    close(fd);
    unlink(fn);
    return 0;

fail_unmap_data:
    munmap(data, s.st_size);
fail_close_file:
    close(fd);
fail_restore_text:
    vt_set_mode(0);
    return -1;
}
由上面的代码可以看出,当装入initlogo.rle失败后,就走到fail_restore_text了,然后调用vt_set_mode(0),恢复成文本模式了。
现在问题找到了,是因为找不到initlogo.rle,系统回到text模式了。解决问题的方案有两个:
  1. 制作initlogo.rle,即Android启动静态画面,见后文。
  2. 如果我不想要initlogo.rle怎么办呢?很简单,修改源代码。注释掉上面代码中的fail_restore_text: 后面一句vt_set_mode(0);这样即使找不到initlogo.rle,也会进入图形模式,重新make,问题解决。
    # cd /Volumes/android/source
    # vi system/core/init/logo.c
    
    ...
    /* 565RLE image format: [count(2 bytes), rle(2 bytes)] */
    
    int load_565rle_image(char *fn)
    {
    ...
        munmap(data, s.st_size);
        fb_update(&fb);
        fb_close(&fb);
        close(fd);
        unlink(fn);
        return 0;
    
    fail_unmap_data:
        munmap(data, s.st_size);
    fail_close_file:
        close(fd);
    fail_restore_text:
        /*vt_set_mode(0)*/;
        return -1;
    }
    # make -j16

三、定制Android启动Logo

Android启动时的静态画面为initlogo.rle文件,具体制作过程如下:

3.1 安装ImageMagick工具

此工具用于将png图片转换成raw文件,安装过程如下:
# sudo port install ImageMagick
安装完成后,convert指令将可以使用。

3.2 编译to2565.c,生成rgb2565工具

to565.c是Android自带的工具,用于将raw文件转换为rle文件。编译过程如下:
# cd /Volumes/android/source/build/tools/rgb2565/
# gcc -O2 -Wall  -Wno-unused-parameter -o rgb2565 to565.c

3.3 制作initlogo.rle文件

先用Photoshop等软件制作一张与手机分辨率相同大小的图片(模拟器的分辨率为320*480),假定保存的文件名为:initlogo.png。为了看到运行变化,initlogo.png不要和内核Logo的图片一样。
# cd initlogo.png目录
# convert  -depth 8  initlogo.png  rgb:initlogo.raw
# /Volumes/android/source/build/tools/rgb2565/rgb2565 -rle  <initlogo.raw>  initlogo.rle
# cp initlogo.rle /Volumes/android/source/out/target/product/generic/root/

3.4 重新编译

为确保成功,先删除ramdisk.img。
# cd /Volumes/android/source
# rm out/target/product/generic/ramdisk.img
# make -j16
编译完成后,运行。
# cd /Volumes/android-kernel/source/golefish
# emulator -kernel arch/arm/boot/zImage
这时,你应该看到先启动内核Logo,再启动此静态画面,最后启动Android动画了。

四、定制Android开机动画

其实,默认情况下,前面的内核Logo和Andriod的启动Logo是看不见的,如果没有特别需要,是不需要定制的。但Android开机动画默认情况下是能看见的,因此,此动画定制的可能性要大的多。而且,定制开机动画也要容易,没有前面那么多复杂的步骤,这应该是谷歌考虑到此屏幕基本都要被定制才弄成这么简单吧。

4.1 仿Android自带的动画

即Android默认的动画,此动画由两张图片组成:android-logo-mask.png,android-logo-shine.png,mask是由空心的android文字组成,即一个黑色的块,中间用Android文字挖空,这张图片是作为遮罩放在前端。shine则是一个发光的图片,放在mask的后面,通过循环移动shine,就成为Android发光动画。
用Photoshop修改这两张图片,保存在frameworks/base/core/res/assets/images目录下,文件名不变,再make即可。这个涉及资源重新编译,编译过程比较长,耐心等待。

4.2 帧动画

帧动画是指由多帧图片组成的动画。这个设置是最简单的,以致于手机ROOT后,就可以在手机里直接下载一个bootanimation.zip,替换掉/system/media/bootanimation.zip,就能换掉这个开机动画。
bootanimation.zip可以放在/system/media目录或者放在/data/local目录下,/data/local目录下的优先使用。这两个目录在播放上也稍有区别,/system/media可以无限播放,/data/local只能播放10秒。

bootanimation.zip解压后的结构如下:
- desc.txt
- part0
  - 0000.png
  - 0001.png
  - ...
- part1
  - 0000.png
  - 0001.png
  - ...
part0,part1是两个存放动画帧系列图片的目录。desc.txt是一个文本文件,文件的内容如下:
320    480    30
p      1      0      part0
p      0      0      part1
第一行:320  480 30 表示播放分辨率为320*480,30表示每秒播放30帧图片。
第二行:p 1 0 part0  p为标志符,表示将part0目录里的图片,播放一次,间隔时间为0秒。
第三行:p 0 0 part1 p为标志符,表示将part1目录里的图片,重复播放,间隔时间为0秒。

上面的目录和文件准备好后,执行以下指令:
# cd bootanimation所在目录
# zip -0 -r ../bootanimation.zip ./*
# cp ../bootanimation.zip /Volumes/android/source/out/target/product/generic/system/media/
# make snod
先CD进入bootanimation目录后,再执行zip压缩指令,保证不会将bootanimation目录打进包内。-0 表示只存储,不压缩,-r 表示包括子目录
将打好的bootanimation.zip包,拷贝到/system/media目录,再用make snod重新生成。再运行查看效果。
# cd /Volumes/android-kernel/source/golefish
# emulator -kernel arch/arm/boot/zImage
下面是(自上而下依次为内核Logo,启动Logo,启动动画):






没有评论:

发表评论