Android系统的开机画面显示过程分析(1)

简介:
   好几个月都没有更新过博客了,从今天开始,老罗将尝试对Android系统的UI实现作一个系统的分析,也算是落实之前所作出的承诺。提到Android系统的UI,我们最先接触到的便是系统在启动过程中所出现的画面了。Android系统在启动的过程中,最多可以出现三个画面,每一个画面都用来描述一个不同的启动阶段。本文将详细分析这三个开机画面的显示过程,以便可以开启我们对Android系统UI实现的分析之路。
        第一个开机画面是在内核启动的过程中出现的,它是一个静态的画面。第二个开机画面是在init进程启动的过程中出现的,它也是一个静态的画面。第三个开机画面是在系统服务启动的过程中出现的,它是一个动态的画面。无论是哪一个画面,它们都是在一个称为帧缓冲区(frame buffer,简称fb)的硬件设备上进行渲染的。接下来,我们就分别分析这三个画面是如何在fb上显示的。
        1. 第一个开机画面的显示过程
        Android系统的第一个开机画面其实是Linux内核的启动画面。在默认情况下,这个画面是不会出现的,除非我们在编译内核的时候,启用以下两个编译选项:
        CONFIG_FRAMEBUFFER_CONSOLE
        CONFIG_LOGO
        第一个编译选项表示内核支持帧缓冲区控制台,它对应的配置菜单项为:Device Drivers ---> Graphics support ---> Console display driver support ---> Framebuffer Console support。第二个编译选项表示内核在启动的过程中,需要显示LOGO,它对应的配置菜单项为:Device Drivers ---> Graphics support ---> Bootup logo。配置Android内核编译选项可以参考 在Ubuntu上下载、编译和安装Android最新内核源代码(Linux Kernel) 一文。
        帧缓冲区硬件设备在内核中有一个对应的驱动程序模块fbmem,它实现在文件kernel/goldfish/drivers/video/fbmem.c中,它的初始化函数如下所示:
 
 
  1. /**  
  2.  *      fbmem_init - init frame buffer subsystem  
  3.  *  
  4.  *      Initialize the frame buffer subsystem.  
  5.  *  
  6.  *      NOTE: This function is _only_ to be called by drivers/char/mem.c.  
  7.  *  
  8.  */   
  9.    
  10. static int __init   
  11. fbmem_init(void)   
  12. {   
  13.         proc_create("fb", 0, NULL, &fb_proc_fops);   
  14.    
  15.         if (register_chrdev(FB_MAJOR,"fb",&fb_fops))   
  16.                 printk("unable to get major %d for fb devs\n", FB_MAJOR);   
  17.    
  18.         fb_class = class_create(THIS_MODULE, "graphics");   
  19.         if (IS_ERR(fb_class)) {   
  20.                 printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));   
  21.                 fb_class = NULL;   
  22.         }   
  23.         return 0;   
  24. }   
        这个函数首先调用函数proc_create在/proc目录下创建了一个fb文件,接着又调用函数register_chrdev来注册了一个名称为fb的字符设备,最后调用函数class_create在/sys/class目录下创建了一个graphics目录,用来描述内核的图形系统。

 
        模块fbmem除了会执行上述初始化工作之外,还会导出一个函数register_framebuffer:
 
 
  1. EXPORT_SYMBOL(register_framebuffer);   

        这个函数在内核的启动过程会被调用,以便用来执行注册帧缓冲区硬件设备的操作,它的实现如下所示:
 
 
  1. /**  
  2.  *      register_framebuffer - registers a frame buffer device  
  3.  *      @fb_info: frame buffer info structure  
  4.  *  
  5.  *      Registers a frame buffer device @fb_info.  
  6.  *  
  7.  *      Returns negative errno on error, or zero for success.  
  8.  *  
  9.  */   
  10.    
  11. int   
  12. register_framebuffer(struct fb_info *fb_info)   
  13. {   
  14.         int i;   
  15.         struct fb_event event;   
  16.         ......   
  17.    
  18.         if (num_registered_fb == FB_MAX)   
  19.                 return -ENXIO;   
  20.    
  21.         ......   
  22.    
  23.         num_registered_fb++;   
  24.         for (i = 0 ; i < FB_MAX; i++)   
  25.                 if (!registered_fb[i])   
  26.                         break;   
  27.         fb_info->node = i;   
  28.         mutex_init(&fb_info->lock);   
  29.         fb_info->dev = device_create(fb_class, fb_info->device,   
  30.                                      MKDEV(FB_MAJOR, i), NULL"fb%d", i);   
  31.         if (IS_ERR(fb_info->dev)) {   
  32.                 /* Not fatal */   
  33.                 printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));   
  34.                 fb_info->dev = NULL;   
  35.         } else   
  36.                 fb_init_device(fb_info);   
  37.    
  38.         ......   
  39.    
  40.         registered_fb[i] = fb_info;   
  41.    
  42.         event.info = fb_info;   
  43.         fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);   
  44.         return 0;   
  45. }   
        由于系统中可能会存在多个帧缓冲区硬件设备,因此,fbmem模块使用一个数组registered_fb保存所有已经注册了的帧缓冲区硬件设备,其中,每一个帧缓冲区硬件都是使用一个结构体fb_info来描述的。

 
        我们知道,在Linux内核中,每一个硬件设备都有一个主设备号和一个从设备号,它们用来唯一地标识一个硬件设备。对于帧缓冲区硬件设备来说,它们的主设备号定义为FB_MAJOR(29),而从设备号则与注册的顺序有关,它们的值依次等于0,1,2等。
        每一个被注册的帧缓冲区硬件设备在/dev/graphics目录下都有一个对应的设备文件fb<minor>,其中,<minor>表示一个从设备号。例如,第一个被注册的帧缓冲区硬件设备在/dev/graphics目录下都有一个对应的设备文件fb0。用户空间的应用程序通过这个设备文件就可以操作帧缓冲区硬件设备了,即将要显示的画面渲染到帧缓冲区硬件设备上去。
        这个函数最后会通过调用函数fb_notifier_call_chain来通知帧缓冲区控制台,有一个新的帧缓冲区设备被注册到内核中来了。
        帧缓冲区控制台在内核中对应的驱动程序模块为fbcon,它实现在文件kernel/goldfish/drivers/video/console/fbcon.c中,它的初始化函数如下所示:
 
 
  1. static struct notifier_block fbcon_event_notifier = {   
  2.         .notifier_call  = fbcon_event_notify,   
  3. };   
  4.    
  5. ......   
  6.    
  7. static int __init fb_console_init(void)   
  8. {   
  9.         int i;   
  10.    
  11.         acquire_console_sem();   
  12.         fb_register_client(&fbcon_event_notifier);   
  13.         fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,   
  14.                                      "fbcon");   
  15.    
  16.         if (IS_ERR(fbcon_device)) {   
  17.                 printk(KERN_WARNING "Unable to create device "   
  18.                        "for fbcon; errno = %ld\n",   
  19.                        PTR_ERR(fbcon_device));   
  20.                 fbcon_device = NULL;   
  21.         } else   
  22.                 fbcon_init_device();   
  23.    
  24.         for (i = 0; i < MAX_NR_CONSOLES; i++)   
  25.                 con2fb_map[i] = -1;   
  26.    
  27.         release_console_sem();   
  28.         fbcon_start();   
  29.         return 0;   
  30. }   

        这个函数除了会调用函数device_create来创建一个类别为graphics的设备fbcon之外,还会调用函数fb_register_client来监听帧缓冲区硬件设备的注册事件,这是由函数fbcon_event_notify来实现的,如下所示:
 
 
  1. static int fbcon_event_notify(struct notifier_block *self,   
  2.                               unsigned long action, void *data)   
  3. {   
  4.         struct fb_event *event = data;   
  5.         struct fb_info *info = event->info;   
  6.         ......   
  7.         int ret = 0;   
  8.    
  9.         ......   
  10.    
  11.         switch(action) {   
  12.         ......   
  13.         case FB_EVENT_FB_REGISTERED:   
  14.                 ret = fbcon_fb_registered(info);   
  15.                 break;   
  16.         ......   
  17.    
  18.         }   
  19.    
  20. done:   
  21.         return ret;   
  22. }   
     帧缓冲区硬件设备的注册事件最终是由函数fbcon_fb_registered来处理的,它的实现如下所示:
 
 
  1. static int fbcon_fb_registered(struct fb_info *info)   
  2. {   
  3.         int ret = 0, i, idx = info->node;   
  4.    
  5.         fbcon_select_primary(info);   
  6.    
  7.         if (info_idx == -1) {   
  8.                 for (i = first_fb_vc; i <= last_fb_vc; i++) {   
  9.                         if (con2fb_map_boot[i] == idx) {   
  10.                                 info_idx = idx;   
  11.                                 break;   
  12.                         }   
  13.                 }   
  14.    
  15.                 if (info_idx != -1)   
  16.                         ret = fbcon_takeover(1);   
  17.         } else {   
  18.                 for (i = first_fb_vc; i <= last_fb_vc; i++) {   
  19.                         if (con2fb_map_boot[i] == idx)   
  20.                                 set_con2fb_map(i, idx, 0);   
  21.                 }   
  22.         }   
  23.    
  24.         return ret;   
  25. }   
        函数fbcon_select_primary用来检查当前注册的帧缓冲区硬件设备是否是一个主帧缓冲区硬件设备。如果是的话,那么就将它的信息记录下来。这个函数只有当指定了CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY编译选项时才有效,否则的话,它是一个空函数。

 
        在Linux内核中,每一个控制台和每一个帧缓冲区硬件设备都有一个从0开始的编号,它们的初始对应关系保存在全局数组con2fb_map_boot中。控制台和帧缓冲区硬件设备的初始对应关系是可以通过设置内核启动参数来初始化的。在模块fbcon中,还有另外一个全局数组con2fb_map,也是用来映射控制台和帧缓冲区硬件设备的对应关系,不过它映射的是控制台和帧缓冲区硬件设备的实际对应关系。
        全局变量first_fb_vc和last_fb_vc是全局数组con2fb_map_boot和con2fb_map的索引值,用来指定系统当前可用的控制台编号范围,它们也是可以通过设置内核启动参数来初始化的。全局变量first_fb_vc的默认值等于0,而全局变量last_fb_vc的默认值等于MAX_NR_CONSOLES - 1。
        全局变量info_idx表示系统当前所使用的帧缓冲区硬件的编号。如果它的值等于-1,那么就说明系统当前还没有设置好当前所使用的帧缓冲区硬件设备。在这种情况下,函数fbcon_fb_registered就会在全局数组con2fb_map_boot中检查是否存在一个控制台编号与当前所注册的帧缓冲区硬件设备的编号idx对应。如果存在的话,那么就会将当前所注册的帧缓冲区硬件设备编号idx保存在全局变量info_idx中。接下来还会调用函数fbcon_takeover来初始化系统所使用的控制台。在调用函数fbcon_takeover的时候,传进去的参数为1,表示要显示第一个开机画面。
        如果全局变量info_idx的值不等于-1,那么函数fbcon_fb_registered同样会在全局数组con2fb_map_boot中检查是否存在一个控制台编号与当前所注册的帧缓冲区硬件设备的编号idx对应。如果存在的话,那么就会调用函数set_con2fb_map来调整当前所注册的帧缓冲区硬件设备与控制台的映射关系,即调整数组con2fb_map_boot和con2fb_map的值。
        为了简单起见,我们假设系统只有一个帧缓冲区硬件设备,这样当它被注册的时候,全局变量info_idx的值就会等于-1。当函数fbcon_fb_registered在全局数组con2fb_map_boot中发现有一个控制台的编号与这个帧缓冲区硬件设备的编号idx对应时,接下来就会调用函数fbcon_takeover来设置系统所使用的控制台。
        函数fbcon_takeover的实现如下所示:
 
 
  1. static int fbcon_takeover(int show_logo)   
  2. {   
  3.         int err, i;   
  4.    
  5.         if (!num_registered_fb)   
  6.                 return -ENODEV;   
  7.    
  8.         if (!show_logo)   
  9.                 logo_shown = FBCON_LOGO_DONTSHOW;   
  10.    
  11.         for (i = first_fb_vc; i <= last_fb_vc; i++)   
  12.                 con2fb_map[i] = info_idx;   
  13.    
  14.         err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,   
  15.                                 fbcon_is_default);   
  16.    
  17.         if (err) {   
  18.                 for (i = first_fb_vc; i <= last_fb_vc; i++) {   
  19.                         con2fb_map[i] = -1;   
  20.                 }   
  21.                 info_idx = -1;   
  22.         }   
  23.    
  24.         return err;   
  25. }   

        全局变量logo_shown的初始值为FBCON_LOGO_CANSHOW,表示可以显示第一个开机画面。但是当参数show_logo的值等于0的时候,全局变量logo_shown的值会被重新设置为FBCON_LOGO_DONTSHOW,表示不可以显示第一个开机画面。

 
        中间的for循环将当前可用的控制台的编号都映射到当前正在注册的帧缓冲区硬件设备的编号info_idx中去,表示当前可用的控制台与缓冲区硬件设备的实际映射关系。
        函数take_over_console用来初始化系统当前所使用的控制台。如果它的返回值不等于0,那么就表示初始化失败。在这种情况下,最后的for循环就会将全局数组con2fb_map的各个元素的值设置为-1,表示系统当前可用的控制台还没有映射到实际的帧缓冲区硬件设备中去。这时候全局变量info_idx的值也会被重新设置为-1。
       调用函数take_over_console来初始化系统当前所使用的控制台,实际上就是向系统注册一系列回调函数,以便系统可以通过这些回调函数来操作当前所使用的控制台。这些回调函数使用结构体consw来描述。这里所注册的结构体consw是由全局变量fb_con来指定的,它的定义如下所示:
 
 
  1. /*  
  2.  *  The console `switch' structure for the frame buffer based console  
  3.  */   
  4.    
  5. static const struct consw fb_con = {   
  6.         .owner                  = THIS_MODULE,   
  7.         .con_startup            = fbcon_startup,   
  8.         .con_init               = fbcon_init,   
  9.         .con_deinit             = fbcon_deinit,   
  10.         .con_clear              = fbcon_clear,   
  11.         .con_putc               = fbcon_putc,   
  12.         .con_putcs              = fbcon_putcs,   
  13.         .con_cursor             = fbcon_cursor,   
  14.         .con_scroll             = fbcon_scroll,   
  15.         .con_bmove              = fbcon_bmove,   
  16.         .con_switch             = fbcon_switch,   
  17.         .con_blank              = fbcon_blank,   
  18.         .con_font_set           = fbcon_set_font,   
  19.         .con_font_get           = fbcon_get_font,   
  20.         .con_font_default       = fbcon_set_def_font,   
  21.         .con_font_copy          = fbcon_copy_font,   
  22.         .con_set_palette        = fbcon_set_palette,   
  23.         .con_scrolldelta        = fbcon_scrolldelta,   
  24.         .con_set_origin         = fbcon_set_origin,   
  25.         .con_invert_region      = fbcon_invert_region,   
  26.         .con_screen_pos         = fbcon_screen_pos,   
  27.         .con_getxy              = fbcon_getxy,   
  28.         .con_resize             = fbcon_resize,   
  29. };   




