Android系统的开机画面显示过程分析
提到Android系統(tǒng)的UI,我們最先接觸到的便是系統(tǒng)在啟動(dòng)過(guò)程中所出現(xiàn)的畫(huà)面了。Android系統(tǒng)在啟動(dòng)的過(guò)程中,最多可以出現(xiàn)三個(gè)畫(huà)面,每一個(gè)畫(huà)面都用來(lái)描述一個(gè)不同的啟動(dòng)階段。本文將詳細(xì)分析這三個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程,以便可以開(kāi)啟我們對(duì)Android系統(tǒng)UI實(shí)現(xiàn)的分析之路。
第一個(gè)開(kāi)機(jī)畫(huà)面是在內(nèi)核啟動(dòng)的過(guò)程中出現(xiàn)的,它是一個(gè)靜態(tài)的畫(huà)面。第二個(gè)開(kāi)機(jī)畫(huà)面是在init進(jìn)程啟動(dòng)的過(guò)程中出現(xiàn)的,它也是一個(gè)靜態(tài)的畫(huà)面。第三個(gè)開(kāi)機(jī)畫(huà)面是在系統(tǒng)服務(wù)啟動(dòng)的過(guò)程中出現(xiàn)的,它是一個(gè)動(dòng)態(tài)的畫(huà)面。無(wú)論是哪一個(gè)畫(huà)面,它們都是在一個(gè)稱為幀緩沖區(qū)(frame buffer,簡(jiǎn)稱fb)的硬件設(shè)備上進(jìn)行渲染的。接下來(lái),我們就分別分析這三個(gè)畫(huà)面是如何在fb上顯示的。
1. 第一個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程
Android系統(tǒng)的第一個(gè)開(kāi)機(jī)畫(huà)面其實(shí)是Linux內(nèi)核的啟動(dòng)畫(huà)面。在默認(rèn)情況下,這個(gè)畫(huà)面是不會(huì)出現(xiàn)的,除非我們?cè)诰幾g內(nèi)核的時(shí)候,啟用以下兩個(gè)編譯選項(xiàng):
CONFIG_FRAMEBUFFER_CONSOLE
CONFIG_LOGO
第一個(gè)編譯選項(xiàng)表示內(nèi)核支持幀緩沖區(qū)控制臺(tái),它對(duì)應(yīng)的配置菜單項(xiàng)為:Device Drivers ---> Graphics support ---> Console display driver support ---> Framebuffer Console support。第二個(gè)編譯選項(xiàng)表示內(nèi)核在啟動(dòng)的過(guò)程中,需要顯示LOGO,它對(duì)應(yīng)的配置菜單項(xiàng)為:Device Drivers ---> Graphics support ---> Bootup logo。配置Android內(nèi)核編譯選項(xiàng)可以參考在Ubuntu上下載、編譯和安裝Android最新內(nèi)核源代碼(Linux Kernel)一文。
幀緩沖區(qū)硬件設(shè)備在內(nèi)核中有一個(gè)對(duì)應(yīng)的驅(qū)動(dòng)程序模塊fbmem,它實(shí)現(xiàn)在文件kernel/goldfish/drivers/video/fbmem.c中,它的初始化函數(shù)如下所示:
| /*** fbmem_init - init frame buffer subsystem** Initialize the frame buffer subsystem.** NOTE: This function is _only_ to be called by drivers/char/mem.c.**/static int __init fbmem_init(void) {proc_create("fb", 0, NULL, &fb_proc_fops);if (register_chrdev(FB_MAJOR,"fb",&fb_fops))printk("unable to get major %d for fb devs\n", FB_MAJOR);fb_class = class_create(THIS_MODULE, "graphics");if (IS_ERR(fb_class)) {printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));fb_class = NULL;}return 0; } |
這個(gè)函數(shù)首先調(diào)用函數(shù)proc_create在/proc目錄下創(chuàng)建了一個(gè)fb文件,接著又調(diào)用函數(shù)register_chrdev來(lái)注冊(cè)了一個(gè)名稱為fb的字符設(shè)備,最后調(diào)用函數(shù)class_create在/sys/class目錄下創(chuàng)建了一個(gè)graphics目錄,用來(lái)描述內(nèi)核的圖形系統(tǒng)。
模塊fbmem除了會(huì)執(zhí)行上述初始化工作之外,還會(huì)導(dǎo)出一個(gè)函數(shù)register_framebuffer:
| EXPORT_SYMBOL(register_framebuffer); |
這個(gè)函數(shù)在內(nèi)核的啟動(dòng)過(guò)程會(huì)被調(diào)用,以便用來(lái)執(zhí)行注冊(cè)幀緩沖區(qū)硬件設(shè)備的操作,它的實(shí)現(xiàn)如下所示:
| /*** register_framebuffer - registers a frame buffer device* @fb_info: frame buffer info structure** Registers a frame buffer device @fb_info.** Returns negative errno on error, or zero for success.**/int register_framebuffer(struct fb_info *fb_info) {int i;struct fb_event event;......if (num_registered_fb == FB_MAX)return -ENXIO;......num_registered_fb++;for (i = 0 ; i < FB_MAX; i++)if (!registered_fb[i])break;fb_info->node = i;mutex_init(&fb_info->lock);fb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), NULL, "fb%d", i);if (IS_ERR(fb_info->dev)) {/* Not fatal */printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR (fb_info->dev));fb_info->dev = NULL;} elsefb_init_device(fb_info);......registered_fb[i] = fb_info;event.info = fb_info;fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);return 0; } |
由于系統(tǒng)中可能會(huì)存在多個(gè)幀緩沖區(qū)硬件設(shè)備,因此,fbmem模塊使用一個(gè)數(shù)組registered_fb保存所有已經(jīng)注冊(cè)了的幀緩沖區(qū)硬件設(shè)備,其中,每一個(gè)幀緩沖區(qū)硬件都是使用一個(gè)結(jié)構(gòu)體fb_info來(lái)描述的。
我們知道,在Linux內(nèi)核中,每一個(gè)硬件設(shè)備都有一個(gè)主設(shè)備號(hào)和一個(gè)從設(shè)備號(hào),它們用來(lái)唯一地標(biāo)識(shí)一個(gè)硬件設(shè)備。對(duì)于幀緩沖區(qū)硬件設(shè)備來(lái)說(shuō),它們的主設(shè)備號(hào)定義為FB_MAJOR(29),而從設(shè)備號(hào)則與注冊(cè)的順序有關(guān),它們的值依次等于0,1,2等。
每一個(gè)被注冊(cè)的幀緩沖區(qū)硬件設(shè)備在/dev/graphics目錄下都有一個(gè)對(duì)應(yīng)的設(shè)備文件fb<minor>,其中,<minor>表示一個(gè)從設(shè)備號(hào)。例如,第一個(gè)被注冊(cè)的幀緩沖區(qū)硬件設(shè)備在/dev/graphics目錄下都有一個(gè)對(duì)應(yīng)的設(shè)備文件fb0。用戶空間的應(yīng)用程序通過(guò)這個(gè)設(shè)備文件就可以操作幀緩沖區(qū)硬件設(shè)備了,即將要顯示的畫(huà)面渲染到幀緩沖區(qū)硬件設(shè)備上去。
這個(gè)函數(shù)最后會(huì)通過(guò)調(diào)用函數(shù)fb_notifier_call_chain來(lái)通知幀緩沖區(qū)控制臺(tái),有一個(gè)新的幀緩沖區(qū)設(shè)備被注冊(cè)到內(nèi)核中來(lái)了。
幀緩沖區(qū)控制臺(tái)在內(nèi)核中對(duì)應(yīng)的驅(qū)動(dòng)程序模塊為fbcon,它實(shí)現(xiàn)在文件kernel/goldfish/drivers/video/console/fbcon.c中,它的初始化函數(shù)如下所示:
| static struct notifier_block fbcon_event_notifier = {.notifier_call = fbcon_event_notify, };......static int __init fb_console_init(void) {int i;acquire_console_sem();fb_register_client(&fbcon_event_notifier);fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,"fbcon");if (IS_ERR(fbcon_device)) {printk(KERN_WARNING "Unable to create device ""for fbcon; errno = %ld\n",PTR_ERR(fbcon_device));fbcon_device = NULL;} elsefbcon_init_device();for (i = 0; i < MAX_NR_CONSOLES; i++)con2fb_map[i] = -1;release_console_sem();fbcon_start();return 0; } |
這個(gè)函數(shù)除了會(huì)調(diào)用函數(shù)device_create來(lái)創(chuàng)建一個(gè)類(lèi)別為graphics的設(shè)備fbcon之外,還會(huì)調(diào)用函數(shù)fb_register_client來(lái)監(jiān)聽(tīng)?zhēng)彌_區(qū)硬件設(shè)備的注冊(cè)事件,這是由函數(shù)fbcon_event_notify來(lái)實(shí)現(xiàn)的,如下所示:
| static int fbcon_event_notify(struct notifier_block *self,unsigned long action, void *data) {struct fb_event *event = data;struct fb_info *info = event->info;......int ret = 0;......switch(action) {......case FB_EVENT_FB_REGISTERED:ret = fbcon_fb_registered(info);break;......}done:return ret; } |
幀緩沖區(qū)硬件設(shè)備的注冊(cè)事件最終是由函數(shù)fbcon_fb_registered來(lái)處理的,它的實(shí)現(xiàn)如下所示:
| static int fbcon_fb_registered(struct fb_info *info) {int ret = 0, i, idx = info->node;fbcon_select_primary(info);if (info_idx == -1) {for (i = first_fb_vc; i <= last_fb_vc; i++) {if (con2fb_map_boot[i] == idx) {info_idx = idx;break;}}if (info_idx != -1)ret = fbcon_takeover(1);} else {for (i = first_fb_vc; i <= last_fb_vc; i++) {if (con2fb_map_boot[i] == idx)set_con2fb_map(i, idx, 0);}}return ret; } |
函數(shù)fbcon_select_primary用來(lái)檢查當(dāng)前注冊(cè)的幀緩沖區(qū)硬件設(shè)備是否是一個(gè)主幀緩沖區(qū)硬件設(shè)備。如果是的話,那么就將它的信息記錄下來(lái)。這個(gè)函數(shù)只有當(dāng)指定了CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY編譯選項(xiàng)時(shí)才有效,否則的話,它是一個(gè)空函數(shù)。
在Linux內(nèi)核中,每一個(gè)控制臺(tái)和每一個(gè)幀緩沖區(qū)硬件設(shè)備都有一個(gè)從0開(kāi)始的編號(hào),它們的初始對(duì)應(yīng)關(guān)系保存在全局?jǐn)?shù)組con2fb_map_boot中。控制臺(tái)和幀緩沖區(qū)硬件設(shè)備的初始對(duì)應(yīng)關(guān)系是可以通過(guò)設(shè)置內(nèi)核啟動(dòng)參數(shù)來(lái)初始化的。在模塊fbcon中,還有另外一個(gè)全局?jǐn)?shù)組con2fb_map,也是用來(lái)映射控制臺(tái)和幀緩沖區(qū)硬件設(shè)備的對(duì)應(yīng)關(guān)系,不過(guò)它映射的是控制臺(tái)和幀緩沖區(qū)硬件設(shè)備的實(shí)際對(duì)應(yīng)關(guān)系。
全局變量first_fb_vc和last_fb_vc是全局?jǐn)?shù)組con2fb_map_boot和con2fb_map的索引值,用來(lái)指定系統(tǒng)當(dāng)前可用的控制臺(tái)編號(hào)范圍,它們也是可以通過(guò)設(shè)置內(nèi)核啟動(dòng)參數(shù)來(lái)初始化的。全局變量first_fb_vc的默認(rèn)值等于0,而全局變量last_fb_vc的默認(rèn)值等于MAX_NR_CONSOLES - 1。
全局變量info_idx表示系統(tǒng)當(dāng)前所使用的幀緩沖區(qū)硬件的編號(hào)。如果它的值等于-1,那么就說(shuō)明系統(tǒng)當(dāng)前還沒(méi)有設(shè)置好當(dāng)前所使用的幀緩沖區(qū)硬件設(shè)備。在這種情況下,函數(shù)fbcon_fb_registered就會(huì)在全局?jǐn)?shù)組con2fb_map_boot中檢查是否存在一個(gè)控制臺(tái)編號(hào)與當(dāng)前所注冊(cè)的幀緩沖區(qū)硬件設(shè)備的編號(hào)idx對(duì)應(yīng)。如果存在的話,那么就會(huì)將當(dāng)前所注冊(cè)的幀緩沖區(qū)硬件設(shè)備編號(hào)idx保存在全局變量info_idx中。接下來(lái)還會(huì)調(diào)用函數(shù)fbcon_takeover來(lái)初始化系統(tǒng)所使用的控制臺(tái)。在調(diào)用函數(shù)fbcon_takeover的時(shí)候,傳進(jìn)去的參數(shù)為1,表示要顯示第一個(gè)開(kāi)機(jī)畫(huà)面。
如果全局變量info_idx的值不等于-1,那么函數(shù)fbcon_fb_registered同樣會(huì)在全局?jǐn)?shù)組con2fb_map_boot中檢查是否存在一個(gè)控制臺(tái)編號(hào)與當(dāng)前所注冊(cè)的幀緩沖區(qū)硬件設(shè)備的編號(hào)idx對(duì)應(yīng)。如果存在的話,那么就會(huì)調(diào)用函數(shù)set_con2fb_map來(lái)調(diào)整當(dāng)前所注冊(cè)的幀緩沖區(qū)硬件設(shè)備與控制臺(tái)的映射關(guān)系,即調(diào)整數(shù)組con2fb_map_boot和con2fb_map的值。
為了簡(jiǎn)單起見(jiàn),我們假設(shè)系統(tǒng)只有一個(gè)幀緩沖區(qū)硬件設(shè)備,這樣當(dāng)它被注冊(cè)的時(shí)候,全局變量info_idx的值就會(huì)等于-1。當(dāng)函數(shù)fbcon_fb_registered在全局?jǐn)?shù)組con2fb_map_boot中發(fā)現(xiàn)有一個(gè)控制臺(tái)的編號(hào)與這個(gè)幀緩沖區(qū)硬件設(shè)備的編號(hào)idx對(duì)應(yīng)時(shí),接下來(lái)就會(huì)調(diào)用函數(shù)fbcon_takeover來(lái)設(shè)置系統(tǒng)所使用的控制臺(tái)。
函數(shù)fbcon_takeover的實(shí)現(xiàn)如下所示:
| static int fbcon_takeover(int show_logo) {int err, i;if (!num_registered_fb)return -ENODEV;if (!show_logo)logo_shown = FBCON_LOGO_DONTSHOW;for (i = first_fb_vc; i <= last_fb_vc; i++)con2fb_map[i] = info_idx;err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,fbcon_is_default);if (err) {for (i = first_fb_vc; i <= last_fb_vc; i++) {con2fb_map[i] = -1;}info_idx = -1;}return err; } |
全局變量logo_shown的初始值為FBCON_LOGO_CANSHOW,表示可以顯示第一個(gè)開(kāi)機(jī)畫(huà)面。但是當(dāng)參數(shù)show_logo的值等于0的時(shí)候,全局變量logo_shown的值會(huì)被重新設(shè)置為FBCON_LOGO_DONTSHOW,表示不可以顯示第一個(gè)開(kāi)機(jī)畫(huà)面。
中間的for循環(huán)將當(dāng)前可用的控制臺(tái)的編號(hào)都映射到當(dāng)前正在注冊(cè)的幀緩沖區(qū)硬件設(shè)備的編號(hào)info_idx中去,表示當(dāng)前可用的控制臺(tái)與緩沖區(qū)硬件設(shè)備的實(shí)際映射關(guān)系。
函數(shù)take_over_console用來(lái)初始化系統(tǒng)當(dāng)前所使用的控制臺(tái)。如果它的返回值不等于0,那么就表示初始化失敗。在這種情況下,最后的for循環(huán)就會(huì)將全局?jǐn)?shù)組con2fb_map的各個(gè)元素的值設(shè)置為-1,表示系統(tǒng)當(dāng)前可用的控制臺(tái)還沒(méi)有映射到實(shí)際的幀緩沖區(qū)硬件設(shè)備中去。這時(shí)候全局變量info_idx的值也會(huì)被重新設(shè)置為-1。
調(diào)用函數(shù)take_over_console來(lái)初始化系統(tǒng)當(dāng)前所使用的控制臺(tái),實(shí)際上就是向系統(tǒng)注冊(cè)一系列回調(diào)函數(shù),以便系統(tǒng)可以通過(guò)這些回調(diào)函數(shù)來(lái)操作當(dāng)前所使用的控制臺(tái)。這些回調(diào)函數(shù)使用結(jié)構(gòu)體consw來(lái)描述。這里所注冊(cè)的結(jié)構(gòu)體consw是由全局變量fb_con來(lái)指定的,它的定義如下所示:
| /** The console `switch' structure for the frame buffer based console*/static const struct consw fb_con = {.owner = THIS_MODULE,.con_startup = fbcon_startup,.con_init = fbcon_init,.con_deinit = fbcon_deinit,.con_clear = fbcon_clear,.con_putc = fbcon_putc,.con_putcs = fbcon_putcs,.con_cursor = fbcon_cursor,.con_scroll = fbcon_scroll,.con_bmove = fbcon_bmove,.con_switch = fbcon_switch,.con_blank = fbcon_blank,.con_font_set = fbcon_set_font,.con_font_get = fbcon_get_font,.con_font_default = fbcon_set_def_font,.con_font_copy = fbcon_copy_font,.con_set_palette = fbcon_set_palette,.con_scrolldelta = fbcon_scrolldelta,.con_set_origin = fbcon_set_origin,.con_invert_region = fbcon_invert_region,.con_screen_pos = fbcon_screen_pos,.con_getxy = fbcon_getxy,.con_resize = fbcon_resize, }; |
接下來(lái)我們主要關(guān)注函數(shù)fbcon_init和fbcon_switch的實(shí)現(xiàn),系統(tǒng)就是通過(guò)它來(lái)初始化和切換控制臺(tái)的。在初始化的過(guò)程中,會(huì)決定是否需要準(zhǔn)備第一個(gè)開(kāi)機(jī)畫(huà)面的內(nèi)容,而在切換控制臺(tái)的過(guò)程中,會(huì)決定是否需要顯示第一個(gè)開(kāi)機(jī)畫(huà)面的內(nèi)容。
函數(shù)fbcon_init的實(shí)現(xiàn)如下所示:
| static void fbcon_init(struct vc_data *vc, int init) {struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];struct fbcon_ops *ops;struct vc_data **default_mode = vc->vc_display_fg;struct vc_data *svc = *default_mode;struct display *t, *p = &fb_display[vc->vc_num];int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;int cap;if (info_idx == -1 || info == NULL)return;......if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||(info->fix.type == FB_TYPE_TEXT))logo = 0;......if (logo)fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);...... } |
當(dāng)前正在初始化的控制臺(tái)使用參數(shù)vc來(lái)描述,而它的成員變量vc_num用來(lái)描述當(dāng)前正在初始化的控制臺(tái)的編號(hào)。通過(guò)這個(gè)編號(hào)之后,就可以在全局?jǐn)?shù)組con2fb_map中找到對(duì)應(yīng)的幀緩沖區(qū)硬件設(shè)備編號(hào)。有了幀緩沖區(qū)硬件設(shè)備編號(hào)之后,就可以在另外一個(gè)全局?jǐn)?shù)組中registered_fb中找到一個(gè)fb_info結(jié)構(gòu)體info,用來(lái)描述與當(dāng)前正在初始化的控制臺(tái)所對(duì)應(yīng)的幀緩沖區(qū)硬件設(shè)備。
參數(shù)vc的成員變量vc_display_fg用來(lái)描述系統(tǒng)當(dāng)前可見(jiàn)的控制臺(tái),它是一個(gè)類(lèi)型為vc_data**的指針。從這里就可以看出,最終得到的vc_data結(jié)構(gòu)體svc就是用來(lái)描述系統(tǒng)當(dāng)前可見(jiàn)的控制臺(tái)的。
變量logo開(kāi)始的時(shí)候被設(shè)置為1,表示需要顯示第一個(gè)開(kāi)機(jī)畫(huà)面,但是在以下三種情況下,它的值會(huì)被設(shè)置為0,表示不需要顯示開(kāi)機(jī)畫(huà)面:
A. 參數(shù)vc和變量svc指向的不是同一個(gè)vc_data結(jié)構(gòu)體,即當(dāng)前正在初始化的控制臺(tái)不是系統(tǒng)當(dāng)前可見(jiàn)的控制臺(tái)。
B. 全局變量logo_shown的值等于FBCON_LOGO_DONTSHOW,即系統(tǒng)不需要顯示第一個(gè)開(kāi)機(jī)畫(huà)面。
C. 與當(dāng)前正在初始化的控制臺(tái)所對(duì)應(yīng)的幀緩沖區(qū)硬件設(shè)備的顯示方式被設(shè)置為文本方式,即info->fix.type的值等于FB_TYPE_TEXT。
當(dāng)最終得到的變量logo的值等于1的時(shí)候,接下來(lái)就會(huì)調(diào)用函數(shù)fbcon_prepare_logo來(lái)準(zhǔn)備要顯示的第一個(gè)開(kāi)機(jī)畫(huà)面的內(nèi)容。
在函數(shù)fbcon_prepare_logo中,第一個(gè)開(kāi)機(jī)畫(huà)面的內(nèi)容是通過(guò)調(diào)用函數(shù)fb_prepare_logo來(lái)準(zhǔn)備的,如下所示:
| static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,int cols, int rows, int new_cols, int new_rows) {......int logo_height;......logo_height = fb_prepare_logo(info, ops->rotate);......if (logo_lines > vc->vc_bottom) {......} else if (logo_shown != FBCON_LOGO_DONTSHOW) {logo_shown = FBCON_LOGO_DRAW;......} } |
從函數(shù)fb_prepare_logo返回來(lái)之后,如果要顯示的第一個(gè)開(kāi)機(jī)畫(huà)面所占用的控制臺(tái)行數(shù)小于等于參數(shù)vc所描述的控制臺(tái)的最大行數(shù),并且全局變量logo_show的值不等于FBCON_LOGO_DRAW,那么就說(shuō)明前面所提到的第一個(gè)開(kāi)機(jī)畫(huà)面可以顯示在控制臺(tái)中。這時(shí)候全局變量logo_show的值就會(huì)被設(shè)置為FBCON_LOGO_DRAW,表示第一個(gè)開(kāi)機(jī)畫(huà)面處于等待渲染的狀態(tài)。
函數(shù)fb_prepare_logo實(shí)現(xiàn)在文件kernel/goldfish/drivers/video/fbmem.c中,如下所示:
| int fb_prepare_logo(struct fb_info *info, int rotate) {int depth = fb_get_color_depth(&info->var, &info->fix);unsigned int yres;memset(&fb_logo, 0, sizeof(struct logo_data));......if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {depth = info->var.blue.length;if (info->var.red.length < depth)depth = info->var.red.length;if (info->var.green.length < depth)depth = info->var.green.length;}if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) {/* assume console colormap */depth = 4;}/* Return if no suitable logo was found */fb_logo.logo = fb_find_logo(depth);......return fb_prepare_extra_logos(info, fb_logo.logo->height, yres); } |
這個(gè)函數(shù)首先得到參數(shù)info所描述的幀緩沖區(qū)硬件設(shè)備的顏色深度depth,接著再調(diào)用函數(shù)fb_find_logo來(lái)獲得要顯示的第一個(gè)開(kāi)機(jī)畫(huà)面的內(nèi)容,并且保存在全局變量fb_logo的成員變量logo中。
函數(shù)fb_find_logo實(shí)現(xiàn)在文件kernel/goldfish/drivers/video/logo/logo.c文件中,如下所示:
| extern const struct linux_logo logo_linux_mono; extern const struct linux_logo logo_linux_vga16; extern const struct linux_logo logo_linux_clut224; extern const struct linux_logo logo_blackfin_vga16; extern const struct linux_logo logo_blackfin_clut224; extern const struct linux_logo logo_dec_clut224; extern const struct linux_logo logo_mac_clut224; extern const struct linux_logo logo_parisc_clut224; extern const struct linux_logo logo_sgi_clut224; extern const struct linux_logo logo_sun_clut224; extern const struct linux_logo logo_superh_mono; extern const struct linux_logo logo_superh_vga16; extern const struct linux_logo logo_superh_clut224; extern const struct linux_logo logo_m32r_clut224;static int nologo; module_param(nologo, bool, 0); MODULE_PARM_DESC(nologo, "Disables startup logo");/* logo's are marked __initdata. Use __init_refok to tell* modpost that it is intended that this function uses data* marked __initdata.*/ const struct linux_logo * __init_refok fb_find_logo(int depth) {const struct linux_logo *logo = NULL;if (nologo)return NULL;if (depth >= 1) { #ifdef CONFIG_LOGO_LINUX_MONO/* Generic Linux logo */logo = &logo_linux_mono; #endif #ifdef CONFIG_LOGO_SUPERH_MONO/* SuperH Linux logo */logo = &logo_superh_mono; #endif}if (depth >= 4) { #ifdef CONFIG_LOGO_LINUX_VGA16/* Generic Linux logo */logo = &logo_linux_vga16; #endif #ifdef CONFIG_LOGO_BLACKFIN_VGA16/* Blackfin processor logo */logo = &logo_blackfin_vga16; #endif #ifdef CONFIG_LOGO_SUPERH_VGA16/* SuperH Linux logo */logo = &logo_superh_vga16; #endif}if (depth >= 8) { #ifdef CONFIG_LOGO_LINUX_CLUT224/* Generic Linux logo */logo = &logo_linux_clut224; #endif #ifdef CONFIG_LOGO_BLACKFIN_CLUT224/* Blackfin Linux logo */logo = &logo_blackfin_clut224; #endif #ifdef CONFIG_LOGO_DEC_CLUT224/* DEC Linux logo on MIPS/MIPS64 or ALPHA */logo = &logo_dec_clut224; #endif #ifdef CONFIG_LOGO_MAC_CLUT224/* Macintosh Linux logo on m68k */if (MACH_IS_MAC)logo = &logo_mac_clut224; #endif #ifdef CONFIG_LOGO_PARISC_CLUT224/* PA-RISC Linux logo */logo = &logo_parisc_clut224; #endif #ifdef CONFIG_LOGO_SGI_CLUT224/* SGI Linux logo on MIPS/MIPS64 and VISWS */logo = &logo_sgi_clut224; #endif #ifdef CONFIG_LOGO_SUN_CLUT224/* Sun Linux logo */logo = &logo_sun_clut224; #endif #ifdef CONFIG_LOGO_SUPERH_CLUT224/* SuperH Linux logo */logo = &logo_superh_clut224; #endif #ifdef CONFIG_LOGO_M32R_CLUT224/* M32R Linux logo */logo = &logo_m32r_clut224; #endif}return logo; } EXPORT_SYMBOL_GPL(fb_find_logo); |
文件開(kāi)始聲明的一系列l(wèi)inux_logo結(jié)構(gòu)體變量分別用來(lái)保存kernel/goldfish/drivers/video/logo目錄下的一系列ppm或者pbm文件的內(nèi)容的。這些ppm或者pbm文件都是用來(lái)描述第一個(gè)開(kāi)機(jī)畫(huà)面的。
全局變量nologo是一個(gè)類(lèi)型為布爾變量的模塊參數(shù),它的默認(rèn)值等于0,表示要顯示第一個(gè)開(kāi)機(jī)畫(huà)面。在這種情況下,函數(shù)fb_find_logo就會(huì)根據(jù)參數(shù)depth的值以及不同的編譯選項(xiàng)來(lái)選擇第一個(gè)開(kāi)機(jī)畫(huà)面的內(nèi)容,并且保存在變量logo中返回給調(diào)用者。
這一步執(zhí)行完成之后,第一個(gè)開(kāi)機(jī)畫(huà)面的內(nèi)容就保存在模塊fbmem的全局變量fb_logo的成員變量logo中了。這時(shí)候控制臺(tái)的初始化過(guò)程也結(jié)束了,接下來(lái)系統(tǒng)就會(huì)執(zhí)行切換控制臺(tái)的操作。前面提到,當(dāng)系統(tǒng)執(zhí)行切換控制臺(tái)的操作的時(shí)候,模塊fbcon中的函數(shù)fbcon_switch就會(huì)被調(diào)用。在調(diào)用的過(guò)程中,就會(huì)執(zhí)行顯示第一個(gè)開(kāi)機(jī)畫(huà)面的操作。
函數(shù)fbcon_switch實(shí)現(xiàn)在文件kernel/goldfish/drivers/video/console/fbcon.c中,顯示第一個(gè)開(kāi)機(jī)畫(huà)面的過(guò)程如下所示:
| static int fbcon_switch(struct vc_data *vc) {struct fb_info *info, *old_info = NULL;struct fbcon_ops *ops;struct display *p = &fb_display[vc->vc_num];struct fb_var_screeninfo var;int i, prev_console, charcnt = 256;......if (logo_shown == FBCON_LOGO_DRAW) {logo_shown = fg_console;/* This is protected above by initmem_freed */fb_show_logo(info, ops->rotate);......return 0;}return 1; } |
由于前面在準(zhǔn)備第一個(gè)開(kāi)機(jī)畫(huà)面的內(nèi)容的時(shí)候,全局變量logo_show的值被設(shè)置為FBCON_LOGO_DRAW,因此,接下來(lái)就會(huì)調(diào)用函數(shù)fb_show_logo來(lái)顯示第一個(gè)開(kāi)機(jī)畫(huà)面。在顯示之前,這個(gè)函數(shù)會(huì)將全局變量logo_shown的值設(shè)置為fg_console,后者表示系統(tǒng)當(dāng)前可見(jiàn)的控制臺(tái)的編號(hào)。
函數(shù)fb_show_logo實(shí)現(xiàn)在文件kernel/goldfish/drivers/video/fbmem.c中,如下所示:
| int fb_show_logo(struct fb_info *info, int rotate) {int y;y = fb_show_logo_line(info, rotate, fb_logo.logo, 0,num_online_cpus());......return y; } |
這個(gè)函數(shù)調(diào)用另外一個(gè)函數(shù)fb_show_logo_line來(lái)進(jìn)一步執(zhí)行渲染第一個(gè)開(kāi)機(jī)畫(huà)面的操作。
函數(shù)fb_show_logo_line也是實(shí)現(xiàn)在文件kernel/goldfish/drivers/video/fbmem.c中,如下所示:
| static int fb_show_logo_line(struct fb_info *info, int rotate,const struct linux_logo *logo, int y,unsigned int n) {u32 *palette = NULL, *saved_pseudo_palette = NULL;unsigned char *logo_new = NULL, *logo_rotate = NULL;struct fb_image image;/* Return if the frame buffer is not mapped or suspended */if (logo == NULL || info->state != FBINFO_STATE_RUNNING ||info->flags & FBINFO_MODULE)return 0;image.depth = 8;image.data = logo->data;if (fb_logo.needs_cmapreset)fb_set_logocmap(info, logo);if (fb_logo.needs_truepalette ||fb_logo.needs_directpalette) {palette = kmalloc(256 * 4, GFP_KERNEL);if (palette == NULL)return 0;if (fb_logo.needs_truepalette)fb_set_logo_truepalette(info, logo, palette);elsefb_set_logo_directpalette(info, logo, palette);saved_pseudo_palette = info->pseudo_palette;info->pseudo_palette = palette;}if (fb_logo.depth <= 4) {logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL);if (logo_new == NULL) {kfree(palette);if (saved_pseudo_palette)info->pseudo_palette = saved_pseudo_palette;return 0;}image.data = logo_new;fb_set_logo(info, logo, logo_new, fb_logo.depth);}image.dx = 0;image.dy = y;image.width = logo->width;image.height = logo->height;if (rotate) {logo_rotate = kmalloc(logo->width *logo->height, GFP_KERNEL);if (logo_rotate)fb_rotate_logo(info, logo_rotate, &image, rotate);}fb_do_show_logo(info, &image, rotate, n);kfree(palette);if (saved_pseudo_palette != NULL)info->pseudo_palette = saved_pseudo_palette;kfree(logo_new);kfree(logo_rotate);return logo->height; } |
參數(shù)logo指向了前面所準(zhǔn)備的第一個(gè)開(kāi)機(jī)畫(huà)面的內(nèi)容。這個(gè)函數(shù)首先根據(jù)參數(shù)logo的內(nèi)容來(lái)構(gòu)造一個(gè)fb_image結(jié)構(gòu)體image,用來(lái)描述最終要顯示的第一個(gè)開(kāi)機(jī)畫(huà)面。最后就調(diào)用函數(shù)fb_do_show_logo來(lái)真正執(zhí)行渲染第一個(gè)開(kāi)機(jī)畫(huà)面的操作。
函數(shù)fb_do_show_logo也是實(shí)現(xiàn)在文件kernel/goldfish/drivers/video/fbmem.c中,如下所示:
| static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,int rotate, unsigned int num) {unsigned int x;if (rotate == FB_ROTATE_UR) {for (x = 0;x < num && image->dx + image->width <= info->var.xres;x++) {info->fbops->fb_imageblit(info, image);image->dx += image->width + 8;}} else if (rotate == FB_ROTATE_UD) {for (x = 0; x < num && image->dx >= 0; x++) {info->fbops->fb_imageblit(info, image);image->dx -= image->width + 8;}} else if (rotate == FB_ROTATE_CW) {for (x = 0;x < num && image->dy + image->height <= info->var.yres;x++) {info->fbops->fb_imageblit(info, image);image->dy += image->height + 8;}} else if (rotate == FB_ROTATE_CCW) {for (x = 0; x < num && image->dy >= 0; x++) {info->fbops->fb_imageblit(info, image);image->dy -= image->height + 8;}} } |
參數(shù)rotate用來(lái)描述屏幕的當(dāng)前旋轉(zhuǎn)方向。屏幕旋轉(zhuǎn)方向不同,第一個(gè)開(kāi)機(jī)畫(huà)面的渲染方式也有所不同。例如,當(dāng)屏幕上下顛倒時(shí)(FB_ROTATE_UD),第一個(gè)開(kāi)機(jī)畫(huà)面的左右順序就剛好調(diào)換過(guò)來(lái),這時(shí)候就需要從右到左來(lái)渲染。其它三個(gè)方向FB_ROTATE_UR、FB_ROTATE_CW和FB_ROTATE_CCW分別表示沒(méi)有旋轉(zhuǎn)、順時(shí)針旋轉(zhuǎn)90度和逆時(shí)針旋轉(zhuǎn)90度。
參數(shù)info用來(lái)描述要渲染的幀緩沖區(qū)硬件設(shè)備,它的成員變量fbops指向了一系列回調(diào)函數(shù),用來(lái)操作幀緩沖區(qū)硬件設(shè)備,其中,回調(diào)函數(shù)fb_imageblit就是用來(lái)在指定的幀緩沖區(qū)硬件設(shè)備渲染指定的圖像的。
至此,第一個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程就分析完成了。
2. 第二個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程
由于第二個(gè)開(kāi)機(jī)畫(huà)面是在init進(jìn)程啟動(dòng)的過(guò)程中顯示的,因此,我們就從init進(jìn)程的入口函數(shù)main開(kāi)始分析第二個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程。
init進(jìn)程的入口函數(shù)main實(shí)現(xiàn)在文件system/core/init/init.c中,如下所示:
| int main(int argc, char **argv) {int fd_count = 0;struct pollfd ufds[4];......int property_set_fd_init = 0;int signal_fd_init = 0;int keychord_fd_init = 0;if (!strcmp(basename(argv[0]), "ueventd"))return ueventd_main(argc, argv);......queue_builtin_action(console_init_action, "console_init");......for(;;) {int nr, i, timeout = -1;execute_one_command();restart_processes();if (!property_set_fd_init && get_property_set_fd() > 0) {ufds[fd_count].fd = get_property_set_fd();ufds[fd_count].events = POLLIN;ufds[fd_count].revents = 0;fd_count++;property_set_fd_init = 1;}if (!signal_fd_init && get_signal_fd() > 0) {ufds[fd_count].fd = get_signal_fd();ufds[fd_count].events = POLLIN;ufds[fd_count].revents = 0;fd_count++;signal_fd_init = 1;}if (!keychord_fd_init && get_keychord_fd() > 0) {ufds[fd_count].fd = get_keychord_fd();ufds[fd_count].events = POLLIN;ufds[fd_count].revents = 0;fd_count++;keychord_fd_init = 1;}if (process_needs_restart) {timeout = (process_needs_restart - gettime()) * 1000;if (timeout < 0)timeout = 0;}if (!action_queue_empty() || cur_action)timeout = 0;......nr = poll(ufds, fd_count, timeout);if (nr <= 0)continue;for (i = 0; i < fd_count; i++) {if (ufds[i].revents == POLLIN) {if (ufds[i].fd == get_property_set_fd())handle_property_set_fd();else if (ufds[i].fd == get_keychord_fd())handle_keychord();else if (ufds[i].fd == get_signal_fd())handle_signal();}}}return 0; } |
函數(shù)一開(kāi)始就首先判斷參數(shù)argv[0]的值是否等于“ueventd”,即當(dāng)前正在啟動(dòng)的進(jìn)程名稱是否等于“ueventd”。如果是的話,那么就以u(píng)eventd_main函數(shù)來(lái)作入口函數(shù)。這是怎么回事呢?當(dāng)前正在啟動(dòng)的進(jìn)程不是init嗎?它的名稱怎么可能會(huì)等于“ueventd”?原來(lái),在目標(biāo)設(shè)備上,可執(zhí)行文件/sbin/ueventd是可執(zhí)行文件/init的一個(gè)符號(hào)鏈接文件,即應(yīng)用程序ueventd和init運(yùn)行的是同一個(gè)可執(zhí)行文件。內(nèi)核啟動(dòng)完成之后,可執(zhí)行文件/init首先會(huì)被執(zhí)行,即init進(jìn)程會(huì)首先被啟動(dòng)。init進(jìn)程在啟動(dòng)的過(guò)程中,會(huì)對(duì)啟動(dòng)腳本/init.rc進(jìn)行解析。在啟動(dòng)腳本/init.rc中,配置了一個(gè)ueventd進(jìn)程,它對(duì)應(yīng)的可執(zhí)行文件為/sbin/ueventd,即ueventd進(jìn)程加載的可執(zhí)行文件也為/init。因此,通過(guò)判斷參數(shù)argv[0]的值,就可以知道當(dāng)前正在啟動(dòng)的是init進(jìn)程還是ueventd進(jìn)程。
ueventd進(jìn)程是作什么用的呢?它是用來(lái)處理uevent事件的,即用來(lái)管理系統(tǒng)設(shè)備的。從前面的描述可以知道,它真正的入口函數(shù)為ueventd_main,實(shí)現(xiàn)在system/core/init/ueventd.c中。ueventd進(jìn)程會(huì)通過(guò)一個(gè)socket接口來(lái)和內(nèi)核通信,以便可以監(jiān)控系統(tǒng)設(shè)備事件。例如,在前面在Ubuntu上為Android系統(tǒng)編寫(xiě)Linux內(nèi)核驅(qū)動(dòng)程序一文中, 我們調(diào)用device_create函數(shù)來(lái)創(chuàng)建了一個(gè)名稱為“hello”的字符設(shè)備,這時(shí)候內(nèi)核就會(huì)向前面提到的socket發(fā)送一個(gè)設(shè)備增加事件。ueventd進(jìn)程通過(guò)這個(gè)socket獲得了這個(gè)設(shè)備增加事件之后,就會(huì)/dev目錄下創(chuàng)建一個(gè)名稱為“hello”的設(shè)備文件。這樣用戶空間的應(yīng)用程序就可以通過(guò)設(shè)備文件/dev/hello來(lái)和驅(qū)動(dòng)程序hello進(jìn)行通信了。
接下來(lái)調(diào)用另外一個(gè)函數(shù)queue_builtin_action來(lái)向init進(jìn)程中的一個(gè)待執(zhí)行action隊(duì)列增加了一個(gè)名稱等于“console_init”的action。這個(gè)action對(duì)應(yīng)的執(zhí)行函數(shù)為console_init_action,它就是用來(lái)顯示第二個(gè)開(kāi)機(jī)畫(huà)面的。
函數(shù)queue_builtin_action實(shí)現(xiàn)在文件system/core/init/init_parser.c文件中,如下所示:
| static list_declare(action_list); static list_declare(action_queue);void queue_builtin_action(int (*func)(int nargs, char **args), char *name) {struct action *act;struct command *cmd;act = calloc(1, sizeof(*act));act->name = name;list_init(&act->commands);cmd = calloc(1, sizeof(*cmd));cmd->func = func;cmd->args[0] = name;list_add_tail(&act->commands, &cmd->clist);list_add_tail(&action_list, &act->alist);action_add_queue_tail(act); }void action_add_queue_tail(struct action *act) {list_add_tail(&action_queue, &act->qlist); } |
action_list列表用來(lái)保存從啟動(dòng)腳本/init.rc解析得到的一系列action,以及一系列內(nèi)建的action。當(dāng)這些action需要執(zhí)行的時(shí)候,它們就會(huì)被添加到action_queue列表中去,以便init進(jìn)程可以執(zhí)行它們。
回到init進(jìn)程的入口函數(shù)main中,最后init進(jìn)程會(huì)進(jìn)入到一個(gè)無(wú)限循環(huán)中去。在這個(gè)無(wú)限循環(huán)中,init進(jìn)程會(huì)做以下五個(gè)事情:
A. 調(diào)用函數(shù)execute_one_command來(lái)檢查action_queue列表是否為空。如果不為空的話,那么init進(jìn)程就會(huì)將保存在列表頭中的action移除,并且執(zhí)行這個(gè)被移除的action。由于前面我們將一個(gè)名稱為“console_init”的action添加到了action_queue列表中,因此,在這個(gè)無(wú)限循環(huán)中,這個(gè)action就會(huì)被執(zhí)行,即函數(shù)console_init_action會(huì)被調(diào)用。
B. 調(diào)用函數(shù)restart_processes來(lái)檢查系統(tǒng)中是否有進(jìn)程需要重啟。在啟動(dòng)腳本/init.rc中,我們可以指定一個(gè)進(jìn)程在退出之后會(huì)自動(dòng)重新啟動(dòng)。在這種情況下,函數(shù)restart_processes就會(huì)檢查是否存在需要重新啟動(dòng)的進(jìn)程,如果存在的話,那么就會(huì)將它重新啟動(dòng)起來(lái)。
C. 處理系統(tǒng)屬性變化事件。當(dāng)我們調(diào)用函數(shù)property_set來(lái)改變一個(gè)系統(tǒng)屬性值時(shí),系統(tǒng)就會(huì)通過(guò)一個(gè)socket(通過(guò)調(diào)用函數(shù)get_property_set_fd可以獲得它的文件描述符)來(lái)向init進(jìn)程發(fā)送一個(gè)屬性值改變事件通知。init進(jìn)程接收到這個(gè)屬性值改變事件之后,就會(huì)調(diào)用函數(shù)handle_property_set_fd來(lái)進(jìn)行相應(yīng)的處理。后面在分析第三個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程時(shí),我們就會(huì)看到,SurfaceFlinger服務(wù)就是通過(guò)修改“ctl.start”和“ctl.stop”屬性值來(lái)啟動(dòng)和停止第三個(gè)開(kāi)機(jī)畫(huà)面的。
D. 處理一種稱為“chorded keyboard”的鍵盤(pán)輸入事件。這種類(lèi)型為chorded keyboard”的鍵盤(pán)設(shè)備通過(guò)不同的銨鍵組合來(lái)描述不同的命令或者操作,它對(duì)應(yīng)的設(shè)備文件為/dev/keychord。我們可以通過(guò)調(diào)用函數(shù)get_keychord_fd來(lái)獲得這個(gè)設(shè)備的文件描述符,以便可以監(jiān)控它的輸入事件,并且調(diào)用函數(shù)handle_keychord來(lái)對(duì)這些輸入事件進(jìn)行處理。
E. 回收僵尸進(jìn)程。我們知道,在Linux內(nèi)核中,如果父進(jìn)程不等待子進(jìn)程結(jié)束就退出,那么當(dāng)子進(jìn)程結(jié)束的時(shí)候,就會(huì)變成一個(gè)僵尸進(jìn)程,從而占用系統(tǒng)的資源。為了回收這些僵尸進(jìn)程,init進(jìn)程會(huì)安裝一個(gè)SIGCHLD信號(hào)接收器。當(dāng)那些父進(jìn)程已經(jīng)退出了的子進(jìn)程退出的時(shí)候,內(nèi)核就會(huì)發(fā)出一個(gè)SIGCHLD信號(hào)給init進(jìn)程。init進(jìn)程可以通過(guò)一個(gè)socket(通過(guò)調(diào)用函數(shù)get_signal_fd可以獲得它的文件描述符)來(lái)將接收到的SIGCHLD信號(hào)讀取回來(lái),并且調(diào)用函數(shù)handle_signal來(lái)對(duì)接收到的SIGCHLD信號(hào)進(jìn)行處理,即回收那些已經(jīng)變成了僵尸的子進(jìn)程。
注意,由于后面三個(gè)事件都是可以通過(guò)文件描述符來(lái)描述的,因此,init進(jìn)程的入口函數(shù)main使用poll機(jī)制來(lái)同時(shí)輪詢它們,以便可以提高效率。
接下來(lái)我們就重點(diǎn)分析函數(shù)console_init_action的實(shí)現(xiàn),以便可以了解第二個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程:
| 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; } |
這個(gè)函數(shù)主要做了兩件事件:
A. 初始化控制臺(tái)。init進(jìn)程在啟動(dòng)的時(shí)候,會(huì)解析內(nèi)核的啟動(dòng)參數(shù)(保存在文件/proc/cmdline中)。如果發(fā)現(xiàn)內(nèi)核的啟動(dòng)參數(shù)中包含有了一個(gè)名稱為“androidboot.console”的屬性,那么就會(huì)將這個(gè)屬性的值保存在字符數(shù)組console中。這樣我們就可以通過(guò)設(shè)備文件/dev/<console>來(lái)訪問(wèn)系統(tǒng)的控制臺(tái)。如果內(nèi)核的啟動(dòng)參數(shù)沒(méi)有包含名稱為“androidboot.console”的屬性,那么默認(rèn)就通過(guò)設(shè)備文件/dev/console來(lái)訪問(wèn)系統(tǒng)的控制臺(tái)。如果能夠成功地打開(kāi)設(shè)備文件/dev/<console>或者/dev/console,那么就說(shuō)明系統(tǒng)支持訪問(wèn)控制臺(tái),因此,全局變量have_console的就會(huì)被設(shè)置為1。
B. 顯示第二個(gè)開(kāi)機(jī)畫(huà)面。顯示第二個(gè)開(kāi)機(jī)畫(huà)面是通過(guò)調(diào)用函數(shù)load_565rle_image來(lái)實(shí)現(xiàn)的。在調(diào)用函數(shù)load_565rle_image的時(shí)候,指定的開(kāi)機(jī)畫(huà)面文件為INIT_IMAGE_FILE。INIT_IMAGE_FILE是一個(gè)宏,定義在system/core/init/init.h文件中,如下所示:
| #define INIT_IMAGE_FILE "/initlogo.rle" |
即第二個(gè)開(kāi)機(jī)畫(huà)面的內(nèi)容是由文件/initlogo.rle來(lái)指定的。如果文件/initlogo.rle不存在,或者在顯示它的過(guò)程中出現(xiàn)異常,那么函數(shù)load_565rle_image的返回值就會(huì)等于-1,這時(shí)候函數(shù)console_init_action就以文本的方式來(lái)顯示第二個(gè)開(kāi)機(jī)畫(huà)面,即向編號(hào)為0的控制臺(tái)(/dev/tty0)輸出“ANDROID”這7個(gè)字符。
函數(shù)load_565rle_image實(shí)現(xiàn)在文件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;}if (fstat(fd, &s) < 0) {goto fail_close_file;}data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0);if (data == MAP_FAILED)goto fail_close_file;if (fb_open(&fb))goto fail_unmap_data;max = fb_width(&fb) * fb_height(&fb);ptr = data;count = s.st_size;bits = fb.bits;while (count > 3) {unsigned n = ptr[0];if (n > max)break;android_memset16(bits, ptr[1], n << 1);bits += n;max -= n;ptr += 2;count -= 4;}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; } |
函數(shù)首先將控制臺(tái)的顯示方式設(shè)置為圖形方式,這是通過(guò)調(diào)用函數(shù)vt_set_mode來(lái)實(shí)現(xiàn)的,如下所示:
| static int vt_set_mode(int graphics) {int fd, r;fd = open("/dev/tty0", O_RDWR | O_SYNC);if (fd < 0)return -1;r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT));close(fd);return r; } |
函數(shù)vt_set_mode首先打開(kāi)控制臺(tái)設(shè)備文件/dev/tty0,接著再通過(guò)IO控制命令KDSETMODE來(lái)將控制臺(tái)的顯示方式設(shè)置為文本方式或者圖形方式,取決于參數(shù)graphics的值。從前面的調(diào)用過(guò)程可以知道,參數(shù)graphics的值等于1,因此,這里是將控制臺(tái)的顯示方式設(shè)備為圖形方式。
回到函數(shù)load_565rle_image中,從前面的調(diào)用過(guò)程可以知道,參數(shù)fn的值等于“/initlogo.rle”,即指向目標(biāo)設(shè)備上的initlogo.rle文件。函數(shù)load_565rle_image首先調(diào)用函數(shù)open打開(kāi)這個(gè)文件,并且將獲得的文件描述符保存在變量fd中,接著再調(diào)用函數(shù)fstat來(lái)獲得這個(gè)文件的大小。有了這些信息之后,函數(shù)load_565rle_image就可以調(diào)用函數(shù)mmap來(lái)把文件/initlogo.rle映射到init進(jìn)程的地址空間來(lái)了,以便可以讀取它的內(nèi)容。
將文件/initlogo.rle映射到init進(jìn)程的地址空間之后,接下來(lái)再調(diào)用函數(shù)fb_open來(lái)打開(kāi)設(shè)備文件/dev/graphics/fb0。前面在介紹第一個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程中提到,設(shè)備文件/dev/graphics/fb0是用來(lái)訪問(wèn)系統(tǒng)的幀緩沖區(qū)硬件設(shè)備的,因此,打開(kāi)了設(shè)備文件/dev/graphics/fb0之后,我們就可以將文件/initlogo.rle的內(nèi)容輸出到幀緩沖區(qū)硬件設(shè)備中去了。
函數(shù)fb_open的實(shí)現(xiàn)如下所示:
| static int fb_open(struct FB *fb) {fb->fd = open("/dev/graphics/fb0", O_RDWR);if (fb->fd < 0)return -1;if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0)goto fail;if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0)goto fail;fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE,MAP_SHARED, fb->fd, 0);if (fb->bits == MAP_FAILED)goto fail;return 0;fail:close(fb->fd);return -1; } |
打開(kāi)了設(shè)備文件/dev/graphics/fb0之后,接著再分別通過(guò)IO控制命令FBIOGET_FSCREENINFO和FBIOGET_VSCREENINFO來(lái)獲得幀緩沖硬件設(shè)備的固定信息和可變信息。固定信息使用一個(gè)fb_fix_screeninfo結(jié)構(gòu)體來(lái)描述,它保存的是幀緩沖區(qū)硬件設(shè)備固有的特性,這些特性在幀緩沖區(qū)硬件設(shè)備被初始化了之后,就不會(huì)發(fā)生改變,例如屏幕大小以及物理地址等信息。可變信息使用一個(gè)fb_var_screeninfo結(jié)構(gòu)體來(lái)描述,它保存的是幀緩沖區(qū)硬件設(shè)備可變的特性,這些特性在系統(tǒng)運(yùn)行的期間是可以改變的,例如屏幕所使用的分辨率、顏色深度以及顏色格式等。
除了獲得幀緩沖區(qū)硬件設(shè)備的固定信息和可變信息之外,函數(shù)fb_open還會(huì)將設(shè)備文件/dev/graphics/fb0的內(nèi)容映射到init進(jìn)程的地址空間來(lái),這樣init進(jìn)程就可以通過(guò)映射得到的虛擬地址來(lái)訪問(wèn)幀緩沖區(qū)硬件設(shè)備的內(nèi)容了。
回到函數(shù)load_565rle_image中,接下來(lái)分別使用宏fb_width和fb_height來(lái)獲得屏幕所使用的的分辨率,即屏幕的寬度和高度。宏fb_width和fb_height的定義如下所示:
| 01.#define fb_width(fb) ((fb)->vi.xres) 02.#define fb_height(fb) ((fb)->vi.yres) |
屏幕的所使用的分辨率使用結(jié)構(gòu)體fb_var_screeninfo的成員變量xres和yres來(lái)描述,其中,成員變量xres用來(lái)描述屏幕的寬度,而成員變量成員變量yres用來(lái)描述屏幕的高度。得到了屏幕的分辨率之后,就可以知道最多可以向幀緩沖區(qū)硬件設(shè)備寫(xiě)入的字節(jié)數(shù)的大小了,這個(gè)大小就等于屏幕的寬度乘以高度,保存在變量max中。
現(xiàn)在我們分別得到了文件initlogo.rle和幀緩沖區(qū)硬件設(shè)備在init進(jìn)程中的虛擬訪問(wèn)地址以及大小,這樣我們就可以將文件initlogo.rle的內(nèi)容寫(xiě)入到幀緩沖區(qū)硬件設(shè)備中去,以便可以將第二個(gè)開(kāi)機(jī)畫(huà)面顯示出來(lái),這是通過(guò)函數(shù)load_565rle_image中的while循環(huán)來(lái)實(shí)現(xiàn)的。
文件initlogo.rle保存的第二個(gè)開(kāi)機(jī)畫(huà)面的圖像格式是565rle的。rle的全稱是run-length encoding,翻譯為游程編碼或者行程長(zhǎng)度編碼,它可以使用4個(gè)字節(jié)來(lái)描述一個(gè)連續(xù)的具有相同顏色值的序列。在rle565格式,前面2個(gè)字節(jié)中用來(lái)描述序列的個(gè)數(shù),而后面2個(gè)字節(jié)用來(lái)描述一個(gè)具體的顏色,其中,顏色的RGB值分別占5位、6位和7位。理解了565rle圖像格式之后,我們就可以理解函數(shù)load_565rle_image中的while循環(huán)的實(shí)現(xiàn)邏輯了。在每一次循環(huán)中,都會(huì)依次從文件initlogo.rle中讀出4個(gè)字節(jié),其中,前兩個(gè)字節(jié)的內(nèi)容保存在變量n中,而后面2個(gè)字節(jié)的內(nèi)容用來(lái)寫(xiě)入到幀緩沖區(qū)硬件設(shè)備中去。由于2個(gè)字節(jié)剛好就可以使用一個(gè)無(wú)符號(hào)短整數(shù)來(lái)描述,因此,函數(shù)load_565rle_image通過(guò)調(diào)用函數(shù)android_memset16來(lái)將從文件initlogo.rle中讀取出來(lái)的顏色值寫(xiě)入到幀緩沖區(qū)硬件設(shè)備中去,
函數(shù)android_memset16的實(shí)現(xiàn)如下所示:
| void android_memset16(void *_ptr, unsigned short val, unsigned count) {unsigned short *ptr = _ptr;count >>= 1;while(count--)*ptr++ = val; } |
參數(shù)ptr指向被寫(xiě)入的地址,在我們這個(gè)場(chǎng)景中,這個(gè)地址即為幀緩沖區(qū)硬件設(shè)備映射到init進(jìn)程中的虛擬地址值。
參數(shù)val用來(lái)描述被寫(xiě)入的值,在我們這個(gè)場(chǎng)景中,這個(gè)值即為從文件initlogo.rle中讀取出來(lái)的顏色值。
參數(shù)count用來(lái)描述被寫(xiě)入的地址的長(zhǎng)度,它是以字節(jié)為單位的。由于在將參數(shù)val的值寫(xiě)入到參數(shù)ptr所描述的地址中去時(shí),是以無(wú)符號(hào)短整數(shù)為單位的,即是以2個(gè)字節(jié)為單位的,因此,函數(shù)android_memset16在將參數(shù)val寫(xiě)入到地址ptr中去之前,首先會(huì)將參數(shù)count的值除以2。相應(yīng)的地,在函數(shù)load_565rle_image中,需要將具有相同顏色值的序列的個(gè)數(shù)乘以2之后,再調(diào)用函數(shù)android_memset16。
回到函數(shù)load_565rle_image中,將文件/initlogo.rle的內(nèi)容寫(xiě)入到幀緩沖區(qū)硬件設(shè)備去之后,第二個(gè)開(kāi)機(jī)畫(huà)面就可以顯示出來(lái)了。接下來(lái)函數(shù)load_565rle_image就會(huì)調(diào)用函數(shù)munmap來(lái)注銷(xiāo)文件/initlogo.rle在init進(jìn)程中的映射,并且調(diào)用函數(shù)close來(lái)關(guān)閉文件/initlogo.rle。關(guān)閉了文件/initlogo.rle之后,還會(huì)調(diào)用函數(shù)unlink來(lái)刪除目標(biāo)設(shè)備上的/initlogo.rle文件。注意,這只是刪除了目標(biāo)設(shè)備上的/initlogo.rle文件,而不是刪除ramdisk映像中的initlogo.rle文件,因此,每次關(guān)機(jī)啟動(dòng)之后,系統(tǒng)都會(huì)重新將ramdisk映像中的initlogo.rle文件安裝到目標(biāo)設(shè)備上的根目錄來(lái),這樣就可以在每次開(kāi)機(jī)的時(shí)候都能將它顯示出來(lái)。
除了需要注銷(xiāo)文件/initlogo.rle在init進(jìn)程中的映射和關(guān)閉文件/initlogo.rle之外,還需要注銷(xiāo)文件/dev/graphics/fb0在init進(jìn)程中的映射以及關(guān)閉文件/dev/graphics/fb0,這是通過(guò)調(diào)用fb_close函數(shù)來(lái)實(shí)現(xiàn)的,如下所示:
| static void fb_close(struct FB *fb) {munmap(fb->bits, fb_size(fb));close(fb->fd); } |
在調(diào)用fb_close函數(shù)之前,函數(shù)load_565rle_image還會(huì)調(diào)用另外一個(gè)函數(shù)fb_update來(lái)更新屏幕上的第二個(gè)開(kāi)機(jī)畫(huà)面,它的實(shí)現(xiàn)如下所示:
| static void fb_update(struct FB *fb) {fb->vi.yoffset = 1;ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);fb->vi.yoffset = 0;ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); } |
在結(jié)構(gòu)體fb_var_screeninfo中,除了使用成員變量xres和yres來(lái)描述屏幕所使用的分辨率之外,還使用成員變量xres_virtual和yres_virtual來(lái)描述屏幕所使用的虛擬分辨率。成員變量xres和yres所描述屏幕的分辨率稱為可視分辨率。可視分辨率和虛擬分辨率有什么關(guān)系呢?可視分辨率是屏幕實(shí)際上使用的分辨率,即用戶所看到的分辨率,而虛擬分辨率是在系統(tǒng)內(nèi)部使用的,它是不可見(jiàn)的,并且可以大于可視分辨率。例如,假設(shè)可視分辨率是800 x 600,那么虛擬分辨率可以設(shè)置為1600 x 600。由于屏幕最多只可以顯示800 x 600個(gè)像素,因此,在系統(tǒng)內(nèi)部,就需要決定從1600 x 600中取出800 x 600個(gè)像素來(lái)顯示,這是通過(guò)結(jié)構(gòu)體fb_var_screeninfo的成員變量xoffset和yoffset的值來(lái)描述的。成員變量xoffset和yoffset的默認(rèn)值等于0,即默認(rèn)從虛擬分辨率的左上角取出與可視分辨率大小相等的像素出來(lái)顯示,否則的話,就會(huì)根據(jù)成員變量xoffset和yoffset的值來(lái)從虛擬分辨率的中間位置取出與可視分辨率大小相等的像素出來(lái)顯示。
幀緩沖區(qū)的大小是由虛擬分辨率決定的,因此,我們就可以在幀緩沖中寫(xiě)入比屏幕大小還要多的像素值,多出來(lái)的這個(gè)部分像素值就可以用作雙緩沖。我們?nèi)匀患僭O(shè)可視分辨率和虛擬分辨率分別是800 x 600和1600 x 600,那么我們就可以先將前一個(gè)圖像的內(nèi)容寫(xiě)入到幀緩沖區(qū)的前面800 x 600個(gè)像素中去,接著再將后一個(gè)圖像的內(nèi)容寫(xiě)入到幀緩沖區(qū)的后面800 x 600個(gè)像素中。通過(guò)分別將用來(lái)描述幀緩沖區(qū)硬件設(shè)備的fb_var_screeninfo結(jié)構(gòu)體的成員變量yoffset的值設(shè)置為0和800,就可以平滑地顯示兩個(gè)圖像。
理解了幀緩沖區(qū)硬件設(shè)備的可視分辨性和虛擬分辨性之后,函數(shù)fb_update的實(shí)現(xiàn)邏輯就可以很好地理解了。
至此,第二個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程就分析完成了。
3. 第三個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程
第三個(gè)開(kāi)機(jī)畫(huà)面是由應(yīng)用程序bootanimation來(lái)負(fù)責(zé)顯示的。應(yīng)用程序bootanimation在啟動(dòng)腳本init.rc中被配置成了一個(gè)服務(wù),如下所示:
| service bootanim /system/bin/bootanimationuser graphicsgroup graphicsdisabledoneshot |
應(yīng)用程序bootanimation的用戶和用戶組名稱分別被設(shè)置為graphics。注意, 用來(lái)啟動(dòng)應(yīng)用程序bootanimation的服務(wù)是disable的,即init進(jìn)程在啟動(dòng)的時(shí)候,不會(huì)主動(dòng)將應(yīng)用程序bootanimation啟動(dòng)起來(lái)。當(dāng)SurfaceFlinger服務(wù)啟動(dòng)的時(shí)候,它會(huì)通過(guò)修改系統(tǒng)屬性ctl.start的值來(lái)通知init進(jìn)程啟動(dòng)應(yīng)用程序bootanimation,以便可以顯示第三個(gè)開(kāi)機(jī)畫(huà)面,而當(dāng)System進(jìn)程將系統(tǒng)中的關(guān)鍵服務(wù)都啟動(dòng)起來(lái)之后,ActivityManagerService服務(wù)就會(huì)通知SurfaceFlinger服務(wù)來(lái)修改系統(tǒng)屬性ctl.stop的值,以便可以通知init進(jìn)程停止執(zhí)行應(yīng)用程序bootanimation,即停止顯示第三個(gè)開(kāi)機(jī)畫(huà)面。接下來(lái)我們就分別分析第三個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程和停止過(guò)程。
從前面Android系統(tǒng)進(jìn)程Zygote啟動(dòng)過(guò)程的源代碼分析一文可以知道,Zygote進(jìn)程在啟動(dòng)的過(guò)程中,會(huì)將System進(jìn)程啟動(dòng)起來(lái),而從前面Android應(yīng)用程序安裝過(guò)程源代碼分析一文又可以知道,System進(jìn)程在啟動(dòng)的過(guò)程(Step 3)中,會(huì)調(diào)用SurfaceFlinger類(lèi)的靜態(tài)成員函數(shù)instantiate來(lái)啟動(dòng)SurfaceFlinger服務(wù)。Sytem進(jìn)程在啟動(dòng)SurfaceFlinger服務(wù)的過(guò)程中,首先會(huì)創(chuàng)建一個(gè)SurfaceFlinger實(shí)例,然后再將這個(gè)實(shí)例注冊(cè)到Service Manager中去。在注冊(cè)的過(guò)程,前面創(chuàng)建的SurfaceFlinger實(shí)例會(huì)被一個(gè)sp指針引用。從前面Android系統(tǒng)的智能指針(輕量級(jí)指針、強(qiáng)指針和弱指針)的實(shí)現(xiàn)原理分析一文可以知道,當(dāng)一個(gè)對(duì)象第一次被智能指針引用的時(shí)候,這個(gè)對(duì)象的成員函數(shù)onFirstRef就會(huì)被調(diào)用。由于SurfaceFlinger重寫(xiě)了父類(lèi)RefBase的成員函數(shù)onFirstRef,因此,在注冊(cè)SurfaceFlinger服務(wù)的過(guò)程中,將會(huì)調(diào)用SurfaceFlinger類(lèi)的成員函數(shù)onFirstRef。在調(diào)用的過(guò)程,就會(huì)創(chuàng)建一個(gè)線程來(lái)啟動(dòng)第三個(gè)開(kāi)機(jī)畫(huà)面。
SurfaceFlinger類(lèi)實(shí)現(xiàn)在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp 中,它的成員函數(shù)onFirstRef的實(shí)現(xiàn)如下所示:
| void SurfaceFlinger::onFirstRef() {run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);// Wait for the main thread to be done with its initializationmReadyToRunBarrier.wait(); } |
SurfaceFlinger類(lèi)繼承了Thread類(lèi),當(dāng)它的成員函數(shù)run被調(diào)用的時(shí)候,系統(tǒng)就會(huì)創(chuàng)建一個(gè)新的線程。這個(gè)線程在第一次運(yùn)行之前,會(huì)調(diào)用SurfaceFlinger類(lèi)的成員函數(shù)readyToRun來(lái)通知SurfaceFlinger,它準(zhǔn)備就緒了。當(dāng)這個(gè)線程準(zhǔn)備就緒之后,它就會(huì)循環(huán)執(zhí)行SurfaceFlinger類(lèi)的成員函數(shù)threadLoop,直到這個(gè)成員函數(shù)的返回值等于false為止。
注意,SurfaceFlinger類(lèi)的成員函數(shù)onFirstRef是在System進(jìn)程的主線程中調(diào)用的,它需要等待前面創(chuàng)建的線程準(zhǔn)備就緒之后,再繼續(xù)往前執(zhí)行,這個(gè)通過(guò)調(diào)用SurfaceFlinger類(lèi)的成員變量mReadytoRunBarrier所描述的一個(gè)Barrier對(duì)象的成員函數(shù)wait來(lái)實(shí)現(xiàn)的。每一個(gè)Barrier對(duì)象內(nèi)問(wèn)都封裝了一個(gè)條件變量(Condition Variable),而條件變量是用來(lái)同步線程的。
接下來(lái),我們繼續(xù)分析SurfaceFlinger類(lèi)的成員函數(shù)readyToRun的實(shí)現(xiàn),如下所示:
| status_t SurfaceFlinger::readyToRun() {LOGI( "SurfaceFlinger's main thread ready to run. ""Initializing graphics H/W...");......mReadyToRunBarrier.open();/** We're now ready to accept clients...*/// start boot animationproperty_set("ctl.start", "bootanim");return NO_ERROR; } |
前面創(chuàng)建的線程用作SurfaceFlinger的主線程。這個(gè)線程在啟動(dòng)的時(shí)候,會(huì)對(duì)設(shè)備主屏幕以及OpenGL庫(kù)進(jìn)行初始化。初始化完成之后,接著就會(huì)調(diào)用SurfaceFlinger類(lèi)的成員變量mReadyToRunBarrier所描述的一個(gè)Barrier對(duì)象的成員函數(shù)open來(lái)喚醒System進(jìn)程的主線程,以便它可以繼續(xù)往前執(zhí)行。最后,SurfaceFlinger類(lèi)的成員函數(shù)readyToRun的成員函數(shù)會(huì)調(diào)用函數(shù)property_set來(lái)將系統(tǒng)屬性“ctl.start”的值設(shè)置為“bootanim”,表示要將應(yīng)用程序bootanimation啟動(dòng)起來(lái),以便可以顯示第三個(gè)開(kāi)機(jī)畫(huà)面。
前面在介紹第二個(gè)開(kāi)機(jī)畫(huà)面的時(shí)候提到,當(dāng)系統(tǒng)屬性發(fā)生改變時(shí),init進(jìn)程就會(huì)接收到一個(gè)系統(tǒng)屬性變化通知,這個(gè)通知最終是由在init進(jìn)程中的函數(shù)handle_property_set_fd來(lái)處理的。
函數(shù)handle_property_set_fd實(shí)現(xiàn)在文件system/core/init/property_service.c中,如下所示:
| void handle_property_set_fd() {prop_msg msg;int s;int r;int res;struct ucred cr;struct sockaddr_un addr;socklen_t addr_size = sizeof(addr);socklen_t cr_size = sizeof(cr);if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {return;}/* Check socket options here */if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {close(s);ERROR("Unable to recieve socket options\n");return;}r = recv(s, &msg, sizeof(msg), 0);close(s);if(r != sizeof(prop_msg)) {ERROR("sys_prop: mis-match msg size recieved: %d expected: %d\n",r, sizeof(prop_msg));return;}switch(msg.cmd) {case PROP_MSG_SETPROP:msg.name[PROP_NAME_MAX-1] = 0;msg.value[PROP_VALUE_MAX-1] = 0;if(memcmp(msg.name,"ctl.",4) == 0) {if (check_control_perms(msg.value, cr.uid, cr.gid)) {handle_control_message((char*) msg.name + 4, (char*) msg.value);} else {ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n",msg.name + 4, msg.value, cr.uid, cr.pid);}} else {if (check_perms(msg.name, cr.uid, cr.gid)) {property_set((char*) msg.name, (char*) msg.value);} else {ERROR("sys_prop: permission denied uid:%d name:%s\n",cr.uid, msg.name);}}break;default:break;} } |
init進(jìn)程是通過(guò)一個(gè)socket來(lái)接收系統(tǒng)屬性變化事件的。每一個(gè)系統(tǒng)屬性變化事件的內(nèi)容都是通過(guò)一個(gè)prop_msg對(duì)象來(lái)描述的。在prop_msg對(duì)象對(duì),成員變量name用來(lái)描述發(fā)生變化的系統(tǒng)屬性的名稱,而成員變量value用來(lái)描述發(fā)生變化的系統(tǒng)屬性的值。系統(tǒng)屬性分為兩種類(lèi)型,一種是普通類(lèi)型的系統(tǒng)屬性,另一種是控制類(lèi)型的系統(tǒng)屬性(屬性名稱以“ctl.”開(kāi)頭)。控制類(lèi)型的系統(tǒng)屬性在發(fā)生變化時(shí),會(huì)觸發(fā)init進(jìn)程執(zhí)行一個(gè)命令,而普通類(lèi)型的系統(tǒng)屬性就不具有這個(gè)特性。注意,改變系統(tǒng)屬性是需要權(quán)限,因此,函數(shù)handle_property_set_fd在處理一個(gè)系統(tǒng)屬性變化事件之前,首先會(huì)檢查修改系統(tǒng)屬性的進(jìn)程是否具有相應(yīng)的權(quán)限,這是通過(guò)調(diào)用函數(shù)check_control_perms或者check_perms來(lái)實(shí)現(xiàn)的。
從前面的調(diào)用過(guò)程可以知道,當(dāng)前發(fā)生變化的系統(tǒng)屬性的名稱為“ctl.start”,它的值被設(shè)置為“bootanim”。由于這是一個(gè)控制類(lèi)型的系統(tǒng)屬性,因此,在通過(guò)了權(quán)限檢查之后,另外一個(gè)函數(shù)handle_control_message就會(huì)被調(diào)用,以便可以執(zhí)行一個(gè)名稱為“bootanim”的命令。
函數(shù)handle_control_message實(shí)現(xiàn)在system/core/init/init.c中,如下所示:
| void handle_control_message(const char *msg, const char *arg) {if (!strcmp(msg,"start")) {msg_start(arg);} else if (!strcmp(msg,"stop")) {msg_stop(arg);} else {ERROR("unknown control msg '%s'\n", msg);} } |
控制類(lèi)型的系統(tǒng)屬性的名稱是以"ctl."開(kāi)頭,并且是以“start”或者“stop”結(jié)尾的,其中,“start”表示要啟動(dòng)某一個(gè)服務(wù),而“stop”表示要停止某一個(gè)服務(wù),它們是分別通過(guò)函數(shù)msg_start和msg_stop來(lái)實(shí)現(xiàn)的。由于當(dāng)前發(fā)生變化的系統(tǒng)屬性是以“start”來(lái)結(jié)尾的,因此,接下來(lái)就會(huì)調(diào)用函數(shù)msg_start來(lái)啟動(dòng)一個(gè)名稱為“bootanim”的服務(wù)。
函數(shù)msg_start實(shí)現(xiàn)在文件system/core/init/init.c中,如下所示:
| static void msg_start(const char *name) {struct service *svc;char *tmp = NULL;char *args = NULL;if (!strchr(name, ':'))svc = service_find_by_name(name);else {tmp = strdup(name);args = strchr(tmp, ':');*args = '\0';args++;svc = service_find_by_name(tmp);}if (svc) {service_start(svc, args);} else {ERROR("no such service '%s'\n", name);}if (tmp)free(tmp); } |
參數(shù)name的值等于“bootanim”,它用來(lái)描述一個(gè)服務(wù)名稱。這個(gè)函數(shù)首先調(diào)用函數(shù)service_find_by_name來(lái)找到名稱等于“bootanim”的服務(wù)的信息,這些信息保存在一個(gè)service結(jié)構(gòu)體svc中,接著再調(diào)用另外一個(gè)函數(shù)service_start來(lái)將對(duì)應(yīng)的應(yīng)用程序啟動(dòng)起來(lái)。
從前面的內(nèi)容可以知道,名稱等于“bootanim”的服務(wù)所對(duì)應(yīng)的應(yīng)用程序?yàn)?system/bin/bootanimation,這個(gè)應(yīng)用程序?qū)崿F(xiàn)在frameworks/base/cmds/bootanimation目錄中,其中,應(yīng)用程序入口函數(shù)main是實(shí)現(xiàn)在frameworks/base/cmds/bootanimation/bootanimation_main.cpp中的,如下所示:
| int main(int argc, char** argv) { #if defined(HAVE_PTHREADS)setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY); #endifchar value[PROPERTY_VALUE_MAX];property_get("debug.sf.nobootanimation", value, "0");int noBootAnimation = atoi(value);LOGI_IF(noBootAnimation, "boot animation disabled");if (!noBootAnimation) {sp<ProcessState> proc(ProcessState::self());ProcessState::self()->startThreadPool();// create the boot animation objectsp<BootAnimation> boot = new BootAnimation();IPCThreadState::self()->joinThreadPool();}return 0; } |
這個(gè)函數(shù)首先檢查系統(tǒng)屬性“debug.sf.nobootnimaition”的值是否不等于0。如果不等于的話,那么接下來(lái)就會(huì)啟動(dòng)一個(gè)Binder線程池,并且創(chuàng)建一個(gè)BootAnimation對(duì)象。這個(gè)BootAnimation對(duì)象就是用來(lái)顯示第三個(gè)開(kāi)機(jī)畫(huà)面的。由于BootAnimation對(duì)象在顯示第三個(gè)開(kāi)機(jī)畫(huà)面的過(guò)程中,需要與SurfaceFlinger服務(wù)通信,因此,應(yīng)用程序bootanimation就需要啟動(dòng)一個(gè)Binder線程池。
BootAnimation類(lèi)間接地繼承了RefBase類(lèi),并且重寫(xiě)了RefBase類(lèi)的成員函數(shù)onFirstRef,因此,當(dāng)一個(gè)BootAnimation對(duì)象第一次被智能指針引用的時(shí),這個(gè)BootAnimation對(duì)象的成員函數(shù)onFirstRef就會(huì)被調(diào)用。
BootAnimation類(lèi)的成員函數(shù)onFirstRef實(shí)現(xiàn)在文件frameworks/base/cmds/bootanimation/BootAnimation.cpp中,如下所示:
| void BootAnimation::onFirstRef() {status_t err = mSession->linkToComposerDeath(this);LOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));if (err == NO_ERROR) {run("BootAnimation", PRIORITY_DISPLAY);} } |
mSession是BootAnimation類(lèi)的一個(gè)成員變量,它的類(lèi)型為SurfaceComposerClient,是用來(lái)和SurfaceFlinger執(zhí)行Binder進(jìn)程間通信的,它是在BootAnimation類(lèi)的構(gòu)造函數(shù)中創(chuàng)建的,如下所示:
| BootAnimation::BootAnimation() : Thread(false) {mSession = new SurfaceComposerClient(); } |
SurfaceComposerClient類(lèi)內(nèi)部有一個(gè)實(shí)現(xiàn)了ISurfaceComposerClient接口的Binder代理對(duì)象mClient,這個(gè)Binder代理對(duì)象引用了SurfaceFlinger服務(wù),SurfaceComposerClient類(lèi)就是通過(guò)它來(lái)和SurfaceFlinger服務(wù)通信的。
回到BootAnimation類(lèi)的成員函數(shù)onFirstRef中,由于BootAnimation類(lèi)引用了SurfaceFlinger服務(wù),因此,當(dāng)SurfaceFlinger服務(wù)意外死亡時(shí),BootAnimation類(lèi)就需要得到通知,這是通過(guò)調(diào)用成員變量mSession的成員函數(shù)linkToComposerDeath來(lái)注冊(cè)SurfaceFlinger服務(wù)的死亡接收通知來(lái)實(shí)現(xiàn)的。
BootAnimation類(lèi)繼承了Thread類(lèi),因此,當(dāng)BootAnimation類(lèi)的成員函數(shù)onFirstRef調(diào)用了父類(lèi)Thread的成員函數(shù)run之后,系統(tǒng)就會(huì)創(chuàng)建一個(gè)線程,這個(gè)線程在第一次運(yùn)行之前,會(huì)調(diào)用BootAnimation類(lèi)的成員函數(shù)readyToRun來(lái)執(zhí)行一些初始化工作,后面再調(diào)用BootAnimation類(lèi)的成員函數(shù)htreadLoop來(lái)顯示第三個(gè)開(kāi)機(jī)畫(huà)面。
BootAnimation類(lèi)的成員函數(shù)readyToRun的實(shí)現(xiàn)如下所示:
| status_t BootAnimation::readyToRun() {mAssets.addDefaultAssets();DisplayInfo dinfo;status_t status = session()->getDisplayInfo(0, &dinfo);if (status)return -1;// create the native surfacesp control = session()->createSurface(getpid(), 0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);session()->openTransaction();control->setLayer(0x40000000);session()->closeTransaction();sp s = control->getSurface();// initialize opengl and eglconst EGLint attribs[] = {EGL_DEPTH_SIZE, 0,EGL_NONE};EGLint w, h, dummy;EGLint numConfigs;EGLConfig config;EGLSurface surface;EGLContext context;EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);eglInitialize(display, 0, 0);EGLUtils::selectConfigForNativeWindow(display, attribs, s.get(), &config);surface = eglCreateWindowSurface(display, config, s.get(), NULL);context = eglCreateContext(display, config, NULL, NULL);eglQuerySurface(display, surface, EGL_WIDTH, &w);eglQuerySurface(display, surface, EGL_HEIGHT, &h);if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)return NO_INIT;mDisplay = display;mContext = context;mSurface = surface;mWidth = w;mHeight = h;mFlingerSurfaceControl = control;mFlingerSurface = s;mAndroidAnimation = true;if ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) &&(mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR) ||(access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&(mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))mAndroidAnimation = false;return NO_ERROR; } |
BootAnimation類(lèi)的成員函數(shù)session用來(lái)返回BootAnimation類(lèi)的成員變量mSession所描述的一個(gè)SurfaceComposerClient對(duì)象。通過(guò)調(diào)用SurfaceComposerClient對(duì)象mSession的成員函數(shù)createSurface可以獲得一個(gè)SurfaceControl對(duì)象control。
SurfaceComposerClient類(lèi)的成員函數(shù)createSurface首先調(diào)用內(nèi)部的Binder代理對(duì)象mClient來(lái)請(qǐng)求SurfaceFlinger返回一個(gè)類(lèi)型為SurfaceLayer的Binder代理對(duì)象,接著再使用這個(gè)Binder代理對(duì)象來(lái)創(chuàng)建一個(gè)SurfaceControl對(duì)象。創(chuàng)建出來(lái)的SurfaceControl對(duì)象的成員變量mSurface就指向了從SurfaceFlinger返回來(lái)的類(lèi)型為SurfaceLayer的Binder代理對(duì)象。有了這個(gè)Binder代理對(duì)象之后,SurfaceControl對(duì)象就可以和SurfaceFlinger服務(wù)通信了。
調(diào)用SurfaceControl對(duì)象control的成員函數(shù)getSurface會(huì)返回一個(gè)Surface對(duì)象s。這個(gè)Surface對(duì)象s內(nèi)部也有一個(gè)類(lèi)型為SurfaceLayer的Binder代理對(duì)象mSurface,這個(gè)Binder代理對(duì)象與前面所創(chuàng)建的SurfaceControl對(duì)象control的內(nèi)部的Binder代理對(duì)象mSurface引用的是同一個(gè)SurfaceLayer對(duì)象。這樣,Surface對(duì)象s也可以通過(guò)其內(nèi)部的Binder代理對(duì)象mSurface來(lái)和SurfaceFlinger服務(wù)通信。
Surface類(lèi)繼承了ANativeWindow類(lèi)。ANativeWindow類(lèi)是連接OpenGL和Android窗口系統(tǒng)的橋梁,即OpenGL需要通過(guò)ANativeWindow類(lèi)來(lái)間接地操作Android窗口系統(tǒng)。這種橋梁關(guān)系是通過(guò)EGL庫(kù)來(lái)建立的,所有以egl為前綴的函數(shù)名均為EGL庫(kù)提供的接口。
為了能夠在OpenGL和Android窗口系統(tǒng)之間的建立一個(gè)橋梁,我們需要一個(gè)EGLDisplay對(duì)象display,一個(gè)EGLConfig對(duì)象config,一個(gè)EGLSurface對(duì)象surface,以及一個(gè)EGLContext對(duì)象context,其中,EGLDisplay對(duì)象display用來(lái)描述一個(gè)EGL顯示屏,EGLConfig對(duì)象config用來(lái)描述一個(gè)EGL幀緩沖區(qū)配置參數(shù),EGLSurface對(duì)象surface用來(lái)描述一個(gè)EGL繪圖表面,EGLContext對(duì)象context用來(lái)描述一個(gè)EGL繪圖上下文(狀態(tài)),它們是分別通過(guò)調(diào)用egl庫(kù)函數(shù)eglGetDisplay、EGLUtils::selectConfigForNativeWindow、eglCreateWindowSurface和eglCreateContext來(lái)獲得的。注意,EGLConfig對(duì)象config、EGLSurface對(duì)象surface和EGLContext對(duì)象context都是用來(lái)描述EGLDisplay對(duì)象display的。有了這些對(duì)象之后,就可以調(diào)用函數(shù)eglMakeCurrent來(lái)設(shè)置當(dāng)前EGL庫(kù)所使用的繪圖表面以及繪圖上下文。
還有另外一個(gè)地方需要注意的是,每一個(gè)EGLSurface對(duì)象surface有一個(gè)關(guān)聯(lián)的ANativeWindow對(duì)象。這個(gè)ANativeWindow對(duì)象是通過(guò)函數(shù)eglCreateWindowSurface的第三個(gè)參數(shù)來(lái)指定的。在我們這個(gè)場(chǎng)景中,這個(gè)ANativeWindow對(duì)象正好對(duì)應(yīng)于前面所創(chuàng)建的 Surface對(duì)象s。每當(dāng)OpenGL需要繪圖的時(shí)候,它就會(huì)找到前面所設(shè)置的繪圖表面,即EGLSurface對(duì)象surface。有了EGLSurface對(duì)象surface之后,就可以找到與它關(guān)聯(lián)的ANativeWindow對(duì)象,即Surface對(duì)象s。有了Surface對(duì)象s之后,就可以通過(guò)其內(nèi)部的Binder代理對(duì)象mSurface來(lái)請(qǐng)求SurfaceFlinger服務(wù)返回幀緩沖區(qū)硬件設(shè)備的一個(gè)圖形訪問(wèn)接口。這樣,OpenGL最終就可以將要繪制的圖形渲染到幀緩沖區(qū)硬件設(shè)備中去,即顯示在實(shí)際屏幕上。屏幕的大小,即寬度和高度,可以通過(guò)函數(shù)eglQuerySurface來(lái)獲得。
BootAnimation類(lèi)的成員變量mAndroidAnimation是一個(gè)布爾變量。當(dāng)它的值等于true的時(shí)候,那么就說(shuō)明需要顯示的第三個(gè)開(kāi)機(jī)畫(huà)面是Android系統(tǒng)默認(rèn)的開(kāi)機(jī)動(dòng)畫(huà),否則的話,第三個(gè)開(kāi)機(jī)畫(huà)面就是由用戶自定義的開(kāi)機(jī)動(dòng)畫(huà)。
自定義的開(kāi)機(jī)動(dòng)畫(huà)是由文件USER_BOOTANIMATION_FILE或者文件SYSTEM_BOOTANIMATION_FILE來(lái)描述的。只要其中的一個(gè)文件存在,那么第三個(gè)開(kāi)機(jī)畫(huà)面就會(huì)使用用戶自定義的開(kāi)機(jī)動(dòng)畫(huà)。USER_BOOTANIMATION_FILE和SYSTEM_BOOTANIMATION_FILE均是一個(gè)宏,它們的定義如下所示:
| #define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip" #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip" |
這一步執(zhí)行完成之后,用來(lái)顯示第三個(gè)開(kāi)機(jī)畫(huà)面的線程的初始化工作就執(zhí)行完成了,接下來(lái),就會(huì)執(zhí)行這個(gè)線程的主體函數(shù),即BootAnimation類(lèi)的成員函數(shù)threadLoop。
BootAnimation類(lèi)的成員函數(shù)threadLoop的實(shí)現(xiàn)如下所示:
| bool BootAnimation::threadLoop() {bool r;if (mAndroidAnimation) {r = android();} else {r = movie();}eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);eglDestroyContext(mDisplay, mContext);eglDestroySurface(mDisplay, mSurface);mFlingerSurface.clear();mFlingerSurfaceControl.clear();eglTerminate(mDisplay);IPCThreadState::self()->stopProcess();return r; } |
如果BootAnimation類(lèi)的成員變量mAndroidAnimation的值等于true,那么接下來(lái)就會(huì)調(diào)用BootAnimation類(lèi)的成員函數(shù)android來(lái)顯示系統(tǒng)默認(rèn)的開(kāi)機(jī)動(dòng)畫(huà),否則的話,就會(huì)調(diào)用BootAnimation類(lèi)的成員函數(shù)movie來(lái)顯示用戶自定義的開(kāi)機(jī)動(dòng)畫(huà)。顯示完成之后,就會(huì)銷(xiāo)毀前面所創(chuàng)建的EGLContext對(duì)象mContext、EGLSurface對(duì)象mSurface,以及EGLDisplay對(duì)象mDisplay等。
接下來(lái),我們就分別分析BootAnimation類(lèi)的成員函數(shù)android和movie的實(shí)現(xiàn)。
BootAnimation類(lèi)的成員函數(shù)android的實(shí)現(xiàn)如下所示:
| bool BootAnimation::android() {initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");// clear screenglShadeModel(GL_FLAT);glDisable(GL_DITHER);glDisable(GL_SCISSOR_TEST);glClear(GL_COLOR_BUFFER_BIT);eglSwapBuffers(mDisplay, mSurface);glEnable(GL_TEXTURE_2D);glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);const GLint xc = (mWidth - mAndroid[0].w) / 2;const GLint yc = (mHeight - mAndroid[0].h) / 2;const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);// draw and update only what we needmFlingerSurface->setSwapRectangle(updateRect);glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),updateRect.height());// Blend stateglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);const nsecs_t startTime = systemTime();do {nsecs_t now = systemTime();double time = now - startTime;float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;GLint x = xc - offset;glDisable(GL_SCISSOR_TEST);glClear(GL_COLOR_BUFFER_BIT);glEnable(GL_SCISSOR_TEST);glDisable(GL_BLEND);glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h);glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);glEnable(GL_BLEND);glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);if (res == EGL_FALSE) {break;}// 12fps: don't animate too fast to preserve CPUconst nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);if (sleepTime > 0)usleep(sleepTime);} while (!exitPending());glDeleteTextures(1, &mAndroid[0].name);glDeleteTextures(1, &mAndroid[1].name);return false; } |
Android系統(tǒng)默認(rèn)的開(kāi)機(jī)動(dòng)畫(huà)是由兩張圖片android-logo-mask.png和android-logo-shine.png中。這兩張圖片保存在frameworks/base/core/res/assets/images目錄中,它們最終會(huì)被編譯在framework-res模塊(frameworks/base/core/res)中,即編譯在framework-res.apk文件中。編譯在framework-res模塊中的資源文件可以通過(guò)AssetManager類(lèi)來(lái)訪問(wèn)。
BootAnimation類(lèi)的成員函數(shù)android首先調(diào)用另外一個(gè)成員函數(shù)initTexture來(lái)將根據(jù)圖片android-logo-mask.png和android-logo-shine.png的內(nèi)容來(lái)分別創(chuàng)建兩個(gè)紋理對(duì)象,這兩個(gè)紋理對(duì)象就分別保存在BootAnimation類(lèi)的成員變量mAndroid所描述的一個(gè)數(shù)組中。通過(guò)混合渲染這兩個(gè)紋理對(duì)象,我們就可以得到一個(gè)開(kāi)機(jī)動(dòng)畫(huà),這是通過(guò)中間的while循環(huán)語(yǔ)句來(lái)實(shí)現(xiàn)的。
圖片android-logo-mask.png用作動(dòng)畫(huà)前景,它是一個(gè)鏤空的“ANDROID”圖像。圖片android-logo-shine.png用作動(dòng)畫(huà)背景,它的中間包含有一個(gè)高亮的呈45度角的條紋。在每一次循環(huán)中,圖片android-logo-shine.png被劃分成左右兩部分內(nèi)容來(lái)顯示。左右兩個(gè)部分的圖像寬度隨著時(shí)間的推移而此消彼長(zhǎng),這樣就可以使得圖片android-logo-shine.png中間高亮的條紋好像在移動(dòng)一樣。另一方面,在每一次循環(huán)中,圖片android-logo-shine.png都作為一個(gè)整體來(lái)渲染,而且它的位置是恒定不變的。由于它是一個(gè)鏤空的“ANDROID”圖像,因此,我們就可以通過(guò)它的鏤空來(lái)看到它背后的圖片android-logo-shine.png的條紋一閃一閃地劃過(guò)。
這個(gè)while循環(huán)語(yǔ)句會(huì)一直被執(zhí)行,直到應(yīng)用程序/system/bin/bootanimation被結(jié)束為止,后面我們?cè)俜治觥?/p>
BootAnimation類(lèi)的成員函數(shù)movie的實(shí)現(xiàn)比較長(zhǎng),我們分段來(lái)閱讀:
| bool BootAnimation::movie() {ZipFileRO& zip(mZip);size_t numEntries = zip.getNumEntries();ZipEntryRO desc = zip.findEntryByName("desc.txt");FileMap* descMap = zip.createEntryFileMap(desc);LOGE_IF(!descMap, "descMap is null");if (!descMap) {return false;}String8 desString((char const*)descMap->getDataPtr(),descMap->getDataLength());char const* s = desString.string();Animation animation;// Parse the description filefor (;;) {const char* endl = strstr(s, "\n");if (!endl) break;String8 line(s, endl - s);const char* l = line.string();int fps, width, height, count, pause;char path[256];if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {//LOGD("> w=%d, h=%d, fps=%d", fps, width, height);animation.width = width;animation.height = height;animation.fps = fps;}if (sscanf(l, "p %d %d %s", &count, &pause, path) == 3) {//LOGD("> count=%d, pause=%d, path=%s", count, pause, path);Animation::Part part;part.count = count;part.pause = pause;part.path = path;animation.parts.add(part);}s = ++endl;} |
從前面BootAnimation類(lèi)的成員函數(shù)readyToRun的實(shí)現(xiàn)可以知道,如果目標(biāo)設(shè)備上存在壓縮文件/data/local/bootanimation.zip,那么BootAnimation類(lèi)的成員變量mZip就會(huì)指向它,否則的話,就會(huì)指向目標(biāo)設(shè)備上的壓縮文件/system/media/bootanimation.zip。無(wú)論BootAnimation類(lèi)的成員變量mZip指向的是哪一個(gè)壓縮文件,這個(gè)壓縮文件都必須包含有一個(gè)名稱為“desc.txt”的文件,用來(lái)描述用戶自定義的開(kāi)機(jī)動(dòng)畫(huà)是如何顯示的。
文件desc.txt的內(nèi)容格式如下面的例子所示:
| 600 480 24 p 1 0 part1 p 0 10 part2 |
第一行的三個(gè)數(shù)字分別表示開(kāi)機(jī)動(dòng)畫(huà)在屏幕中的顯示寬度、高度以及幀速(fps)。剩余的每一行都用來(lái)描述一個(gè)動(dòng)畫(huà)片斷,這些行必須要以字符“p”來(lái)開(kāi)頭,后面緊跟著兩個(gè)數(shù)字以及一個(gè)文件目錄路徑名稱。第一個(gè)數(shù)字表示一個(gè)片斷的循環(huán)顯示次數(shù),如果它的值等于0,那么就表示無(wú)限循環(huán)地顯示該動(dòng)畫(huà)片斷。第二個(gè)數(shù)字表示每一個(gè)片斷在兩次循環(huán)顯示之間的時(shí)間間隔。這個(gè)時(shí)間間隔是以一個(gè)幀的時(shí)間為單位的。文件目錄下面保存的是一系列png文件,這些png文件會(huì)被依次顯示在屏幕中。
以上面這個(gè)desct.txt文件的內(nèi)容為例,它描述了一個(gè)大小為600 x 480的開(kāi)機(jī)動(dòng)畫(huà),動(dòng)畫(huà)的顯示速度為24幀每秒。這個(gè)開(kāi)機(jī)動(dòng)畫(huà)包含有兩個(gè)片斷part1和part2。片斷part1只顯示一次,它對(duì)應(yīng)的png圖片保存在目錄part1中。片斷part2無(wú)限循環(huán)地顯示,其中,每?jī)纱窝h(huán)顯示的時(shí)間間隔為10 x (1 / 24)秒,它對(duì)應(yīng)的png圖片保存在目錄part2中。
上面的for循環(huán)語(yǔ)句分析完成desc.txt文件的內(nèi)容后,就得到了開(kāi)機(jī)動(dòng)畫(huà)的顯示大小、速度以及片斷信息。這些信息都保存在Animation對(duì)象animation中,其中,每一個(gè)動(dòng)畫(huà)片斷都使用一個(gè)Animation::Part對(duì)象來(lái)描述,并且保存在Animation對(duì)象animation的成員變量parts所描述的一個(gè)片斷列表中。
接下來(lái),BootAnimation類(lèi)的成員函數(shù)movie再斷續(xù)將每一個(gè)片斷所對(duì)應(yīng)的png圖片讀取出來(lái),如下所示:
| // read all the data structuresconst size_t pcount = animation.parts.size();for (size_t i=0 ; i<numEntries ; i++) {char name[256];ZipEntryRO entry = zip.findEntryByIndex(i);if (zip.getEntryFileName(entry, name, 256) == 0) {const String8 entryName(name);const String8 path(entryName.getPathDir());const String8 leaf(entryName.getPathLeaf());if (leaf.size() > 0) {for (int j=0 ; j<pcount ; j++) {if (path == animation.parts[j].path) {int method;// supports only stored png filesif (zip.getEntryInfo(entry, &method, 0, 0, 0, 0, 0)) {if (method == ZipFileRO::kCompressStored) {FileMap* map = zip.createEntryFileMap(entry);if (map) {Animation::Frame frame;frame.name = leaf;frame.map = map;Animation::Part& part(animation.parts.editItemAt(j));part.frames.add(frame);}}}}}}}} |
每一個(gè)png圖片都表示一個(gè)動(dòng)畫(huà)幀,使用一個(gè)Animation::Frame對(duì)象來(lái)描述,并且保存在對(duì)應(yīng)的Animation::Part對(duì)象的成員變量frames所描述的一個(gè)幀列表中。
獲得了開(kāi)機(jī)動(dòng)畫(huà)的所有信息之后,接下來(lái)BootAnimation類(lèi)的成員函數(shù)movie就準(zhǔn)備開(kāi)始顯示開(kāi)機(jī)動(dòng)畫(huà)了,如下所示:
| // clear screenglShadeModel(GL_FLAT);glDisable(GL_DITHER);glDisable(GL_SCISSOR_TEST);glDisable(GL_BLEND);glClear(GL_COLOR_BUFFER_BIT);eglSwapBuffers(mDisplay, mSurface);glBindTexture(GL_TEXTURE_2D, 0);glEnable(GL_TEXTURE_2D);glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);const int xc = (mWidth - animation.width) / 2;const int yc = ((mHeight - animation.height) / 2);nsecs_t lastFrame = systemTime();nsecs_t frameDuration = s2ns(1) / animation.fps;Region clearReg(Rect(mWidth, mHeight));clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height)); |
前面的一系列g(shù)l函數(shù)首先用來(lái)清理屏幕,接下來(lái)的一系列g(shù)l函數(shù)用來(lái)設(shè)置OpenGL的紋理顯示方式。
變量xc和yc的值用來(lái)描述開(kāi)機(jī)動(dòng)畫(huà)的顯示位置,即需要在屏幕中間顯示開(kāi)機(jī)動(dòng)畫(huà),另外一個(gè)變量frameDuration的值用來(lái)描述每一幀的顯示時(shí)間,它是以納秒為單位的。
Region對(duì)象clearReg用來(lái)描述屏幕中除了開(kāi)機(jī)動(dòng)畫(huà)之外的其它區(qū)域,它是用整個(gè)屏幕區(qū)域減去開(kāi)機(jī)動(dòng)畫(huà)所點(diǎn)據(jù)的區(qū)域來(lái)得到的。
準(zhǔn)備好開(kāi)機(jī)動(dòng)畫(huà)的顯示參數(shù)之后,最后就可以執(zhí)行顯示開(kāi)機(jī)動(dòng)畫(huà)的操作了,如下所示:
| for (int i=0 ; i<pcount && !exitPending() ; i++) {const Animation::Part& part(animation.parts[i]);const size_t fcount = part.frames.size();glBindTexture(GL_TEXTURE_2D, 0);for (int r=0 ; !part.count || r<part.count ; r++) {for (int j=0 ; j<fcount && !exitPending(); j++) {const Animation::Frame& frame(part.frames[j]);if (r > 0) {glBindTexture(GL_TEXTURE_2D, frame.tid);} else {if (part.count != 1) {glGenTextures(1, &frame.tid);glBindTexture(GL_TEXTURE_2D, frame.tid);glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);}initTexture(frame.map->getDataPtr(),frame.map->getDataLength());}if (!clearReg.isEmpty()) {Region::const_iterator head(clearReg.begin());Region::const_iterator tail(clearReg.end());glEnable(GL_SCISSOR_TEST);while (head != tail) {const Rect& r(*head++);glScissor(r.left, mHeight - r.bottom,r.width(), r.height());glClear(GL_COLOR_BUFFER_BIT);}glDisable(GL_SCISSOR_TEST);}glDrawTexiOES(xc, yc, 0, animation.width, animation.height);eglSwapBuffers(mDisplay, mSurface);nsecs_t now = systemTime();nsecs_t delay = frameDuration - (now - lastFrame);lastFrame = now;long wait = ns2us(frameDuration);if (wait > 0)usleep(wait);}usleep(part.pause * ns2us(frameDuration));}// free the textures for this partif (part.count != 1) {for (int j=0 ; j<fcount ; j++) {const Animation::Frame& frame(part.frames[j]);glDeleteTextures(1, &frame.tid);}}}return false; } |
第一層for循環(huán)用來(lái)顯示每一個(gè)動(dòng)畫(huà)片斷,第二層的for循環(huán)用來(lái)循環(huán)顯示每一個(gè)動(dòng)畫(huà)片斷,第三層的for循環(huán)用來(lái)顯示每一個(gè)動(dòng)畫(huà)片斷所對(duì)應(yīng)的png圖片。這些png圖片以紋理的方式來(lái)顯示在屏幕中。
注意,如果一個(gè)動(dòng)畫(huà)片斷的循環(huán)顯示次數(shù)不等于1,那么就說(shuō)明這個(gè)動(dòng)畫(huà)片斷中的png圖片需要重復(fù)地顯示在屏幕中。由于每一個(gè)png圖片都需要轉(zhuǎn)換為一個(gè)紋理對(duì)象之后才能顯示在屏幕中,因此,為了避免重復(fù)地為同一個(gè)png圖片創(chuàng)建紋理對(duì)象,第三層的for循環(huán)在第一次顯示一個(gè)png圖片的時(shí)候,會(huì)調(diào)用函數(shù)glGenTextures來(lái)為這個(gè)png圖片創(chuàng)建一個(gè)紋理對(duì)象,并且將這個(gè)紋理對(duì)象的名稱保存在對(duì)應(yīng)的Animation::Frame對(duì)象的成員變量tid中,這樣,下次再顯示相同的圖片時(shí),就可以使用前面已經(jīng)創(chuàng)建好了的紋理對(duì)象,即調(diào)用函數(shù)glBindTexture來(lái)指定當(dāng)前要操作的紋理對(duì)象。
如果Region對(duì)象clearReg所包含的區(qū)域不為空,那么在調(diào)用函數(shù)glDrawTexiOES和eglSwapBuffers來(lái)顯示每一個(gè)png圖片之前,首先要將它所包含的區(qū)域裁剪掉,避免開(kāi)機(jī)動(dòng)畫(huà)可以顯示在指定的位置以及大小中。
每當(dāng)顯示完成一個(gè)png圖片之后,都要將變量frameDuration的值從納秒轉(zhuǎn)換為毫秒。如果轉(zhuǎn)換后的值大小于,那么就需要調(diào)用函數(shù)usleep函數(shù)來(lái)讓線程睡眠一下,以保證每一個(gè)png圖片,即每一幀動(dòng)畫(huà)都按照預(yù)先指定好的速度來(lái)顯示。注意,函數(shù)usleep指定的睡眠時(shí)間只能精確到毫秒,因此,如果預(yù)先指定的幀顯示時(shí)間小于1毫秒,那么BootAnimation類(lèi)的成員函數(shù)movie是無(wú)法精確地控制地每一幀的顯示時(shí)間的。
還有另外一個(gè)地方需要注意的是,每當(dāng)循環(huán)顯示完成一個(gè)片斷時(shí),需要調(diào)用usleep函數(shù)來(lái)使得線程睡眠part.pause * ns2us(frameDuration)毫秒,以便可以按照預(yù)先設(shè)定的節(jié)奏來(lái)顯示開(kāi)機(jī)動(dòng)畫(huà)。
最后一個(gè)if語(yǔ)句判斷一個(gè)動(dòng)畫(huà)片斷是否是循環(huán)顯示的,即循環(huán)次數(shù)不等于1。如果是的話,那么就說(shuō)明前面為它所對(duì)應(yīng)的每一個(gè)png圖片都創(chuàng)建過(guò)一個(gè)紋理對(duì)象。現(xiàn)在既然這個(gè)片斷的顯示過(guò)程已經(jīng)結(jié)束了,因此,就需要釋放前面為它所創(chuàng)建的紋理對(duì)象。
至此,第三個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程就分析完成了。
接下來(lái),我們?cè)倮^續(xù)分析第三個(gè)開(kāi)機(jī)畫(huà)面是如何停止顯示的。
從前面Android系統(tǒng)默認(rèn)Home應(yīng)用程序(Launcher)的啟動(dòng)過(guò)程源代碼分析一文可以知道,當(dāng)System進(jìn)程將系統(tǒng)中的關(guān)鍵服務(wù)啟動(dòng)起來(lái)之后,就會(huì)將應(yīng)用程序啟動(dòng)器(Launcher)啟動(dòng)起來(lái)。從Android應(yīng)用程序啟動(dòng)過(guò)程源代碼分析一文又可以知道,Android應(yīng)用程序的啟動(dòng)過(guò)程實(shí)際上就是它的根Activity組件的啟動(dòng)過(guò)程。對(duì)于應(yīng)用程序Launcher來(lái)說(shuō),它的根Activity組件即為L(zhǎng)auncher組件。
一個(gè)Activity組件在啟動(dòng)起來(lái)之后,就會(huì)被記錄起來(lái),等到它所運(yùn)行在的主線程空閑的時(shí)候,這個(gè)主線程就會(huì)向ActivityManagerService發(fā)送一個(gè)Activity組件空閑的通知。由于應(yīng)用程序Launcher是系統(tǒng)中第一個(gè)被啟動(dòng)的應(yīng)用程序,即它的根Activity組件是系統(tǒng)中第一個(gè)被啟動(dòng)的Activity組件,因此,當(dāng)ActivityManagerService接收到它的空閑通知的時(shí)候,就可以知道系統(tǒng)是剛剛啟動(dòng)起來(lái)的。在這種情況下,ActivityManagerService就會(huì)停止顯示開(kāi)機(jī)動(dòng)畫(huà),以便可以在屏幕中顯示應(yīng)用程序Lancher的界面。
從前面Android應(yīng)用程序消息處理機(jī)制(Looper、Handler)分析一文可以知道,如果一個(gè)線程想要在空閑的時(shí)候處理一些事務(wù),那么就必須要向這個(gè)線程的消息隊(duì)列注冊(cè)一個(gè)空閑消息處理器。自定義的空閑消息處理器燈必須要從MessageQueue.IdleHandler類(lèi)繼承下來(lái),并且重寫(xiě)成員函數(shù)queueIdle。當(dāng)一個(gè)線程空閑的時(shí)候,即消息隊(duì)列中沒(méi)有新的消息需要處理的時(shí)候,那些注冊(cè)了的空閑消息處理器的成員函數(shù)queueIdle就會(huì)被調(diào)用。
應(yīng)用程序的主線程是通過(guò)ActivityThread類(lèi)來(lái)描述的,它實(shí)現(xiàn)在文件frameworks/base/core/java/android/app/ActivityThread.java中。每當(dāng)有一個(gè)新的Activity組件啟動(dòng)起來(lái)的時(shí)候,ActivityThread類(lèi)都會(huì)向它所描述的應(yīng)用程序主線程的消息隊(duì)列注冊(cè)一個(gè)類(lèi)型為Idler的空閑消息處理器。這樣一個(gè)應(yīng)用程序的主線程就可以在空閑的時(shí)候,向ActivityManagerService發(fā)送一個(gè)Activity組件空閑通知,相當(dāng)于是通知ActivityManagerService,一個(gè)新的Activity組件已經(jīng)準(zhǔn)備就緒了。
Idler類(lèi)定義在frameworks/base/core/java/android/app/ActivityThread.java中, 它的成員函數(shù)queueIdle的實(shí)現(xiàn)如下所示:
| public final class ActivityThread {......private final class Idler implements MessageQueue.IdleHandler {public final boolean queueIdle() {ActivityClientRecord a = mNewActivities;if (a != null) {mNewActivities = null;IActivityManager am = ActivityManagerNative.getDefault();ActivityClientRecord prev;do {......if (a.activity != null && !a.activity.mFinished) {try {am.activityIdle(a.token, a.createdConfig);a.createdConfig = null;} catch (RemoteException ex) {}}prev = a;a = a.nextIdle;prev.nextIdle = null;} while (a != null);}ensureJitEnabled();return false;}}...... } |
ActivityThread類(lèi)有一個(gè)類(lèi)型為ActivityClientRecord的成員變量mNewActivities,用來(lái)描述所有在當(dāng)前應(yīng)用程序主線程中新啟動(dòng)起來(lái)的Activity組件。這些新啟動(dòng)起來(lái)的Activity組件通過(guò)ActivityClientRecord類(lèi)的成員變量nextIdle連接在一起。一旦當(dāng)前應(yīng)用程序主線程向ActivityManagerService發(fā)送了這些新啟動(dòng)的Activity組件的空閑通知之后,這些新啟動(dòng)起來(lái)的Activity組件就不會(huì)再被保存在ActivityThread類(lèi)的成員變量mNewActivities中了,即每一個(gè)新啟動(dòng)的Activity組件只有一次機(jī)會(huì)向ActivityManagerService發(fā)送一個(gè)空閑通知。
向ActivityManagerService發(fā)送一個(gè)Activity組件空閑通知是通過(guò)調(diào)用ActivityManagerService代理對(duì)象的成員函數(shù)activityIdle來(lái)實(shí)現(xiàn)的,而ActivityManagerService代理對(duì)象可以通過(guò)調(diào)用ActivityManagerNative類(lèi)的靜態(tài)成員函數(shù)getDefault來(lái)獲得。
ActivityManagerService代理對(duì)象的類(lèi)型為ActivityManagerProxy,它的成員函數(shù)activityIdle實(shí)現(xiàn)在文件frameworks/base/core/java/android/app/ActivityManagerNative.java中,如下所示:
| class ActivityManagerProxy implements IActivityManager {......public void activityIdle(IBinder token, Configuration config) throws RemoteException{Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeInterfaceToken(IActivityManager.descriptor);data.writeStrongBinder(token);if (config != null) {data.writeInt(1);config.writeToParcel(data, 0);} else {data.writeInt(0);}mRemote.transact(ACTIVITY_IDLE_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);reply.readException();data.recycle();reply.recycle();}...... } |
ActivityManagerProxy類(lèi)的成員函數(shù)activityIdle實(shí)際上是向ActivityManagerService發(fā)送一個(gè)類(lèi)型為ACTIVITY_IDLE_TRANSACTION的Binder進(jìn)程間通信請(qǐng)求,其中,參數(shù)token用來(lái)描述與這個(gè)進(jìn)程間通信請(qǐng)求所關(guān)聯(lián)的一個(gè)Activity組件,在我們這個(gè)場(chǎng)景中,這個(gè)Activity組件即為應(yīng)用程序Launcher的根Activity組件Launcher。
類(lèi)型為ACTIVITY_IDLE_TRANSACTION的Binder進(jìn)程間通信請(qǐng)求是由ActivityManagerService類(lèi)的成員函數(shù)activityIdle來(lái)處理的,如下所示:
| public final class ActivityManagerService extends ActivityManagerNativeimplements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {......public final void activityIdle(IBinder token, Configuration config) {final long origId = Binder.clearCallingIdentity();mMainStack.activityIdleInternal(token, false, config);Binder.restoreCallingIdentity(origId);}...... } |
ActivityManagerService類(lèi)有一個(gè)類(lèi)型為ActivityStack的成員變量mMainStack,它用來(lái)描述系統(tǒng)的Activity組件堆棧,它的成員函數(shù)activityIdleInternal的實(shí)現(xiàn)如下所示:
| public class ActivityStack {......final void activityIdleInternal(IBinder token, boolean fromTimeout,Configuration config) {......boolean enableScreen = false;synchronized (mService) {......// Get the activity record.int index = indexOfTokenLocked(token);if (index >= 0) {ActivityRecord r = (ActivityRecord)mHistory.get(index); ......if (mMainStack) {if (!mService.mBooted && !fromTimeout) {mService.mBooted = true;enableScreen = true;}}}......}......if (enableScreen) {mService.enableScreenAfterBoot();}}...... } |
參數(shù)token用來(lái)描述剛剛啟動(dòng)起來(lái)的Launcher組件,通過(guò)它來(lái)調(diào)用函數(shù)indexOfTokenLocked就可以得到Launcher組件在系統(tǒng)Activity組件堆棧中的位置index。得到了Launcher組件在系統(tǒng)Activity組件堆棧中的位置index之后,就可以在ActivityStack類(lèi)的成員變量mHistory中得到一個(gè)ActivityRecord對(duì)象r。這個(gè)ActivityRecord對(duì)象r同樣是用來(lái)描述Launcher組件的。
ActivityStack類(lèi)的成員變量mMainStack是一個(gè)布爾變量,當(dāng)它的值等于true的時(shí)候,就說(shuō)明當(dāng)前正在處理的ActivityStack對(duì)象是用來(lái)描述系統(tǒng)的Activity組件堆棧的。 ActivityStack類(lèi)的另外一個(gè)成員變量mService指向了系統(tǒng)中的ActivityManagerService服務(wù)。ActivityManagerService服務(wù)有一個(gè)類(lèi)型為布爾值的成員變量mBooted,它的初始值為false,表示系統(tǒng)尚未啟動(dòng)完成。
從前面的調(diào)用過(guò)程可以知道,參數(shù)fromTimeout的值等于false。在這種情況下,如果ActivityManagerService服務(wù)的成員變量mBooted也等于false,那么就說(shuō)明應(yīng)用程序已經(jīng)啟動(dòng)起來(lái)了,即說(shuō)明系統(tǒng)已經(jīng)啟動(dòng)完成了。這時(shí)候ActivityManagerService服務(wù)的成員變量mBooted以及變量enableScreen的值就會(huì)被設(shè)置為true。
當(dāng)變量enableScreen的值等于true的時(shí)候,ActivityStack類(lèi)就會(huì)調(diào)用ActivityManagerService服務(wù)的成員函數(shù)enableScreenAfterBoot停止顯示開(kāi)機(jī)動(dòng)畫(huà),以便可以將屏幕讓出來(lái)顯示應(yīng)用程序Launcher的界面。
ActivityManagerService類(lèi)的成員函數(shù)enableScreenAfterBoot的實(shí)現(xiàn)如下所示:
| public final class ActivityManagerService extends ActivityManagerNativeimplements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {......void enableScreenAfterBoot() {EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,SystemClock.uptimeMillis());mWindowManager.enableScreenAfterBoot();}...... } |
ActivityManagerService類(lèi)的成員變量mWindowManager指向了系統(tǒng)中的Window管理服務(wù)WindowManagerService,ActivityManagerService服務(wù)通過(guò)調(diào)用它的成員函數(shù)enableScreenAfterBoot來(lái)停止顯示開(kāi)機(jī)動(dòng)畫(huà)。
WindowManagerService類(lèi)的成員函數(shù)enableScreenAfterBoot的實(shí)現(xiàn)如下所示:
| public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor {......public void enableScreenAfterBoot() {synchronized(mWindowMap) {if (mSystemBooted) {return;}mSystemBooted = true;}performEnableScreen();}...... } |
WindowManagerService類(lèi)的成員變量mSystemBooted用來(lái)記錄系統(tǒng)是否已經(jīng)啟動(dòng)完成的。如果已經(jīng)啟動(dòng)完成的話,那么這個(gè)成員變量的值就會(huì)等于true,這時(shí)候WindowManagerService類(lèi)的成員函數(shù)enableScreenAfterBoot什么也不做就返回了,否則的話,WindowManagerService類(lèi)的成員函數(shù)enableScreenAfterBoot首先將這個(gè)成員變量的值設(shè)置為true,接著再調(diào)用另外一個(gè)成員函數(shù)performEnableScreen來(lái)執(zhí)行停止顯示開(kāi)機(jī)動(dòng)畫(huà)的操作。
WindowManagerService類(lèi)的成員函數(shù)performEnableScreen的實(shí)現(xiàn)如下所示:
| public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor {......public void performEnableScreen() {synchronized(mWindowMap) {if (mDisplayEnabled) {return;}if (!mSystemBooted) {return;}......mDisplayEnabled = true;......try {IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");if (surfaceFlinger != null) {//Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");Parcel data = Parcel.obtain();data.writeInterfaceToken("android.ui.ISurfaceComposer");surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION,data, null, 0);data.recycle();}} catch (RemoteException ex) {Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!");}}......}...... } |
WindowManagerService類(lèi)的另外一個(gè)成員變量mDisplayEnabled用來(lái)描述WindowManagerService是否已經(jīng)初始化過(guò)系統(tǒng)的屏幕了,只有當(dāng)它的值等于false,并且系統(tǒng)已經(jīng)完成啟動(dòng),即WindowManagerService類(lèi)的成員變量mSystemBooted等于true的情況下,WindowManagerService類(lèi)的成員函數(shù)performEnableScreen才通知SurfaceFlinger服務(wù)停止顯示開(kāi)機(jī)動(dòng)畫(huà)。
注意,WindowManagerService類(lèi)的成員函數(shù)performEnableScreen是通過(guò)一個(gè)類(lèi)型為IBinder.FIRST_CALL_TRANSACTION的進(jìn)程間通信請(qǐng)求來(lái)通知SurfaceFlinger服務(wù)停止顯示開(kāi)機(jī)動(dòng)畫(huà)的。
在SurfaceFlinger服務(wù),類(lèi)型為IBinder.FIRST_CALL_TRANSACTION的進(jìn)程間通信請(qǐng)求被定義為停止顯示開(kāi)機(jī)動(dòng)畫(huà)的請(qǐng)求,如下所示:
| class BnSurfaceComposer : public BnInterface { public:enum {// Note: BOOT_FINISHED must remain this value, it is called from// Java by ActivityManagerService.BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,......};virtual status_t onTransact( uint32_t code,const Parcel& data,Parcel* reply,uint32_t flags = 0); }; |
BnSurfaceComposer類(lèi)定義在文件frameworks/base/include/surfaceflinger/ISurfaceComposer.h中,它是SurfaceFlinger服務(wù)所要繼承的Binder本地對(duì)象類(lèi),其中。當(dāng)SurfaceFlinger服務(wù)接收到類(lèi)型為IBinder::FIRST_CALL_TRANSACTION,即類(lèi)型為BOOT_FINISHED的進(jìn)程間通信請(qǐng)求時(shí),它就會(huì)將該請(qǐng)求交給它的成員函數(shù)bootFinished來(lái)處理。
SurfaceFlinger服務(wù)的成員函數(shù)bootFinished實(shí)現(xiàn)在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中,如下所示:
| void SurfaceFlinger::bootFinished() {const nsecs_t now = systemTime();const nsecs_t duration = now - mBootTime;LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );mBootFinished = true;property_set("ctl.stop", "bootanim"); } |
這個(gè)函數(shù)主要就是將系統(tǒng)屬性“ctl.stop”的值設(shè)置為“bootanim”。前面提到,每當(dāng)有一個(gè)系統(tǒng)屬性發(fā)生變化時(shí),init進(jìn)程就會(huì)被喚醒,并且調(diào)用運(yùn)行在它里面的函數(shù)handle_property_set_fd來(lái)處理這個(gè)系統(tǒng)屬性變化事件。在我們這個(gè)場(chǎng)景中,由于被改變的系統(tǒng)屬性的名稱是以"ctl."開(kāi)頭的,即被改變的系統(tǒng)屬性是一個(gè)控制類(lèi)型的屬性,因此,接下來(lái)函數(shù)handle_property_set_fd又會(huì)調(diào)用另外一個(gè)函數(shù)handle_control_message來(lái)處理該系統(tǒng)屬性變化事件。
函數(shù)handle_control_message實(shí)現(xiàn)在文件system/core/init/init.c中,如下所示:
| void handle_control_message(const char *msg, const char *arg) {if (!strcmp(msg,"start")) {msg_start(arg);} else if (!strcmp(msg,"stop")) {msg_stop(arg);} else {ERROR("unknown control msg '%s'\n", msg);} } |
從前面的調(diào)用過(guò)程可以知道,參數(shù)msg和arg的值分別等于"stop"和“bootanim”,這表示要停止執(zhí)行名稱為“bootanim”的服務(wù),這是通過(guò)調(diào)用函數(shù)msg_stop來(lái)實(shí)現(xiàn)的。
函數(shù)msg_stop也是實(shí)現(xiàn)在文件system/core/init/init.c中,如下所示:
| static void msg_stop(const char *name) {struct service *svc = service_find_by_name(name);if (svc) {service_stop(svc);} else {ERROR("no such service '%s'\n", name);} } |
這個(gè)函數(shù)首先調(diào)用函數(shù)service_find_by_name來(lái)找到名稱等于name,即“bootanim”的服務(wù),然后再調(diào)用函數(shù)service_stop來(lái)停止這個(gè)服務(wù)。
前面提到,名稱為“bootanim”的服務(wù)對(duì)應(yīng)的應(yīng)用程序即為/system/bin/bootanimation。因此,停止名稱為“bootanim”的服務(wù)即為停止執(zhí)行應(yīng)用程序/system/bin/bootanimation,而當(dāng)應(yīng)用程序/system/bin/bootanimation停止執(zhí)行的時(shí)候,開(kāi)機(jī)動(dòng)畫(huà)就會(huì)停止顯示了。
至此,Android系統(tǒng)的三個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程就分析完成了。通過(guò)這個(gè)三個(gè)開(kāi)機(jī)畫(huà)面的顯示過(guò)程分析,我們學(xué)習(xí)到:
前面第1點(diǎn)和第2點(diǎn)的知識(shí)是與Android系統(tǒng)的UI實(shí)現(xiàn)相關(guān)的,而后面第3點(diǎn)和第4點(diǎn)是兩個(gè)額外獲得的知識(shí)點(diǎn)。
總結(jié)
以上是生活随笔為你收集整理的Android系统的开机画面显示过程分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 买R1理财会亏本吗?这些情况有可能!
- 下一篇: android sina oauth2.