android开机充电及画面显示

    xiaoxiao2021-03-25  68

    转载自http://blog.csdn.net/xubin341719/article/details/8498580

    一、android正常开机流程、关机充电流程

    1、正常开机流程,按开机键。

    可大致分成三部分

    (1)、OS_level:UBOOT、kenrel、init这三步完成系统启动;

    (2)、Android_level:这部分完成android部的初始化;

    (3)、Home Screen:这部分就是我们看到的launcher部分。

    2、关机充电系统启动流程

           与前面相比,这个流程只走到init这一部分,就没有往后走了,这部分我们会在后面的代码中分析。

     

    二、关机充电逻辑硬件逻辑

     

    1、插入DC,charger IC从硬件上唤醒系统,相当于长按开机键开机。

     

    下面这部分是charger IC连接系统的控制部分。

     

     

    三、软件逻辑。

     

    DC插入,其实相当于关机状态下“按开机键”开机。第一步要走UBOOT、kernel 、android init这一流程。

    1、UBOOT

           UBOOT启动代码我们不在这里详细分析,这里我们只要注意二个问题:

    a:如何判断是DC插入;

    b:设定setenv("bootargs", "androidboot.mode=charger"),androidboot.mode这个参数相当重要,这个参数决定系统是正常启动、还是关机充电状态。

    Uboot/board/samsung/smdk4212/smkd4212.c

    [cpp] view plain copy

    int board_late_init (void)  {      int keystate = 0;      printf("check start mode\n");    if ((*(int *)0x10020800==0x19721212) || (*(int *)0x10020804==0x19721212)  || (*(int *)0x10020808==0x19721212)) //(1)、检查是否有DC插入;  {      setenv ("bootargs", "");//(2)、没有DC插入;    } else  {//DC插入          int tmp=*(int *)0x11000c08;      *(int *)0x10020800=*(int *)0x10020804=0x19721212;      *(int *)0x11000c08=(tmp&(~0xc000))|0xc000;      udelay(10000);      if ((*(int *)0x11000c04 & 0x80)!=0x80 && INF_REG4_REG != 0xf) {          setenv ("bootargs", "androidboot.mode=charger");//(3)、设定bootargs为charger状态          printf("charger mode\n");      } else {          setenv ("bootargs", "");      }      *(int *)0x11000c08=tmp;    }  #ifdef CONFIG_CPU_EXYNOS4X12      int charge_status=CheckBatteryLow();//(4)、检查电池电量;      keystate=board_key_check();//(5)、检查按键状态;      // fuse bootloader      if(second_boot_info != 0) {          boot_symbol=1;          INF_REG2_REG =0x8;          run_command(CONFIG_BOOTCMD_FUSE_BOOTLOADER, NULL);      }      if((INF_REG4_REG == 0xd)) {          // reboot default          char buf[10];          sprintf(buf, "%d", CONFIG_BOOTDELAY);          setenv ("bootdelay", buf);          setenv ("reserved", NULL);          saveenv();      } else if((INF_REG4_REG == 0xe) || keystate == (0x1 | 0x2)) {//(6)、按键进入fastboot模式;          // reboot bootloader          boot_symbol=1;          INF_REG2_REG =0x8;          printf("BOOTLOADER - FASTBOOT\n");          setenv ("reserved", "fastboot");          setenv ("bootdelay", "0");      } else if((INF_REG4_REG == 0xf) || keystate == (0x1 | 0x2 | 0x4)) {//(7)、按键进入recovery模式;          // reboot recovery          printf("BOOTLOADER - RECOVERY\n");          boot_symbol=1;          INF_REG2_REG =0x8;          setenv ("reserved", CONFIG_BOOTCMD_RECOVERY);          setenv ("bootdelay", "0");      } else      if(keystate == (0x1 | 0x4) || second_boot_info != 0 || partition_check()) {//(8)、按键进入卡升级模式;          // 2nd boot          printf("BOOTLOADER - 2ND BOOT DEVICE\n");          boot_symbol=1;          INF_REG2_REG =0x8;          setenv ("bootcmd", CONFIG_BOOTCOMMAND);          setenv ("reserved", CONFIG_BOOTCMD_FUSE_RELEASE);          setenv ("bootdelay", "0");      } else {//(9)、正常启动;          // normal case          char buf[10];          sprintf(buf, "%d", CONFIG_BOOTDELAY);          setenv ("bootdelay", buf);      }      INF_REG4_REG = 0;      return 0;  }  

    (1)、检查是否有DC插入;

    [cpp] view plain copy

    if ((*(int *)0x10020800==0x19721212) || (*(int *)0x10020804==0x19721212)   (*(int *)0x10020808==0x19721212))   

    这部分检查寄存器的值。

    (2)、没有DC插入;

    (3)、设定bootargs为charger状态

    [cpp] view plain copy

    if ((*(int *)0x11000c04 & 0x80)!=0x80 && INF_REG4_REG != 0xf) {          setenv ("bootargs", "androidboot.mode=charger");  

     

    这是这部分的重点,如果能过寄存器判断是DC插入,把androidboot.mode设定为charger状态。

     

    以下这部分根据需要加入,通过判断不同的情况进入不同的功能,如fastboot\revovery…………,这部分不做详细解释。

    (4)、检查电池电量;

        这个在正常开机状态下,如果检测电量太低,则不开机,这部分代码就不做分析。

    (5)、检查按键状态;

          我们这个平台有几种模式:fastboot\recovery\卡升级等……

    (6)、按键进入fastboot模式;

    (7)、按键进入recovery模式;

    (8)、按键进入卡升级模式

    (9)、正常启动;

    2、kernel

    这部分和正常启动是一样的。

    3、init

    前面所有的描述其实只有一点和正常启动不太一样,那就是在UBOOT中把androidboot.mode设定为charger状态,内核正常流程启动,然后到init时要对charger这种状态处理。

    system\core\init\init.c

    [cpp] view plain copy

    int main(int argc, char **argv)  {      ………………      action_for_each_trigger("early-init", action_add_queue_tail);        queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");      queue_builtin_action(property_init_action, "property_init");      queue_builtin_action(keychord_init_action, "keychord_init");      queue_builtin_action(console_init_action, "console_init");  //(1)、显示initlogo.rle,也就是android第二张图片;      queue_builtin_action(set_init_properties_action, "set_init_properties");        /* execute all the boot actions to get us started */      action_for_each_trigger("init", action_add_queue_tail);        /* skip mounting filesystems in charger mode */      if (strcmp(bootmode, "charger") != 0) {//(2)、这里就是UBOOT中设定的bootmode,如果是charger模式,跳过下面初始化;          action_for_each_trigger("early-fs", action_add_queue_tail);          action_for_each_trigger("fs", action_add_queue_tail);          action_for_each_trigger("post-fs", action_add_queue_tail);          action_for_each_trigger("post-fs-data", action_add_queue_tail);      }        queue_builtin_action(property_service_init_action, "property_service_init");      queue_builtin_action(signal_init_action, "signal_init");      queue_builtin_action(check_startup_action, "check_startup");        if (!strcmp(bootmode, "charger")) {//(3)、如果为charger,则调用charger.c。          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);      }  ……………………  }  

    (1)、显示initlogo.rle,也就是android第二张图片;

    queue_builtin_action(console_init_action,"console_init");调用console_init_action

    [cpp] view plain copy

    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) ) {//这里定义rle文件的名称#define INIT_IMAGE_FILE "/initlogo.rle"          fd = open("/dev/tty0", O_WRONLY);          if (fd >= 0) {//如果没有这张图片,就显示android字样,在屏幕左上角;              const char *msg;                  msg = "\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;  }  

    (2)、这里就是UBOOT中设定的bootmode,如果是charger模式,跳过下面初始化;

    [cpp] view plain copy

    /* skip mounting filesystems in charger mode */  if (strcmp(bootmode, "charger") != 0) {      action_for_each_trigger("early-fs", action_add_queue_tail);      action_for_each_trigger("fs", action_add_queue_tail);      action_for_each_trigger("post-fs", action_add_queue_tail);      action_for_each_trigger("post-fs-data", action_add_queue_tail);  }  

    (3)、如果为charger,则调用charger.c

    [cpp] view plain copy

    action_for_each_trigger("charger", action_add_queue_tail);  

    我们在后面细分charger这部分。

    4、charger.c

    这部分就是我们充电部分,充电画面显示的实现。

    system\core\charger\charger.c

    [cpp] view plain copy

    int main(int argc, char **argv)  {  ………………      klog_set_level(CHARGER_KLOG_LEVEL);      dump_last_kmsg();      LOGI("--------------- STARTING CHARGER MODE ---------------\n");        gr_init();      gr_font_size(&char_width, &char_height); //(1)、初始化graphics,包括buf大小;        ev_init(input_callback, charger);//(2)初始化按键;       fd = uevent_open_socket(64*1024, true);      if (fd >= 0) {          fcntl(fd, F_SETFL, O_NONBLOCK);          ev_add_fd(fd, uevent_callback, charger);      }        charger->uevent_fd = fd;      coldboot(charger, "/sys/class/power_supply", "add");//(3)、创建/sys/class/power_supply结点,把socket信息通知应用层;        ret = res_create_surface("charger/battery_fail", &charger->surf_unknown);      if (ret < 0) {          LOGE("Cannot load image\n");          charger->surf_unknown = NULL;      }      for (i = 0; i < charger->batt_anim->num_frames; i++) {//(4)、这里是显示charger logo,res_create_surface显示图片函数;          struct frame *frame = &charger->batt_anim->frames[i];          ret = res_create_surface(frame->name, &frame->surface);          if (ret < 0) {              LOGE("Cannot load image %s\n", frame->name);              /* TODO: free the already allocated surfaces... */              charger->batt_anim->num_frames = 0;              charger->batt_anim->num_cycles = 1;              break;          }      }  ev_sync_key_state(set_key_callback, charger);      gr_fb_blank(true);        charger->next_screen_transition = now - 1;      charger->next_key_check = -1;      charger->next_pwr_check = -1;      reset_animation(charger->batt_anim);      kick_animation(charger->batt_anim);      event_loop(charger);//(5)、event_loop循环,电池状态,检测按键是否按下;      return 0;    }  

    (1)、初始化graphics,包括buf大小

    android/bootable/recovery/minui/graphics.c

    gr_init():minui/graphics.c[settty0 to graphic mode, open fb0],设制tty0为图形模式,打开fb0;

    [cpp] view plain copy

    int gr_init(void)  {      gglInit(&gr_context);      GGLContext *gl = gr_context;      gr_init_font();      gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC);      if (gr_vt_fd < 0) {          // This is non-fatal; post-Cupcake kernels don't have tty0.          perror("can't open /dev/tty0");        } else if (ioctl(gr_vt_fd, KDSETMODE, (void*) KD_GRAPHICS)) {          // However, if we do open tty0, we expect the ioctl to work.          perror("failed KDSETMODE to KD_GRAPHICS on tty0");          gr_exit();          return -1;      }      gr_fb_fd = get_framebuffer(gr_framebuffer);      if (gr_fb_fd < 0) {          gr_exit();          return -1;      }      get_memory_surface(&gr_mem_surface);      fprintf(stderr, "framebuffer: fd %d (%d x %d)\n",              gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height);          /* start with 0 as front (displayed) and 1 as back (drawing) */      gr_active_fb = 0;      set_active_framebuffer(0);      gl->colorBuffer(gl, &gr_mem_surface);      gl->activeTexture(gl, 0);      gl->enable(gl, GGL_BLEND);      gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);      gr_fb_blank(true);      gr_fb_blank(false);      return 0;    }  

    (2)android/bootable/recovery/minui/events.c

    ev_init():minui/events.c[open /dev/input/event*]打开 /dev/input/event*

    这部分是在,充电状态下,按键操作的初始化,比如:短按显示充电logo,长按开机,初始化代码如下。

    [cpp] view plain copy

    int ev_init(ev_callback input_cb, void *data)  {      DIR *dir;      struct dirent *de;      int fd;      dir = opendir("/dev/input");//打开驱动结点;      if(dir != 0) {          while((de = readdir(dir))) {              unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];  //            fprintf(stderr,"/dev/input/%s\n", de->d_name);              if(strncmp(de->d_name,"event",5)) continue;              fd = openat(dirfd(dir), de->d_name, O_RDONLY);              if(fd < 0) continue;              /* read the evbits of the input device */              if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) < 0) {                  close(fd);                  continue;              }              /* TODO: add ability to specify event masks. For now, just assume              * that only EV_KEY and EV_REL event types are ever needed. */              if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits)) {                  close(fd);                  continue;              }              ev_fds[ev_count].fd = fd;              ev_fds[ev_count].events = POLLIN;              ev_fdinfo[ev_count].cb = input_cb;              ev_fdinfo[ev_count].data = data;              ev_count++;              ev_dev_count++;              if(ev_dev_count == MAX_DEVICES) break;          }      }      return 0;  }  

    (3)、创建/sys/class/power_supply结点,把socket信息通知应用层

    uevent_open_socket这个函数是通过kobject_uevent的方式通知的应用层,就是往一个socket广播一个消息,只需要在应用层打开socket监听NETLINK_KOBJECT_UEVENT组的消息,就可以收到了,主要是创建了socket接口获得uevent的文件描述符,然后触发/sys/class/power_supply目录及其子目录下的uevent,然后接受并创建设备节点,至此设备节点才算创建。

    (4)、这里显示charger logo,res_create_surface显示图片函数;

    res_create_surface:minui/resource.c[create surfaces for all bitmaps used later, include icons, bmps]

    创建surface为所以的位图,包括图标、位图。  这些图片的位置为:system\core\charger\images

    (5)、event_loop循环,电池状态,检测按键是否按下;

    5、event_loop

           这个函数判断按键状态,DC是否插拔。如果长按开机:执行android_reboot(ANDROID_RB_RESTART,0, 0);如果拔出DC:执行android_reboot(ANDROID_RB_POWEROFF,0, 0);

    [cpp] view plain copy

    static void event_loop(struct charger *charger)  {      int ret;      while (true) {          int64_t now = curr_time_ms();//(1)、获得当前时间;          LOGV("[%lld] event_loop()\n", now);          handle_input_state(charger, now);//(2)、检查按键状态;          handle_power_supply_state(charger, now);// (3)、检查DC是否拔出;           /* do screen update last in case any of the above want to start          * screen transitions (animations, etc)          */          update_screen_state(charger, now);//(4)、对按键时间状态标志位的判断,显示不同电量的充电logo;           wait_next_event(charger, now);      }  }  

    (1)、获得当前时间;

       int64_t now = curr_time_ms();

           这个时间来判断,有没有屏幕超时,如果超时关闭屏幕充电logo显示。

    (2)、检查按键状态;

    [cpp] view plain copy

    static void handle_input_state(struct charger *charger, int64_t now)  {      process_key(charger, KEY_POWER, now);      if (charger->next_key_check != -1 && now > charger->next_key_check)          charger->next_key_check = -1;  }  我们再看下:process_key(charger, KEY_POWER, now);  static void process_key(struct charger *charger, int code, int64_t now)  {  ………………      if (code == KEY_POWER) {          if (key->down) {              int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;              if (now >= reboot_timeout) {//如果长按power键,就重新启动,也就是重启开机;                  LOGI("[%lld] rebooting\n", now);                  android_reboot(ANDROID_RB_RESTART, 0, 0);//重启命令;              }      ………………      }        key->pending = false;  }  

    (3)、检查DC是否拔出;

    handle_power_supply_state(charger, now); 

    [cpp] view plain copy

    static void handle_power_supply_state(struct charger *charger, int64_t now)  {      if (charger->num_supplies_online == 0) {          if (charger->next_pwr_check == -1) {              charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;              LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n",                   now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);          } else if (now >= charger->next_pwr_check) {              LOGI("[%lld] shutting down\n", now);              android_reboot(ANDROID_RB_POWEROFF, 0, 0);//如果DC拔出,则关机;          }   ………………  }  

    (4)、对按键时间状态标志位的判断,显示不同电量的充电logo;

      update_screen_state(charger, now);

    这个函数比较长了,其实做用就是:我们在状态的过程中,充电logo的电量是要增加的,比如电量是20%时,要从第一格开始闪烁;如果是80%时,则要从第三格开始闪烁,电量显示就是通过这个函数来计算实现的。

    [cpp] view plain copy

    static void update_screen_state(struct charger *charger, int64_t now)  {      struct animation *batt_anim = charger->batt_anim;      int cur_frame;      int disp_time;        if (!batt_anim->run || now < charger->next_screen_transition)          return;        /* animation is over, blank screen and leave */      if (batt_anim->cur_cycle == batt_anim->num_cycles) {          reset_animation(batt_anim);          charger->next_screen_transition = -1;          gr_fb_blank(true);          LOGV("[%lld] animation done\n", now);          return;      }        disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;        /* animation starting, set up the animation */      if (batt_anim->cur_frame == 0) {          int batt_cap;          int ret;            LOGV("[%lld] animation starting\n", now);          batt_cap = get_battery_capacity(charger);          if (batt_cap >= 0 && batt_anim->num_frames != 0) {              int i;                /* find first frame given current capacity */              for (i = 1; i < batt_anim->num_frames; i++) {                  if (batt_cap < batt_anim->frames[i].min_capacity)                      break;              }              batt_anim->cur_frame = i - 1;                /* show the first frame for twice as long */              disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2;          }            batt_anim->capacity = batt_cap;      }        /* unblank the screen  on first cycle */      if (batt_anim->cur_cycle == 0)          gr_fb_blank(false);        /* draw the new frame (@ cur_frame) */      redraw_screen(charger);        /* if we don't have anim frames, we only have one image, so just bump      * the cycle counter and exit      */      if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) {          LOGV("[%lld] animation missing or unknown battery status\n", now);          charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;          batt_anim->cur_cycle++;          return;      }        /* schedule next screen transition */      charger->next_screen_transition = now + disp_time;        /* advance frame cntr to the next valid frame      * if necessary, advance cycle cntr, and reset frame cntr      */      batt_anim->cur_frame++;        /* if the frame is used for level-only, that is only show it when it's      * the current level, skip it during the animation.      */      while (batt_anim->cur_frame < batt_anim->num_frames &&             batt_anim->frames[batt_anim->cur_frame].level_only)          batt_anim->cur_frame++;      if (batt_anim->cur_frame >= batt_anim->num_frames) {          batt_anim->cur_cycle++;          batt_anim->cur_frame = 0;            /* don't reset the cycle counter, since we use that as a signal          * in a test above to check if animation is over          */      }  }  

    下面是不能容量时显示logo的函数:

    [cpp] view plain copy

    static struct frame batt_anim_frames[] = {      {          .name = "charger/battery_0",          .disp_time = 750,          .min_capacity = 0,      },      {          .name = "charger/battery_1",          .disp_time = 750,          .min_capacity = 20,      },      {          .name = "charger/battery_2",          .disp_time = 750,          .min_capacity = 40,      },      {          .name = "charger/battery_3",          .disp_time = 750,          .min_capacity = 60,      },      {          .name = "charger/battery_4",          .disp_time = 750,          .min_capacity = 80,          .level_only = true,      },      {          .name = "charger/battery_5",          .disp_time = 750,          .min_capacity = BATTERY_FULL_THRESH,      },  }; 

     

    转载自http://blog.csdn.net/lushengchu_luis/article/details/20382775

     

    最近RK3026的项目需要修改开机充电,才分析了Android原生态的充电过程。

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

    [java] view plain copy

    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

    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

    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

        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

    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文件,系统进入正常启动。

     

     

     

     

     

     

     

    转载请注明原文地址: https://ju.6miu.com/read-32338.html

    最新回复(0)