本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967027,如需转载请自行联系原作者
目录
相关文章
|
2月前
|
存储 Shell Android开发
基于Android P,自定义Android开机动画的方法
本文详细介绍了基于Android P系统自定义开机动画的步骤,包括动画文件结构、脚本编写、ZIP打包方法以及如何将自定义动画集成到AOSP源码中。
47 2
基于Android P,自定义Android开机动画的方法
|
28天前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
34 2
|
2月前
|
Android开发
基于android-11.0.0_r39,系统应用的手动签名方法和过程
本文介绍了基于Android 11.0.0_r39版本进行系统应用手动签名的方法和解决签名过程中遇到的错误,包括处理`no conscrypt_openjdk_jni-linux-x86_64`和`RegisterNatives failed`的问题。
91 2
|
2月前
|
JavaScript 前端开发 Java
[Android][Framework]系统jar包,sdk的制作及引用
[Android][Framework]系统jar包,sdk的制作及引用
43 0
|
9天前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
|
12天前
|
监控 Android开发 iOS开发
深入探索安卓与iOS的系统架构差异:理解两大移动平台的技术根基在移动技术日新月异的今天,安卓和iOS作为市场上最为流行的两个操作系统,各自拥有独特的技术特性和庞大的用户基础。本文将深入探讨这两个平台的系统架构差异,揭示它们如何支撑起各自的生态系统,并影响着全球数亿用户的使用体验。
本文通过对比分析安卓和iOS的系统架构,揭示了这两个平台在设计理念、安全性、用户体验和技术生态上的根本区别。不同于常规的技术综述,本文以深入浅出的方式,带领读者理解这些差异是如何影响应用开发、用户选择和市场趋势的。通过梳理历史脉络和未来展望,本文旨在为开发者、用户以及行业分析师提供有价值的见解,帮助大家更好地把握移动技术发展的脉络。
|
8天前
|
Dart 开发工具 Android开发
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?
|
16天前
|
安全 Linux Android开发
探索安卓与iOS的安全性差异:技术深度分析
本文深入探讨了安卓(Android)和iOS两个主流操作系统平台在安全性方面的不同之处。通过比较它们在架构设计、系统更新机制、应用程序生态和隐私保护策略等方面的差异,揭示了每个平台独特的安全优势及潜在风险。此外,文章还讨论了用户在使用这些设备时可以采取的一些最佳实践,以增强个人数据的安全。
|
1月前
|
Android开发 UED 开发者
Android经典实战之WindowManager和创建系统悬浮窗
本文详细介绍了Android系统服务`WindowManager`,包括其主要功能和工作原理,并提供了创建系统悬浮窗的完整步骤。通过示例代码,展示了如何添加权限、请求权限、实现悬浮窗口及最佳实践,帮助开发者轻松掌握悬浮窗开发技巧。
57 1
|
1月前
|
IDE 开发工具 Android开发
安卓与iOS开发环境对比分析
本文将探讨安卓和iOS这两大移动操作系统在开发环境上的差异,从工具、语言、框架到生态系统等多个角度进行比较。我们将深入了解各自的优势和劣势,并尝试为开发者提供一些实用的建议,以帮助他们根据自己的需求选择最适合的开发平台。
29 1
下一篇
无影云桌面