为什么要一通电就自动开机
总所周知,一台Android设备,默认情况下,插上USB供电,都是显示正在充电的电池图标,按住开机键才能开机。如果你手里只有一台手机,按住开机键去开机很容易,如果你手里有1000台手机呢?通常见于微信推广、微信站街、手游工作室、刷单工作室等,往往拥有数百台数千台手机,使用群控和脚本批量控制这些手机做一些interesting的事情。这些工作室往往使用定制的手机,手里握着全套Android源代码,这样可以方便的做很多root都做不到的事情。那么一个工作室如果有数百台手机,如果断电了,需要开机,或者需要重启,都是很痛苦的事情,如果能让这些Android手机一通电就自动开机,岂不是方便极了。那么在有全套Android源代码的情况下,如何分析代码并修改去实现这一功能呢?
思路
其实一通电就开机这个功能,和Android没什么关系,分析的时候,也不要再去看Android源代码了。首先了解一下Android设备的启动流程:
实际上通电以后,屏幕上出现电池充电的图标,就一直停留在Bootloader这个步骤了,电池充电和屏幕显示充电的动画都是在这里面实现的,这个时候连Linux内核都还没加载,更不用说Android了。
所以要实现这个功能,首先锁定代码位置,那就是Android的Bootloader,通常是LK,代码位于bootable/bootloader/lk/。锁定了范围,只需要分析这里面的代码就行了。LK的代码量并不多,只有几千行,借助开发板使用串口打LOG调试,观察LOG输出内容,并以此为关键词在代码中搜索,定位LOG对应的代码,相信你很快就能找到方法。这里有网上找到的两篇文章,以MTK平台为例,分析了按下开机键后,由Preloader -> LK -> Kernel 的过程,结合文章中的时序图去阅读LK代码,会得到事半功倍的效果,相信你很快就能分析出来,充电的时候,代码执行到那里,按住开机键的时候,代码执行到了哪里。
深入MTK平台bootloader启动之【 Pre-loader -> Lk】分析笔记
深入MTK平台bootloader启动之【 lk -> kernel】分析笔记
MTK平台改法
有了思路以后,使用开发板串口打LOG,通电进入到充电界面后,观察串口输出的日志,用日志内容中的关键词去LK代码中搜索,进一步定位关键代码位置,通过阅读代码,bootable\bootloader\lk\platform\mt6582\boot_mode.c 中的 void boot_mode_select(void) 函数引起了我的注意,看函数名,很可能是通电以后,选择如何boot,是开机呢,还是充电呢,还是进入recovery。
void boot_mode_select(void)
{
int factory_forbidden = 0;
// int forbid_mode;
/*We put conditions here to filer some cases that can not do key detection*/
extern int kedump_mini(void) __attribute__((weak));
if (kedump_mini)
{
if (kedump_mini())
{
mrdump_check();
return;
}
}
if (meta_detection())
{
return;
}
mrdump_check();
#if defined (HAVE_LK_TEXT_MENU)
/*Check RTC to know if system want to reboot to Fastboot*/
if(Check_RTC_PDN1_bit13())
{
printf("[FASTBOOT] reboot to boot loader\n");
g_boot_mode = FASTBOOT;
Set_Clr_RTC_PDN1_bit13(false);
return;
}
/*If forbidden mode is factory, cacel the factory key detection*/
if(g_boot_arg->sec_limit.magic_num == 0x4C4C4C4C)
{
if(g_boot_arg->sec_limit.forbid_mode == F_FACTORY_MODE)
{
//Forbid to enter factory mode
printf("%s Forbidden\n",MODULE_NAME);
factory_forbidden=1;
}
}
// forbid_mode = g_boot_arg->boot_mode &= 0x000000FF;
/*If boot reason is power key + volumn down, then
disable factory mode dectection*/
if(mtk_detect_pmic_just_rst())
{
factory_forbidden=1;
}
/*Check RTC to know if system want to reboot to Recovery*/
if(Check_RTC_Recovery_Mode())
{
g_boot_mode = RECOVERY_BOOT;
return;
}
/*If MISC Write has not completed in recovery mode
before system reboot, go to recovery mode to
finish remain tasks*/
if(unshield_recovery_detection())
{
return;
}
ulong begin = get_timer(0);
/*we put key dectection here to detect key which is pressed*/
printf("eng build\n");
printf("MT65XX_FACTORY_KEY 0x%x\n",MT65XX_FACTORY_KEY);
printf("MT65XX_BOOT_MENU_KEY 0x%x\n",MT65XX_BOOT_MENU_KEY);
printf("MT65XX_RECOVERY_KEY 0x%x\n",MT65XX_RECOVERY_KEY);
while(get_timer(begin)<50)
{
if(!factory_forbidden){
if(mtk_detect_key(MT65XX_FACTORY_KEY))
{
printf("%s Detect key\n",MODULE_NAME);
printf("%s Enable factory mode\n",MODULE_NAME);
g_boot_mode = FACTORY_BOOT;
//video_printf("%s : detect factory mode !\n",MODULE_NAME);
return;
}
}
if(mtk_detect_key(MT65XX_BOOT_MENU_KEY))
{
printf("\n%s Check boot menu\n",MODULE_NAME);
printf("%s Wait 50ms for special keys\n",MODULE_NAME);
mtk_wdt_disable();
boot_mode_menu_select();
mtk_wdt_init();
return;
}
#ifdef MT65XX_RECOVERY_KEY
if(mtk_detect_key(MT65XX_RECOVERY_KEY))
{
printf("%s Detect cal key\n",MODULE_NAME);
printf("%s Enable recovery mode\n",MODULE_NAME);
g_boot_mode = RECOVERY_BOOT;
//video_printf("%s : detect recovery mode !\n",MODULE_NAME);
return;
}
#endif
}
#else
/*We put conditions here to filer some cases that can not do key detection*/
/*Check RTC to know if system want to reboot to Fastboot*/
#ifdef MTK_FASTBOOT_SUPPORT
if(Check_RTC_PDN1_bit13())
{
dprintf(INFO,"[FASTBOOT] reboot to boot loader\n");
g_boot_mode = FASTBOOT;
Set_Clr_RTC_PDN1_bit13(false);
return ;
}
#endif
/*If forbidden mode is factory, cacel the factory key detection*/
if(g_boot_arg->sec_limit.magic_num == 0x4C4C4C4C)
{
if(g_boot_arg->sec_limit.forbid_mode == F_FACTORY_MODE)
{
//Forbid to enter factory mode
printf("%s Forbidden\n",MODULE_NAME);
factory_forbidden=1;
}
}
// forbid_mode = g_boot_arg->boot_mode &= 0x000000FF;
/*If boot reason is power key + volumn down, then
disable factory mode dectection*/
if(mtk_detect_pmic_just_rst())
{
factory_forbidden=1;
}
/*Check RTC to know if system want to reboot to Recovery*/
if(Check_RTC_Recovery_Mode())
{
g_boot_mode = RECOVERY_BOOT;
return ;
}
/*If MISC Write has not completed in recovery mode
and interrupted by system reboot, go to recovery mode to
finish remain tasks*/
if(unshield_recovery_detection())
{
return ;
}
ulong begin = get_timer(0);
/*we put key dectection here to detect key which is pressed*/
while(get_timer(begin)<50){
#ifdef MTK_FASTBOOT_SUPPORT
if(mtk_detect_key(MT_CAMERA_KEY))
{
dprintf(INFO,"[FASTBOOT]Key Detect\n");
g_boot_mode = FASTBOOT;
return ;
}
#endif
if(!factory_forbidden){
if(mtk_detect_key(MT65XX_FACTORY_KEY))
{
printf("%s Detect key\n",MODULE_NAME);
printf("%s Enable factory mode\n",MODULE_NAME);
g_boot_mode = FACTORY_BOOT;
//video_printf("%s : detect factory mode !\n",MODULE_NAME);
return ;
}
}
#ifdef MT65XX_RECOVERY_KEY
if(mtk_detect_key(MT65XX_RECOVERY_KEY))
{
printf("%s Detect cal key\n",MODULE_NAME);
printf("%s Enable recovery mode\n",MODULE_NAME);
g_boot_mode = RECOVERY_BOOT;
//video_printf("%s : detect recovery mode !\n",MODULE_NAME);
return ;
}
#endif
}
#endif
#ifdef MTK_KERNEL_POWER_OFF_CHARGING
if(kernel_power_off_charging_detection())
{
printf(" < Kernel Power Off Charging Detection Ok> \n");
return;
}
else
{
printf("< Kernel Enter Normal Boot > \n");
}
#endif
}
boot_mode_select函数中的g_boot_mode变量尤为重要,不同的赋值决定着它boot什么。在boot_mode_select函数中可以看到所有的分支,对boot_mode_select的赋值,要么是NORMAL_BOOT要么是FASTBOOT要么是RECOVERY_BOOT,似乎都和充电界面没什么关系啊。但是注意函数的尾部:
#ifdef MTK_KERNEL_POWER_OFF_CHARGING
if(kernel_power_off_charging_detection())
{
printf(" < Kernel Power Off Charging Detection Ok> \n");
return;
}
else
{
printf("< Kernel Enter Normal Boot > \n");
}
#endif
看到了power_off_charging,似乎和关机状态下的充电有关系,再看两个分支的printf,一个分支和充电相关,一个分支是Kernel Enter Normal Boot正常开机启动。马上搜索kernel_power_off_charging_detection的定义:
find -name “*.c” | xargs grep “kernel_power_off_charging_detection”
在bootable\bootloader\lk\platform\mt6582\mt_kernel_power_off_charging.c中找到了这个函数,看文件名,显然是和关机充电相关的代码:
BOOL kernel_power_off_charging_detection(void)
{
int off_mode_status = 1;
/* */
if(is_force_boot()) {
upmu_set_rg_chrind_on(0);
printf("[%s] Turn off HW Led\n", __func__);
return FALSE;
}
off_mode_status = get_off_mode_charge_status();
printf("[%s] off_mode_status %d\n", __func__, off_mode_status);
if(upmu_is_chr_det() == KAL_TRUE) {
if (off_mode_status) {
g_boot_mode = KERNEL_POWER_OFF_CHARGING_BOOT;
} else {
g_boot_mode = NORMAL_BOOT;
upmu_set_rg_chrind_on(0);
return FALSE;
}
return TRUE;
}
else {
/* power off */
#ifndef NO_POWER_OFF
printf("[kernel_power_off_charging_detection] power off\n");
mt6575_power_off();
#endif
return FALSE;
}
/* */
}
可以看到这里对g_boot_mode有两种赋值,一种是关机状态下充电,一种是正常开机。那么一个改动思路就是将:
g_boot_mode = KERNEL_POWER_OFF_CHARGING_BOOT;
改成
g_boot_mode = NORMAL_BOOT;
这样原本要进行关机充电的,改成了正常开机启动。
还有一种找关键代码的思路是,找到了g_boot_mode这个变量后,在LK代码目录中搜索,看看哪里对它进行了赋值:
find -name “*.c” | xargs grep “g_boot_mode = ”
可以看到,只有bootable\bootloader\lk\platform\mt6582\mt_kernel_power_off_charging.c 文件中对g_boot_mode赋值为KERNEL_POWER_OFF_CHARGING_BOOT,其它地方都是赋值为NORMAL_BOOT、FASTBOOT、RECOVERY_BOOT、ALARM_BOOT等,明显和充电无关。这样也能快速定位到关键代码,究竟是哪里,让它进入了关机充电那个界面。
此时还没有结束,我又注意到了这个宏MTK_KERNEL_POWER_OFF_CHARGING:
#ifdef MTK_KERNEL_POWER_OFF_CHARGING
if(kernel_power_off_charging_detection())
{
printf(" < Kernel Power Off Charging Detection Ok> \n");
return;
}
else
{
printf("< Kernel Enter Normal Boot > \n");
}
#endif
在bootable\bootloader\lk\platform\mt6582\boot_mode.c的 boot_mode_select 函数中,如果代码运行到这里,又没定义MTK_KERNEL_POWER_OFF_CHARGING这个宏,宏内这段代码不会执行,那么此时g_boot_mode的赋值是什么?当然是初始值NORMAL_BOOT,当然这只是猜想,万一哪个函数里改变了它的值呢,接下来在这个位置插入打LOG的代码把g_boot_mode的值输出来看看,果然是NORMAL_BOOT。那么尝试取消定义MTK_KERNEL_POWER_OFF_CHARGING这个宏,是不是就能实现直接开机的效果呢?经过测试,果然如此。
这个宏的意思是是否允许在关机状态下充电,可以理解为,如果不允许,那么插电后,设备会开机再充电。
当然,宏的改法是MTK平台特有的,其它平台可以参考代码的改法,虽然不同平台的LK代码有差异,但总体流程还是不变的。
那么,如何取消这个宏的定义呢:
通常这个宏位于ProjectConfig.mk中,具体路径各不相同,如果找不到的话可以find -name “ProjectConfig.mk”搜索,另外有的同学改了以后还是无效,那可能不止这一处的mk文件定义了MTK_KERNEL_POWER_OFF_CHARGING哦,最简单的办法是直接全局搜索:
find -name “*.mk” | xargs grep “MTK_KERNEL_POWER_OFF_CHARGING = yes”
然后全部改为MTK_KERNEL_POWER_OFF_CHARGING = no
改完以后重新编译LK
mmm bootable/bootloader/lk:clean-lk
mmm bootable/bootloader/lk:lk
然后make snod 并 Download
USB插入,通电
Perfect!
总结
① 对于MTK平台,关键在于MTK_KERNEL_POWER_OFF_CHARGING这个宏,搜索源代码目录下的所有mk文件:
find -name “*.mk” | xargs grep “MTK_KERNEL_POWER_OFF_CHARGING = yes”
将其替换为为MTK_KERNEL_POWER_OFF_CHARGING = no即可
② 如果是MTK平台,但是没有MTK_KERNEL_POWER_OFF_CHARGING这个宏,尝试找到bootable\bootloader\lk\platform\mt6582\mt_kernel_power_off_charging.c文件
你的源码不一定是这个路径,尝试搜索mt_kernel_power_off_charging.c
find -name “mt_kernel_power_off_charging.c”
里面有个kernel_power_off_charging_detection(void)函数,函数内对g_boot_mode的赋值,通通改为g_boot_mode = NORMAL_BOOT;
③ 对于高通等其它平台,如果没有方法②中的函数,需要花点功夫了,首先定位bootloader的lk部分代码,然后搜索这些和启动模式相关的关键词:
g_boot_mode NORMAL_BOOT FASTBOOT RECOVERY_BOOT
搜索哪些地方对g_boot_mode赋值了:
find -name “*.c” | xargs grep “g_boot_mode = ”
着重去分析这些地方,配合打LOG调试,相信你很快就能搞定
原文:https://blog.csdn.net/CharlesSimonyi/article/details/82236494