一、综述
最近在做传音的项目,提了一个电池高低温提醒需求。
1.温度58+-1:电池因温度过高,即将关机
2.温度53+-1:您的电池温度过高,请勿充电
3.温度2+-1 :您的电池温度过低,请勿充电
4.温度-18+-1 :电池因温度过低,即将关机
以上是弹出dialog框 提醒功能,另外还有一个功能
5.温度60度,不插充电器会自动关机,插充电器会重启
(该功能MTK默认实现了,跟踪源码时可以看到)
电池这一块,我们公司薛大哥 比较牛
这几天,就跑去跟我们薛大哥唠唠嗑
学习了一下底层电池的一些知识,然后开启源码研究之路
薛大哥->都26好几了,没对象 愁嫁 哈哈哈
今天跑去找她唠嗑,突然聊到婚姻问题,突然她说了个事,真的笑死我了
she said:上个月,有个领导开会,贼能说,开了3个小时,她听得晕头转向,贼烦,突然领导问她,有啥疑问没。
她说:我想结婚(啊 哈啊哈哈哈哈哈)
我接了句:是不是最近孤独了,想要二人世界了
她回了句:对啊,最近寂寞空虚冷(啊哈哈哈哈啊哈哈哈哈)
学习本文你会掌握以下知识点:
1.Android从底层到上层,电池高低温提醒流程源码研究
2.电池高低温提醒客制化
二、从底层到上层,电池高低温提醒流程源码研究
1.kernel层
系统开机的时候,会调用充电入口函数BAT_thread()去初始化一些相关参数,获取电量,检测cpu温度和电池温度
路径:kernel-3.18/drivers/power/mediatek/battery_common.c
void BAT_thread(void)
{
//第一次调用时,battery_meter_initilized 为false,表示没初始化
static kal_bool battery_meter_initilized = KAL_FALSE;
这里省略部分源码,只关注我们需要的代码流程
mt_battery_thermal_check();//cup温度检测
mt_battery_notify_check();//电池温度检测
}
路径:kernel-3.18/drivers/power/mediatek/battery_common.c
接下来去跟进mt_battery_notify_check()函数
unsigned int g_BatteryNotifyCode = 0x0000;
unsigned int g_BN_TestMode = 0x0000;
void mt_battery_notify_check(void)
{
g_BatteryNotifyCode = 0x0000;
if (g_BN_TestMode == 0x0000) { /* for normal case */
battery_log(BAT_LOG_FULL, "[BATTERY] mt_battery_notify_check\n");
//充电电压检测(充电器插入的时候会抬高电压)
mt_battery_notify_VCharger_check();
//电池温度检测
mt_battery_notify_VBatTemp_check();
//充电电流检测
mt_battery_notify_ICharging_check();
//电池电压检测
mt_battery_notify_VBat_check();
//总体充电时间检测
mt_battery_notify_TotalChargingTime_check();
} else { /* for UI test */
//用于测试提醒功能
mt_battery_notify_UI_test();
}
}
分析:
这里的g_BatteryNotifyCode = 0x0000;初始化为0,表示系统一切正常,不需要任何提醒,这个变量会被写入节点中,(后面会介绍怎么写入节点的)系统会根据这个变量来提醒高低温,电压电流等。
g_BN_TestMode 默认是0x0000,所以一开始一定会去走if分支
其中
//充电电压检测(充电器插入的时候会抬高电压)
mt_battery_notify_VCharger_check();
//电池温度检测
mt_battery_notify_VBatTemp_check();
//充电电流检测
mt_battery_notify_ICharging_check();
//电池电压检测
mt_battery_notify_VBat_check();
//总体充电时间检测
mt_battery_notify_TotalChargingTime_check();
我们主要客制化电池温度,所以就关注mt_battery_notify_VBatTemp_check()函数。
后续如果有需求要客制化充电电流、充电电压、电池电压,充电时间,可以关注相应的函数去客制化。
static void mt_battery_notify_VBatTemp_check(void)
{
#if defined(BATTERY_NOTIFY_CASE_0002_VBATTEMP)
//如果电池温度>=60度
if (BMT_status.temperature >= batt_cust_data.max_charge_temperature) {
g_BatteryNotifyCode |= 0x0002;//设置提醒码为0x0002
//打印log
battery_log(BAT_LOG_CRTI, "[BATTERY] bat_temp(%d) out of range(too high)\n",
BMT_status.temperature);
}
#if defined(CONFIG_MTK_JEITA_STANDARD_SUPPORT)
//如果电池温度<0度
else if (BMT_status.temperature < TEMP_NEG_10_THRESHOLD) {
g_BatteryNotifyCode |= 0x0020;//设置提醒码为0x0020
battery_log(BAT_LOG_CRTI, "[BATTERY] bat_temp(%d) out of range(too low)\n",
BMT_status.temperature);
}
#else
#ifdef BAT_LOW_TEMP_PROTECT_ENABLE
//如果电池温度<0度
else if (BMT_status.temperature < MIN_CHARGE_TEMPERATURE) {
g_BatteryNotifyCode |= 0x0020;//设置提醒码为0x0020
battery_log(BAT_LOG_CRTI, "[BATTERY] bat_temp(%d) out of range(too low)\n",
BMT_status.temperature);
}
#endif
#endif
battery_log(BAT_LOG_FULL, "[BATTERY] BATTERY_NOTIFY_CASE_0002_VBATTEMP (%x)\n",
g_BatteryNotifyCode);
#endif
}
分析:
宏定义路径:
kernel-3.18/drivers/misc/mediatek/include/
mt-plat/mt6580/include/mach/mt_charging.h
可以看到源码中只定义了:
BATTERY_NOTIFY_CASE_0002_VBATTEMP
其余两个宏都没有定义或者被注释了
CONFIG_MTK_JEITA_STANDARD_SUPPORT//这个宏没有定义
BAT_LOW_TEMP_PROTECT_ENABLE//这个宏还被注释了
变量含义解析:
BMT_status.temperature:电池温度
batt_cust_data.max_charge_temperature:最大充电温度,默认50度
int __batt_init_cust_data_from_cust_header(void)
{
#if defined(MAX_CHARGE_TEMPERATURE)
batt_cust_data.max_charge_temperature = MAX_CHARGE_TEMPERATURE;
#endif
}
在以下路径中定义了MAX_CHARGE_TEMPERATURE
kernel-3.18/drivers/misc/mediatek/include/
mt-plat/mt6580/include/mach/mt_charging.h
#define MAX_CHARGE_TEMPERATURE 50
#define MIN_CHARGE_TEMPERATURE 0
TEMP_NEG_10_THRESHOLD:最低充电温度,默认为0度
kernel-3.18/drivers/misc/mediatek/include/mt-plat/battery_common.h
#define TEMP_NEG_10_THRESHOLD 0
到这里,我们如果想客制化温度,在函数mt_battery_notify_VBatTemp_check()中添加相应的客制化逻辑就行了,但是有一个问题还没弄清楚就是:
g_BatteryNotifyCode |= 0x0002;//设置提醒码为0x0002 高温提醒码
g_BatteryNotifyCode |= 0x0020;//设置提醒码为0x0020 低温提醒码
这里g_BatteryNotifyCode 默认为0x0000,那么
g_BatteryNotifyCode |= 0x0002
-> g_BatteryNotifyCode = g_BatteryNotifyCode | 0x002
也就是g_BatteryNotifyCode = 0x0000 | 0x0002 (任何数和0按位或等于本身)
g_BatteryNotifyCode = 0x0002 = 2(十进制)
同理
g_BatteryNotifyCode |= 0x0020 ->
g_BatteryNotifyCode = 0x0020 = 32(十进制)
这个g_BatteryNotifyCode 对应的十进制分别是2和32,那么因为还有两个需求,应该改怎么定制这个g_BatteryNotifyCode 呢?
就是高温关机提醒码,低温关机提醒码,应该设置成多少,难道可以随便设置,比如0x0003,答案肯定是不行的,需要分析上层代码来决定怎么去设置这个提醒码
我们继续往下研究
路径:kernel-3.18/drivers/power/mediatek/battery_common.c
代表中调用了
DEVICE_ATTR(BatteryNotify, 0664, show_BatteryNotify, store_BatteryNotify);
BatteryNotify 名称
0664 读写权限
show_BatteryNotify 读函数
store_BatteryNotify 写函数
创建了节点
/sys/devices/platform/charger/BatteryNotify
或者
/sys/devices/platform/mt-battery/BatteryNotify
具体创建的哪个就要看系统中定义的是charger还是mt-battery啦
例如这里定义的就是
kernel-3.18/drivers/power/mediatek/battery_common.c
关于DEVICE_ATTR更加详细的分析使用:
DEVICE_ATTR的使用
DEVICE_ATTR的实例分析
DEVICE_ATTR的原理及用法
在show_BatteryNotify()函数中,可以看到
return sprintf(buf, "%u\n", g_BatteryNotifyCode);
说明操作节点/sys/devices/platform/charger/BatteryNotify
实际上就是操作变量g_BatteryNotifyCode
所以改变g_BatteryNotifyCode的值相当于改变节点BatteryNotify的值
那么在哪里调用函数去读取这个节点呢?
frameworks层
vendor/mediatek/proprietary/frameworks/opt/batterywarning/batterywarning.cpp
分析:
在main函数中,会死循环调用readType()函数去读取type的值.
readType()函数中
先调用 pFile = fopen(FILE_NAME, "r");
打开节点/sys/devices/platform/mt-battery/BatteryNotify
调用fgets(buffer, MAX_CHAR, pFile) 把type的值写入buffer中
int type = atoi(buffer);把读到的buffer字符串转成数字
如果 type>0,就发送广播sendBroadcastMessage(String16(ACTION), type);
上层会接受这个广播,根据type去提醒系统。
注意到这里的ACTION
#define ACTION "mediatek.intent.action.BATTERY_WARNING"
有人发送广播,就有人接收广播,因此继续跟踪谁接收了这个广播。
广播接收者
路径:
vendor/mediatek/proprietary/packages/apps/BatteryWarning/src/com/mediatek
/batterywarning/BatteryWarningReceiver.java
分析:这里面,获取发送的广播,然后启动activity(BatteryWarningActivity),并且把type的值放在inten中传递过去
这里需要注意的就是
type = (int) (Math.log(type) / Math.log(2));
高中学的数学没忘记吧 哈哈哈
相当于type = log2 type(2是底数)
当传过来的type= 2时 ->转换后 type = 1
当传过来的type= 32时 ->转换后 type = 5
转换前的2和32代表底层的type,转换后的1和5是上层需要的type
我们继续去跟进BatteryWarningActivity.java,弄明白 转换后type=1 和type=5到底啥意思
type的含义类型
路径:
vendor/mediatek/proprietary/packages/apps/BatteryWarning/src/com/mediatek
/batterywarning/BatteryWarningActivity.java
CHARGER_OVER_VOLTAGE_TYPE = 0;//充电电压过高
BATTERY_OVER_TEMPERATURE_TYPE = 1;//电池温度过高
CURRENT_OVER_PROTECTION_TYPE = 2;//超过电流保护
BATTERY_OVER_VOLTAGE_TYPE = 3;//电池电压过高
SAFETY_OVER_TIMEOUT_TYPE = 4;//充电时间过长
BATTERY_LOW_TEMPERATURE_TYPE = 5;//电池温度过低
路径: vendor/mediatek/proprietary/packages/apps
/BatteryWarning/res/values-zh-rCN/strings.xml
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="title_charger_over_voltage">"充电电压过高"</string>
<string name="title_battery_over_temperature">"电池温度过高"</string>
<string name="title_over_current_protection">"超过电流保护"</string>
<string name="title_battery_over_voltage">"电池电压过高"</string>
<string name="title_safety_timer_timeout">"充电时间过长"</string>
<string name="title_battery_low_temperature">"电池温度过低"</string>
<string name="msg_charger_over_voltage">"您的充电器电压过高,请断开充电器!"</string>
<string name="msg_battery_over_temperature">"您的电池温度过高,请移除电池!"</string>
<string name="msg_over_current_protection">"您的电池超过电流保护,请移除电池!"</string>
<string name="msg_battery_over_voltage">"您的电池电压过高,请移除电池!"</string>
<string name="msg_safety_timer_timeout">"充电时间过长,请断开充电器!"</string>
<string name="msg_battery_low_temperature">"您的电池温度过低,请断开充电器!"</string>
<string name="btn_ok_msg">"稍后提醒"</string>
<string name="btn_cancel_msg">"忽略"</string>
</resources>
因此
底层0x0002(十六进制)->type= 2 (十进制)->转换后 type = 1
代表电池温度过高
底层0x0020(十六进制)->type= 32(十进制)时 ->转换后 type = 5
代表电池温度过低
在onCreate()方法中,如果mType类型在0-5之间,就调用showWarningDialog()弹出提醒框。
分析到这里,整体的流程已经弄明白了,那么客制化的实现也很简单了。
三、电池高低温提醒客制化
1.确定底层type
type=32 (0x0020) :电池温度过低,即将关机,温度0
type=2 (0x0002) : 电池温度过高,即将关机,温度58
这里的功能系统已经存在了
新增功能
type=64 (0x0040) :您的电池温度过高,请勿充电 温度53
type=128(0x0080):您的电池温度过低,请勿充电温度2
kernel-3.18/drivers/power/mediatek/battery_common.c
static void mt_battery_notify_VBatTemp_check(void)
{
#if defined(BATTERY_NOTIFY_CASE_0002_VBATTEMP)
//这里把batt_cust_data.max_charge_temperature改成53度
//我这边只是懒人写法 真正写的时候 还是要按照系统源码习惯去写
if(BMT_status.temperature >=58){
// 电池温度过高,即将关机,温度58
g_BatteryNotifyCode |= 0x0002;
}
else if (BMT_status.temperature >= 53) {
//:您的电池温度过高,请勿充电 温度53
g_BatteryNotifyCode |= 0x0040;
}
else if (BMT_status.temperature < -18) {
//电池温度过低,即将关机,温度0
g_BatteryNotifyCode |= 0x0020;
}
else if(BMT_status.temperature < 2){
//您的电池温度过低,请勿充电,温度2
g_BatteryNotifyCode |= 0x0080;
}
#endif
}
这里代码很简单,需要注意的就是判断条件
先判断58度,在判断53度(先判断53 在判断58 那永远都都不进58这个分支)
先判断0度,在判断2度
vendor/mediatek/proprietary/packages/apps
/BatteryWarning/res/values-zh-rCN/strings.xml
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="title_battery_low_temperature_can_not_charger">
"电池温度过低,请勿充电"
</string>
<string name="title_battery_over_temperature_can_not_charger">
"电池温度过高,请勿充电"
</string>
<string name="msg_battery_low_temperature_can_not_charger">
"您的电池温度过低,请勿充电"
</string>
<string name="msg_battery_over_temperature_can_not_charger">
"您的电池温度过高,请勿充电!"
</string>
</resources>
vendor/mediatek/proprietary/packages/apps/BatteryWarning/src/com/mediatek
/batterywarning/BatteryWarningActivity.java
private static final int CHARGER_OVER_VOLTAGE_TYPE = 0;
private static final int BATTERY_OVER_TEMPERATURE_TYPE = 1;
private static final int CURRENT_OVER_PROTECTION_TYPE = 2;
private static final int BATTERY_OVER_VOLTAGE_TYPE = 3;
private static final int SAFETY_OVER_TIMEOUT_TYPE = 4;
private static final int BATTERY_LOW_TEMPERATURE_TYPE = 5;
//add
private static final int BATTERY_HIGH_TEMPERATURE_CAN_NOT_CHARGER_TYPE = 6;
private static final int BATTERY_LOW_TEMPERATURE_CAN_NOT_CHARGER_TYPE = 7;
//end add
static final int[] sWarningTitle = new int[] {
R.string.title_charger_over_voltage,
R.string.title_battery_over_temperature,
R.string.title_over_current_protection,
R.string.title_battery_over_voltage,
R.string.title_safety_timer_timeout,
R.string.title_battery_low_temperature
R.string.title_battery_low_temperature_can_not_charger,//add
R.string.title_battery_over_temperature_can_not_charger//add
};
private static final int[] sWarningMsg = new int[] {
R.string.msg_charger_over_voltage,
R.string.msg_battery_over_temperature,
R.string.msg_over_current_protection,
R.string.msg_battery_over_voltage,
R.string.msg_safety_timer_timeout,
R.string.msg_battery_low_temperature
R.string.msg_battery_low_temperature_can_not_charger,//add
R.string.msg_battery_over_temperature_can_not_charger//add
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.battery_warning);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,
R.layout.custom_title_1);
Intent intent = getIntent();
mType = intent.getIntExtra(KEY_TYPE, -1);
TextView textView = (TextView) findViewById(R.id.left_text);
textView.setText(getString(sWarningTitle[mType]));
Log.d(TAG, "onCreate, mType is " + mType);
//原来是0-5 改成0-7
if (mType >= CHARGER_OVER_VOLTAGE_TYPE
&& mType <= BATTERY_LOW_TEMPERATURE_CAN_NOT_CHARGER_TYPE ) {//修改
showWarningDialog(mType);
registerReceiver(mReceiver, new IntentFilter(
Intent.ACTION_POWER_DISCONNECTED));
} else {
finish();
}
}
四、60度时插入充电器重启,不插充电器自动关机功能
这个功能MTK已经默认实现了,我们分析一下源码即可
kernel-3.18/drivers/power/mediatek/battery_common.c
static void mt_battery_thermal_check(void)
{
//省略部分代码
//如果温度>=60度
if (BMT_status.temperature >= 60) {
//判断开机模式
if ((g_platform_boot_mode == META_BOOT)
|| (g_platform_boot_mode == ADVMETA_BOOT)
|| (g_platform_boot_mode == ATE_FACTORY_BOOT)) {
//如果模式为 META_BOOT、ADVMETA_BOOT、ATE_FACTORY_BOOT
//打印log 啥也不干
battery_log(BAT_LOG_FULL,
"[BATTERY] boot mode = %d, bypass temperature check\n",
g_platform_boot_mode);
} else {
struct battery_data *bat_data = &battery_main;
struct power_supply *bat_psy = &bat_data->psy;
battery_log(BAT_LOG_CRTI,
"[Battery] Tbat(%d)>=60, system need power down.\n",
BMT_status.temperature);
bat_data->BAT_CAPACITY = 0;
power_supply_changed(bat_psy);
//如果充电器存在
if (BMT_status.charger_exist == KAL_TRUE) {
/* can not power down due to charger exist, so need reset system */
//重启系统
battery_charging_control(CHARGING_CMD_SET_PLATFORM_RESET, NULL);
}
/* avoid SW no feedback */
//关机
battery_charging_control(CHARGING_CMD_SET_POWER_OFF, NULL);
/* mt_power_off(); */
}
}
}
分析:
如果开机模式为:
META_BOOT、 ADVMETA_BOOT、 ATE_FACTORY_BOOT
这三种模式,只打印log 啥也不干
否则
如果充电器存在BMT_status.charger_exist == KAL_TRUE
调用
battery_charging_control (CHARGING_CMD_SET_PLATFORM_RESET, NULL);去重启系统
否则 调用
battery_charging_control(CHARGING_CMD_SET_POWER_OFF, NULL);
去关机
Stay hungry,Stay foolish!
荆轲刺秦王