
项目名称: 基于DM3730的Android系统
启动过程分析和Logo移植
1.项目开发目的
本次项目研究和开发的主要目的如下:
(1)基于DM3730处理器研究Android系统的启动过程及其主要流程;
(2)基于Android系统的启动流程init进程深入理解。
(3)移植Android系统启动过程中的Logo和相关动画;
(4)修改Android系统的指定桌面,让系统启动后直接跳入自己的开发的应用程序。
2.项目开发内容
本次项目需要完成的主要内容有如下几点:
(1)在Ubuntu11.04上下载Android专用源码,完成Android系统的软件开发环境的搭建;
(2)在EVM37x开发板上搭建Android系统的硬件开发环境;
(3)Android系统的init进程和init.rc文件简单解析;
(4)Android系统启动的动画和Logo的修改;
(5)Android系统桌面源码的修改,实现直接调入自己的应用程序。
3.项目开发软硬件设备
(1)硬件平台:TI高性能处理器DM3730,TI核心开发板EM/AM37xx EVM板
(2)操作系统:Android ICS4.0.3
(3)PC端的开发平台:Ubuntu11.04
(4)开发所需的IDE :TI集成开发环境CCS5.1
4.项目开发流程
4.1 项目开发前期准备
4.1.1 Android源码下载和编译
本项目是在Ubuntu宿主机上完成Android源码的下载和编译。在此之前需要完成以下工作,主要包括各种Android开发所需的插件,编译时所需的基础库以及开发调试的minicom终端等。
sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"
sudo add-apt-repository "deb-src http://archive.canonical.com/ubuntu lucid partner"
sudo apt-get update
sudo apt-get install git-core gnupg sun-java6-jdk flex bison gperf libsdl-dev libesd0-dev libwxgtk2.6-dev build-essential zip curl libncurses5-dev zlib1g-dev minicom tftpd uboot-mkimage expect
sudo update-java-alternatives -s java-6-sun
repo init -u git://gitorious.org/rowboat/manifest.git -m init -u git://gitorious.org/rowboat/manifest.git -m TI-Android-ICS-4.0.3-DevKit-3.0.0.xml
repo sync
./.repo/repo/repo sync –local-only
下载Android源码的过程和命令:
Android源码的编译:包括环境变量的设定,x-loader、u-boot、Linux Kernel以及Android文件系统。
环境变量:
export
PATH=~source/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin:$PATH
x-loader编译:
make CROSS_COMPILE=arm-eabi- distclean
make CROSS_COMPILE=arm-eabi- omap3evm_config
make CROSS_COMPILE=arm-eabi-
u-boot编译:
make CROSS_COMPILE=arm-eabi- distclean
make CROSS_COMPILE=arm-eabi- omap3_evm_config
make CROSS_COMPILE=arm-eabi-
Linux Kernel编译:
make ARCH=arm CROSS_COMPILE=arm-eabi- distclean
make ARCH=arm CROSS_COMPILE=arm-eabi-omap3_evm_android_defconfig
make ARCH=arm CROSS_COMPILE=arm-eabi- uImage
make TARGET_PRODUCT=omap3evm OMAPES=5.x -j8
Android文件系统编译:
整个系统编译过程约3个小时左右,占用空间达12G,因此对PC机的要求较高。
Android文件系统制作:
cd /home/gzz/DM3730/source/out/target/product/omap3evm
cp -r root/* android_rootfs/
cp -r system android_rootfs
../../../../build/tools/mktarball.sh ../../../host/linux-x86/bin/fs_get_stats android_rootfs . rootfs rootfs.tar.bz2
到此为止,整个Android系统的各个部分都编译和制作完成。
4.1.2 EVM板硬件环境的搭建
这里说的硬件开发环境的搭建,主要是指将上述编译好的Android系统所需的xloader、u-boot、Linux kernel和文件系统镜像通过tftp协议下载到Linux的NandFlash上去。
使用如下命令进行镜像文件的烧录:
OMAP3_EVM # tftp 0x80000000 MLO
OMAP3_EVM # nand erase 0x0 0x50000
OMAP3_EVM # nandecc hw 2
OMAP3_EVM # nand write 0x80000000 0x0 0x50000
通过以上主要内容,顺利完成项目开发所需的软硬件开发环境的搭建,为后续项目的顺利进行打下了良好的基础。
4.2 Android启动init进程和init.rc文件的解析
Android中的内核启动后,kernel会启动第一个用户级别的进程:init,它是一个由内核启动的用户级进程。内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级别的进程,完成系统的引导。init始终是第一个进程,位于/sbin/init下面。而init.rc是init进程启动读取的第一个文件,该文件时一个配置Android系统所需要启动的服务和相关Android应用程序最初始化的配置。
因为它与Android启动的界面有直接的联系,所以为了项目的顺利进行,有必要先对其进行理解和熟悉。
分析init进程需要结合init.rc进行,在这里主要通过分析部分代码,做合理的解释。Init进程的源代码位于Android源码的system/core/init/init.c下面,由于代码过于庞大这里不在粘贴。
1. uevent进程
if (!strcmp(basename(argv[0]), "ueventd"))
return ueventd_main(argc, argv);
这个函数是取出argv中的第一个参数,比如/sbin/ueventd,则basename为ueventd。android系统第一次启动的进程名init,所以该函数ueventd_main不执行,该函数的真正执行在init启动service ueventd /sbin/ueventd后,fork出一个子进程,execve启动/sbin/ueventd后,实际上该函数是对init的符合链接,也就是ueventd进程执行起来后执行的代码还是init.c中的main,因此不同的进程名会去执行相同的main函数。
ueventd_main函数的主要功能:在Linux系统中现在都使用uevent机制来管理设备的 热插拔事件,给用户空间权利来完成一些设备文件节点的创建。这种机制是建立在socket的通信机制上,用户空间和内核驱动进行交互,详细的机制没有去了解过。是linux2.6的版本中常用的机制。比如驱动出现device_create等时,会向用户空间报告一个uevent事件,用户空间由uevent进程解析后去创建设备节点。2.init.rc的解析
INFO("reading config file\\n");
init_parse_config_file("/init.rc");
init.rc是一个配置文件,内部有许多的语言规则,所有语言会在init_parse_config_file中进行解析。调用流程如下init_parse_config_file—>read_file—>parse_config.
parse_config(源代码略)。
通过对这个文件的解析,会启动Android的应用程序,使得Android进入Java的世界。
4.3 Android启动Logo和动画移植
在这里主要对Android系统的3个画面进行移植,一是Linux内核在启动加载Frambuffer帧缓冲驱动时会调用一个函数完成Linux的Logo图标的显示。二是Android的第一个init用户进程启动会调用函数完成一个图片的显示。三是Android系统中Java应用程序启动时显示出来的动画效果bootanimation。
4.3.1 Android启动第一个Logo图标移植
Android系统是基于Linux内核设计而成的,Linux下使用帧缓冲(Framebuffer)的概念来表示一个显示接口,通俗理解就表示一块LCD。帧缓冲区的相关驱动在内核启动时调用fbmem_init,在该函数中主要完成使用register_chrdev来注册了一个名称为fb的字符设备,最后调用函数class_create在/sys/class目录下创建了一个graphics目录等。同样的驱动加载中会调用硬件平台相关LCD的驱动初始化函数omapfb_init。
这个函数会针对对个fb,完成相关节点的创建在/dev/graphics/fb0,fb1...等。主设备号为29一旦完成创建,会调用fb_notifier_call_chain,函数通知控制fb的console(理解为控制台).每一个fb都会对应于一个console来控制。而Logo的显示启动,就是在fbcon_init和fbcon_switch中来完成的。在fbcon_init中使用fbcon_prepare_logo函数准备需要显示的Logo数据源,在fbcon_switch使用fb_show_logo来显示Logo。
本项目中需要将一张Jpg的图片作为Logo样式的图标显示在LCD上面。实现的步骤如下:
1.为了能让这部分的Logo图标能够正常显示,需要在编译时对.config文件添加一定的配置:
CONFIG_FRAMEBUFFER_CONSOLE
CONFIG_LOGO
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
完成这些配置之后的Logo才可以正常显示,否则内核在启动驱动加载时,显示Logo图标的函数不会执行相关的操作。
图标的格式转换。
Linux启动图标格式不直接支持jpg格式,主要支持ppm、pbm格式的图像文件,因此需要先将图像进行适当的转换:
1)使用gimp image软件随意修改图片的像素大小
2)使用终端命令完成
3)使用如下命令完成最终的转换(被转换的文件必须是png格式)
# pnmquant 224 xxx.pnm > xxx224.pnm
最终生成一张xxxx224.pnm格式的图像文件。
完成图片的准备工作后,需要修改部分Linux内核的源码,具体的操作如下进行:
a.对Makefile文件做一定的修改
添加如下内容:
obj-$(CONFIG_LOGO_ICS_CLUT224) +=logo_ics_clut224.
b.对logo.c等源文件做一定的修改
添加如下内容:
#ifdef CONFIG_LOGO_ICS_CLUT224
logo = &logo_ics_clut224; //gzz
printk("depth=%d,logo=logo_ics_clut224\\n",depth);//by gzz
#endif
c.修改部分代码让图片显示在LCD在正中间
主要修改部分在fb_show_logo_line,这个函数实现Logo图标的完全显示。
image.dx = 0;
image.dy = y;
image.dx = (info->var.xres/2) -(logo->width/2);
image.dy = (info->var.yres/2) -(logo->height/2); //by gzz
image.width = logo->width;//140
image.height = logo->height;//153
在这个函数中,要显示的图像信息都保存在里image结构体中,同时Logo图像显示的右上角放在image.dx,image.dy这个坐标上。这里可以通过修改这对坐标值,完成位置的修改。比如这里配置的是显示在LCD(480*0)的正中间。
通过以上a,b.c三个步骤可以实现将任意一张图片作为Logo图像显示在自己的LCD上,给出一个良好的用户效果。
4.3.2 Android启动第二个图标移植
第二个图标的显示主要和init进程有关系。在init进程中会有一个console_init_action函数来完成这个图标的显示,这里直接给出部分代码来解析如下:
static int console_init_action(int nargs, char **args)
{
int fd;
char tmp[PROP_VALUE_MAX];
if (console[0]) {
snprintf(tmp, sizeof(tmp), "/dev/%s", console);
console_name = strdup(tmp);
}
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;
}
a.初始化控制台。init进程在启动的时候,会解析内核的启动参数(保存在文件/proc/cmdline中)。如果发现内核的启动参数中包含有了一个名称为“androidboot.console”的属性,那么就会将这个属性的值保存在字符数组console中。这样我们就可以通过设备文件/dev/ b.显示第二个开机画面。显示第二个开机画面是通过调用函数load_565rle_image来实现的。在调用函数load_565rle_image的时候,指定的开机画面文件为INIT_IMAGE_FILE。INIT_IMAGE_FILE是一个宏,定义在system/core/init/init.h文件中,如下所示: #define INIT_IMAGE_FILE "/initlogo.rle" 图像文件initlogo.rle保存的第二个开机画面的图像格式是565rle的。rle的全称是run-length encoding,翻译为游程编码或者行程长度编码,它可以使用4个字节来描述一个连续的具有相同颜色值的序列。在rle565格式,前面2个字节中用来描述序列的个数,而后面2个字节用来描述一个具体的颜色,其中,颜色的RGB值分别占5位、6位和5位。 c.制作initlogo.rle文件。准备一张png格式的文件,使用Ubuntu自带的图片转换工具执行如下命令: 使用android编译后的rgb2565工具,在android/out/host/linux-x86/bin目录下(android为当前源码所在目录),转换命令如下: rgb2565 -rle < android_logo.raw > initlogo.rle。 通过以上步骤就完成了代码的移植和图片的制作,最后只需要将initlogo.rle文件拷贝到Android的文件系统下面,重新启动就可以看到图片的正常显示。 4.3.3 Android启动第三个动画移植 考虑到第三个启动的动画位于Android的Java层,因此代码量相当庞大,因此只写如何替换启动动画来实现我们预期的效果。 第三个开机画面是由应用程序bootanimation来负责显示的。应用程序bootanimation在启动脚本init.rc中被配置成了一个服务,如下所示: .service bootanim /system/bin/bootanimation user graphics group graphics disabled oneshot 应用程序bootanimation的用户和用户组名称分别被设置为graphics。注意, 用来启动应用程序bootanimation的服务是disable的,即init进程在启动的时候,不会主动将应用程序bootanimation启动起来。当SurfaceFlinger服务启动的时候,它会通过修改系统属性ctl.start的值来通知init进程启动应用程序bootanimation,以便可以显示第三个开机画面,而当System进程将系统中的关键服务都启动起来之后,ActivityManagerService服务就会通知SurfaceFlinger服务来修改系统属性ctl.stop的值,以便可以通知init进程停止执行应用程序bootanimation,即停止显示第三个开机画面。 Android的所有版本都可以添加一个bootanimation.zip文件。内容包括part文件夹 和desc.txt。前者的文件夹下面存放的是连续的png格式文件,后者desc.txt里面是播放信息设置,其内容和格式如下: 480 720 15 p 1 0 part1 p 0 0 part2 其中480 800是指显示的分辨率宽和高,15是图像播放的帧数。p后面的数字分别指播放次数 0和播放时间。 Android系统启动时会文件系统如下目录查找: #defineUSER_BOOTANIMATION_FILE "/data/local/bootanimation.zip" #defineSYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip" 只需要将制作好的动画文件放到指定的目录即可实现自己设计的美观的动画以替换默认的开机画面。 到此为止,完成项目所需完成的主要内容:移植完成了三个动画来替换系统自带的图片,使得产品可以实现更加友好的用户界面,提高产品的销量。 4.3.4 Android桌面移植 这部分的内容主要是完成Android系统启动桌面进程的移植,目的是讲这个默认的桌面进程修改为自己所设计的应用进程,使得用户在使用Android终端设备是可以直接进入自行设计的应用操作界面。 既然是移植桌面系统,那么需要修改的就是桌面的源码,阅读Android系统庞大的源码,可以发现Android源码的Home应用程序就是Launcher进程。Android系统中的应用程序安装好了以后,SystemServer组件接下来就要通过ActivityManagerService来启动Home应用程序Launcher了,Launcher在启动的时候便会通过PackageManagerServic把系统中已经安装好的应用程序以快捷图标的形式展示在桌面上,这样用户就可以使用这些应用程序了。 既然作为Home应用程序有这样的优先级,那他的特点就是在AndroidManifest.xml文件中指定了如下内容: 这几句代码,使得系统可以检索到该应用程序时Home应用程序,所以这里我们可以直接将这部分代码去除,而在自己设计的Android应用程序AndroidManifest.xml文件中指定如上内容,那么就可以完成Home应用程序的替换,也就是第一个启动在LCD上的是自己设计的应用程序。 另一方面,在调试时发现系统的锁屏应用程序并没有被禁止掉,因此需要做如下源码的修改: 在/frameworks/base/policy/src/com/android/internal/policy/impl中的KeyguardViewMediator.java函数全局变量mExternallyEnabled=true变为false,这样就可以再任何情况下,其他app或着显示超时间,不会再去自动锁屏。 这样经过以上的所有内容,使得整个Android系统从Linux内核启动到Android第一个init进程启动,再到进入Java的世界、启动第一个Home程序,都修改为了项目所需要实现的任务和功能,对产品的自主化打下了良好的基础。 5.项目开发总结 整个界面移植项目的顺利完成,使得对Android系统的启动有了更加深刻的了解和应用,对熟悉嵌入式系统的开发和移植有很大的帮助。对Android应用程序的开发和调试更加的熟练。整个项目的开发有较强的客户需求,可以使得终端设备界面更加美观,进一步提高产品的销售量,符合嵌入式系统设计和运营的理念。
