Android开机logo一直都是各大产品定制的重点,如果IC厂家代码完善的话,那就很简单,直接把做好的logo文件放在预制好的文件夹就可以了。还有一些定制服务做得好的IC厂家,可能还会通过烧录软件直接修改升级包里面的logo文件,修改完毕后,直接烧录完事。
这些二次开发做得比较完善的IC厂家一般是各大平板电脑,手机方案商。但如果android代码并没得到很完善的优化怎么办?如在工业领域的android BSP。本人就遇到过这样的情况。Android logo的定制还是需要我们花费一番心机。
下面我们来分析一下android启动过程中logo的显示过程。鉴于不同的平台,可能还有IC厂家会在uboot里面显示开机logo,这部分在这篇文章里面不会介绍到。此文章介绍的是从内核logo,到init logo,到动态logo的过程。
首先,我们来看看内核logo:
打开内核logo 显示配置:
androidkernel boot logo
-> Device Drivers
-> Graphics support
-> Bootup logo (LOGO [=y])
[*] Standard 224-color Linux logo
保证.config文件里面几项被配置上
CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set CONFIG_LOGO_LINUX_CLUT224=y # CONFIG_LOGO_ADVSIGNAGE_CLUT224 is not set
其代码和图片是在内核目录下的“drivers\video\logo”文件夹下,内核logo图片的方法可以到网上搜索,内核logo的代码和logo的制作方法这里就不做分析了。
接下来是init logo:
看到vendor.mk文件里面
PRODUCT_COPY_FILES += \ device/vendor/machine/initlogo.rle:root/initlogo.rle
相信一般的IC厂家都会用这样的做法,把init logo放到平台相关的目录下,然后在编译的时候拷贝,打包到boot.img 下。
接下来我们看看init logo的显示过程。
首先是init logo名字的定义,至于initlogo.rle的制作,可以参考网上的一些资料。有直接现成的制作软件。
system/core/init/init.h #define INIT_IMAGE_FILE "/initlogo.rle"
在android init过程里面,执行到console_init_action的时候,会把initlogo.rle 加载到内存里面去,然后显示出来。其实为什么会在console_init_action 里面显示logo,这个函数的本意是用来初始化console, 也就是串口的。其实本人也不是很清楚Google为什么要把init logo的显示放到这个函数里面实现,也没看出有什么特殊的含义,如果有人了解,可以告知。
System/core/init/init.c 647 static int console_init_action(int nargs, char **args) 648 { 649 int fd; 650 651 if (console[0]) { 652 snprintf(console_name, sizeof(console_name), "/dev/%s", console); 653 } 654 655 fd = open(console_name, O_RDWR); 656 if (fd >= 0) 657 have_console = 1; 658 close(fd); 662 if( load_565rle_image(INIT_IMAGE_FILE) ) { 665 fd = open("/dev/ttymxc0", O_WRONLY); 666 if (fd >= 0) { 667 const char *msg; 668 msg = "\n" 669 "\n" 670 "\n" 671 "\n" 672 "\n" 673 "\n" 674 "\n" // console is 40 cols x 30 lines 675 "\n" 676 "\n" 677 "\n" 678 "\n" 679 "\n" 680 "\n" 681 "\n" 682 " A N D R O I D "; 683 write(fd, msg, strlen(msg)); 684 close(fd); 685 } 686 } 687 return 0; 688 }
其中load_565rle_image是在system/core/init/logo.c里面定义,如果有兴趣,可以去看看里面做了什么动作。
最后是动态logo的显示过程:
首先还是vendor.mk文件:
PRODUCT_COPY_FILES += \ device/vendor/machine/bootanimation.zip:system/media/bootanimation.zip
跟init logo的做法一样,拷贝到特定的目录下,至于制作的方法,网上也有资料供参考。
接下来,在framework的代码里面会定义bootanimation.zip的存放路径
55 #define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip" 56 #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip" 57 #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
至于为什么会定义了三个名称的路径?大家可以去研究一下,这里就不进行详细分析了。
接下来是加载logo的动作
280 if ((encryptedAnimation && 281 (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) && 282 (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) || 283 284 ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) && 285 (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) || 286 287 ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) && 288 (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) { 289 mAndroidAnimation = false; 290 }
以上是加载logo的过程,这过程里面,如果加载logo成功,会把mAndroidAnimation设置为false,如果mAndroidAnimation为false,就会播放定制好的动态logo的动画,不然,就播放android原始的android动态logo。如下:
bool BootAnimation::threadLoop() { bool r; if (mAndroidAnimation) { r = android(); } else { r = movie(); }
动态logo的显示开始与结束是由SurfaceFlinger来控制,控制过程是通过设置“service.bootanim.exit”属性的值来完成。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp 653 void SurfaceFlinger::startBootAnim() { 654 // start boot animation 655 property_set("service.bootanim.exit", "0"); 656 property_set("ctl.start", "bootanim"); 657 }
显示开始后,SurfaceFlinger会把“service.bootanim.exit”属性设置为0。然后“service.bootanim.exit”会在BootAnimation.cpp里面进行判断,确定是启动了动态logo,才会申请关闭logo。
frameworks/base/cmds/bootanimation/BootAnimation.cpp 383 void BootAnimation::checkExit() { 384 // Allow surface flinger to gracefully request shutdown 385 char value[PROPERTY_VALUE_MAX]; 386 property_get(EXIT_PROP_NAME, value, "0"); 387 int exitnow = atoi(value); 388 if (exitnow) { 389 requestExit(); 390 } 391 }
在启动完成后,SurfaceFlinger在bootFinished方法里面最后把“service.bootanim.exit”属性设置为1.
296 void SurfaceFlinger::bootFinished() 297 { 298 const nsecs_t now = systemTime(); 299 const nsecs_t duration = now - mBootTime; 300 ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); 301 mBootFinished = true; 302 303 // wait patiently for the window manager death 304 const String16 name("window"); 305 sp<IBinder> window(defaultServiceManager()->getService(name)); 306 if (window != 0) { 307 window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)); 308 } 309 310 // stop boot animation 311 // formerly we would just kill the process, but we now ask it to exit so it 312 // can choose where to stop the animation. 313 property_set("service.bootanim.exit", "1"); 314 }
分析到这里,如果想知道更加详细的过程,可以查看android里面相应的代码。