充电的代码和图标在system/core/charger中,会编译成名字为charger的可执行文件,打包进ramdisk中,在init.rc中脚本启动:




[java] view plain copy print ?


1. on charger  
2. 0
3.     export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin  
4.     export LD_LIBRARY_PATH /vendor/lib:/system/lib  
5.     setprop sys.usb.config adb   
6.   
7. service charger /charger                                                                                                                                         
8.     disabled



on charger
    setprop ro.boot.charger.emmc 0
    export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
    export LD_LIBRARY_PATH /vendor/lib:/system/lib
    setprop sys.usb.config adb 

service charger /charger                                                                                                                                       
    disabled



on charger会触发该服务执行,所以现在要分析的就是on charger是如被何触发的。


在system/core/init/init.c中:



[java] view plain copy print ?



    1. is_charger = !strcmp(bootmode, "charger");  
    2. 。。。。。。  
    3. if
    4. "charger", action_add_queue_tail);  
    5. else
    6. "early-boot", action_add_queue_tail);  
    7. "boot", action_add_queue_tail);  
    8.     }



    is_charger = !strcmp(bootmode, "charger");
    。。。。。。
        if (is_charger) {
            action_for_each_trigger("charger", action_add_queue_tail);
        } else {
            action_for_each_trigger("early-boot", action_add_queue_tail);
            action_for_each_trigger("boot", action_add_queue_tail);
        }

    可见,要触发charger, is_charger为真即可,即bootmode为“charger”,所以继续跟踪代码:



    [java] view plain copy print ?


    1. struct {  
    2. const char
    3. const char
    4. const char
    5. } prop_map[] = {  
    6. //   { "ro.boot.serialno", "ro.serialno", "", }, 
    7. "ro.boot.mode", "ro.bootmode", "unknown", },                                                                                                           
    8. "ro.boot.baseband", "ro.baseband", "unknown", },  
    9. "ro.boot.bootloader", "ro.bootloader", "unknown", },  
    10. };  
    11.   
    12. for (i = 0; i < ARRAY_SIZE(prop_map); i++) {  
    13.     ret = property_get(prop_map[i].src_prop, tmp);  
    14. if (ret > 0)  
    15.         property_set(prop_map[i].dest_prop, tmp);  
    16. else
    17.         property_set(prop_map[i].dest_prop, prop_map[i].def_val);  
    18. }  
    19.   
    20. ret = property_get("ro.boot.console", tmp);  
    21. if
    22.     strlcpy(console, tmp, sizeof(console));  
    23.   
    24. /* save a copy for init's usage during boot */
    25. property_get("ro.bootmode", tmp);  
    26. strlcpy(bootmode, tmp, sizeof(bootmode));



    struct {
            const char *src_prop;
            const char *dest_prop;
            const char *def_val;
        } prop_map[] = {
         //   { "ro.boot.serialno", "ro.serialno", "", },
            { "ro.boot.mode", "ro.bootmode", "unknown", },                                                                                                         
            { "ro.boot.baseband", "ro.baseband", "unknown", },
            { "ro.boot.bootloader", "ro.bootloader", "unknown", },
        };
    
        for (i = 0; i < ARRAY_SIZE(prop_map); i++) {
            ret = property_get(prop_map[i].src_prop, tmp);
            if (ret > 0)
                property_set(prop_map[i].dest_prop, tmp);
            else
                property_set(prop_map[i].dest_prop, prop_map[i].def_val);
        }
    
        ret = property_get("ro.boot.console", tmp);
        if (ret)
            strlcpy(console, tmp, sizeof(console));
    
        /* save a copy for init's usage during boot */
        property_get("ro.bootmode", tmp);
        strlcpy(bootmode, tmp, sizeof(bootmode));

    读取“ro.bootmode”得到的,但是“ro.bootmode”的属性又是通过"ro.boot.mode"来设置的。

    而这个属性是读取/proc/cmdline参数,最终在import_kernel_nv函数中设置的:



    [java] view plain copy print ?


    1. if (!strcmp(name,"qemu")) {  
    2.         strlcpy(qemu, value, sizeof(qemu));  
    3. else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {  
    4. const char *boot_prop_name = name + 12;  
    5. char
    6. int
    7.   
    8. "ro.boot.%s", boot_prop_name);  
    9. if
    10.             property_set(prop, value);  
    11.     }  
    12. }



    if (!strcmp(name,"qemu")) {
            strlcpy(qemu, value, sizeof(qemu));
        } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
            const char *boot_prop_name = name + 12;
            char prop[PROP_NAME_MAX];
            int cnt;
    
            cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
            if (cnt < PROP_NAME_MAX)
                property_set(prop, value);
        }
    }



    命令行是kernel传进来的,所以要进入驱动中去了。


    在kernel/drivers/power/rk29_charger_display.c文件中:



    [java] view plain copy print ?


    1. static void add_bootmode_charger_to_cmdline(void)                                                                                                                
    2. {  
    3. char *pmode=" androidboot.mode=charger";  
    4. //int off = strlen(saved_command_line); 
    5. char *new_command_line = kzalloc(strlen(saved_command_line) + strlen(pmode) + 1, GFP_KERNEL);  
    6. "%s%s", saved_command_line, pmode);  
    7.     saved_command_line = new_command_line;  
    8. //strcpy(saved_command_line+off,pmode); 
    9.   
    10. //int off = strlen(boot_command_line); 
    11. //strcpy(boot_command_line+off,pmode); 
    12.   
    13. "Kernel command line: %s\n", saved_command_line);  
    14. }



    static void add_bootmode_charger_to_cmdline(void)                                                                                                              
    {
        char *pmode=" androidboot.mode=charger";
        //int off = strlen(saved_command_line);
        char *new_command_line = kzalloc(strlen(saved_command_line) + strlen(pmode) + 1, GFP_KERNEL);
        sprintf(new_command_line, "%s%s", saved_command_line, pmode);
        saved_command_line = new_command_line;
        //strcpy(saved_command_line+off,pmode);
    
        //int off = strlen(boot_command_line);
        //strcpy(boot_command_line+off,pmode);
    
        printk("Kernel command line: %s\n", saved_command_line);
    }



    终于看到了,就是在这里设置了androidboot.mode=charger属性。


    继续跟进什么条件下才设置该属性,发现关机充电情况下,设置该属性,机子运行charger执行文件;这时候如果电源键按下超过两秒,charger执行文件重启机子,把标记设置为BOOT_MODE_CHARGE;重启后在驱动中,判断电量如果小于5%(可以修改该值),继续进入charger模式;否则不再设置androidboot.mode=charger属性,系统不会再执行charger文件,系统进入正常启动。




    [java] view plain copy print ?


    1. on charger  
    2. 0
    3.     export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin  
    4.     export LD_LIBRARY_PATH /vendor/lib:/system/lib  
    5.     setprop sys.usb.config adb   
    6.   
    7. service charger /charger                                                                                                                                         
    8.     disabled



    on charger
        setprop ro.boot.charger.emmc 0
        export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
        export LD_LIBRARY_PATH /vendor/lib:/system/lib
        setprop sys.usb.config adb 
    
    service charger /charger                                                                                                                                       
        disabled



    on charger会触发该服务执行,所以现在要分析的就是on charger是如被何触发的。


    在system/core/init/init.c中:



    [java] view plain copy print ?


    1. is_charger = !strcmp(bootmode, "charger");  
    2. 。。。。。。  
    3. if
    4. "charger", action_add_queue_tail);  
    5. else
    6. "early-boot", action_add_queue_tail);  
    7. "boot", action_add_queue_tail);  
    8.     }


    is_charger = !strcmp(bootmode, "charger");
    。。。。。。
        if (is_charger) {
            action_for_each_trigger("charger", action_add_queue_tail);
        } else {
            action_for_each_trigger("early-boot", action_add_queue_tail);
            action_for_each_trigger("boot", action_add_queue_tail);
        }

    可见,要触发charger, is_charger为真即可,即bootmode为“charger”,所以继续跟踪代码:



    [java] view plain copy print ?


    1. struct {  
    2. const char
    3. const char
    4. const char
    5. } prop_map[] = {  
    6. //   { "ro.boot.serialno", "ro.serialno", "", }, 
    7. "ro.boot.mode", "ro.bootmode", "unknown", },                                                                                                           
    8. "ro.boot.baseband", "ro.baseband", "unknown", },  
    9. "ro.boot.bootloader", "ro.bootloader", "unknown", },  
    10. };  
    11.   
    12. for (i = 0; i < ARRAY_SIZE(prop_map); i++) {  
    13.     ret = property_get(prop_map[i].src_prop, tmp);  
    14. if (ret > 0)  
    15.         property_set(prop_map[i].dest_prop, tmp);  
    16. else
    17.         property_set(prop_map[i].dest_prop, prop_map[i].def_val);  
    18. }  
    19.   
    20. ret = property_get("ro.boot.console", tmp);  
    21. if
    22.     strlcpy(console, tmp, sizeof(console));  
    23.   
    24. /* save a copy for init's usage during boot */
    25. property_get("ro.bootmode", tmp);  
    26. strlcpy(bootmode, tmp, sizeof(bootmode));


    struct {
            const char *src_prop;
            const char *dest_prop;
            const char *def_val;
        } prop_map[] = {
         //   { "ro.boot.serialno", "ro.serialno", "", },
            { "ro.boot.mode", "ro.bootmode", "unknown", },                                                                                                         
            { "ro.boot.baseband", "ro.baseband", "unknown", },
            { "ro.boot.bootloader", "ro.bootloader", "unknown", },
        };
    
        for (i = 0; i < ARRAY_SIZE(prop_map); i++) {
            ret = property_get(prop_map[i].src_prop, tmp);
            if (ret > 0)
                property_set(prop_map[i].dest_prop, tmp);
            else
                property_set(prop_map[i].dest_prop, prop_map[i].def_val);
        }
    
        ret = property_get("ro.boot.console", tmp);
        if (ret)
            strlcpy(console, tmp, sizeof(console));
    
        /* save a copy for init's usage during boot */
        property_get("ro.bootmode", tmp);
        strlcpy(bootmode, tmp, sizeof(bootmode));

    读取“ro.bootmode”得到的,但是“ro.bootmode”的属性又是通过"ro.boot.mode"来设置的。

    而这个属性是读取/proc/cmdline参数,最终在import_kernel_nv函数中设置的:



    [java] view plain copy print ?



      1. if (!strcmp(name,"qemu")) {  
      2.         strlcpy(qemu, value, sizeof(qemu));  
      3. else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {  
      4. const char *boot_prop_name = name + 12;  
      5. char
      6. int
      7.   
      8. "ro.boot.%s", boot_prop_name);  
      9. if
      10.             property_set(prop, value);  
      11.     }  
      12. }



      if (!strcmp(name,"qemu")) {
              strlcpy(qemu, value, sizeof(qemu));
          } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
              const char *boot_prop_name = name + 12;
              char prop[PROP_NAME_MAX];
              int cnt;
      
              cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
              if (cnt < PROP_NAME_MAX)
                  property_set(prop, value);
          }
      }



      命令行是kernel传进来的,所以要进入驱动中去了。


      在kernel/drivers/power/rk29_charger_display.c文件中:



      [java] view plain copy print ?


      1. static void add_bootmode_charger_to_cmdline(void)                                                                                                                
      2. {  
      3. char *pmode=" androidboot.mode=charger";  
      4. //int off = strlen(saved_command_line); 
      5. char *new_command_line = kzalloc(strlen(saved_command_line) + strlen(pmode) + 1, GFP_KERNEL);  
      6. "%s%s", saved_command_line, pmode);  
      7.     saved_command_line = new_command_line;  
      8. //strcpy(saved_command_line+off,pmode); 
      9.   
      10. //int off = strlen(boot_command_line); 
      11. //strcpy(boot_command_line+off,pmode); 
      12.   
      13. "Kernel command line: %s\n", saved_command_line);  
      14. }


      static void add_bootmode_charger_to_cmdline(void)                                                                                                              
      {
          char *pmode=" androidboot.mode=charger";
          //int off = strlen(saved_command_line);
          char *new_command_line = kzalloc(strlen(saved_command_line) + strlen(pmode) + 1, GFP_KERNEL);
          sprintf(new_command_line, "%s%s", saved_command_line, pmode);
          saved_command_line = new_command_line;
          //strcpy(saved_command_line+off,pmode);
      
          //int off = strlen(boot_command_line);
          //strcpy(boot_command_line+off,pmode);
      
          printk("Kernel command line: %s\n", saved_command_line);
      }



      终于看到了,就是在这里设置了androidboot.mode=charger属性。


      继续跟进什么条件下才设置该属性,发现关机充电情况下,设置该属性,机子运行charger执行文件;这时候如果电源键按下超过两秒,charger执行文件重启机子,把标记设置为BOOT_MODE_CHARGE;重启后在驱动中,判断电量如果小于5%(可以修改该值),继续进入charger模式;否则不再设置androidboot.mode=charger属性,系统不会再执行charger文件,系统进入正常启动。