一库正、綜述
最近在做傳音的項(xiàng)目稳诚,提了一個(gè)電池高低溫提醒需求棕所。
1.溫度58+-1:電池因溫度過(guò)高,即將關(guān)機(jī)
2.溫度53+-1:您的電池溫度過(guò)高辨宠,請(qǐng)勿充電
3.溫度2+-1 :您的電池溫度過(guò)低遗锣,請(qǐng)勿充電
4.溫度-18+-1 :電池因溫度過(guò)低,即將關(guān)機(jī)
以上是彈出dialog框 提醒功能嗤形,另外還有一個(gè)功能
5.溫度60度精偿,不插充電器會(huì)自動(dòng)關(guān)機(jī),插充電器會(huì)重啟
(該功能MTK默認(rèn)實(shí)現(xiàn)了,跟蹤源碼時(shí)可以看到)
電池這一塊笔咽,我們公司薛大哥 比較牛
這幾天搔预,就跑去跟我們薛大哥嘮嘮嗑
學(xué)習(xí)了一下底層電池的一些知識(shí),然后開(kāi)啟源碼研究之路
薛大哥->都26好幾了叶组,沒(méi)對(duì)象 愁嫁 哈哈哈
今天跑去找她嘮嗑拯田,突然聊到婚姻問(wèn)題,突然她說(shuō)了個(gè)事扶叉,真的笑死我了
she said:上個(gè)月勿锅,有個(gè)領(lǐng)導(dǎo)開(kāi)會(huì),賊能說(shuō)枣氧,開(kāi)了3個(gè)小時(shí)溢十,她聽(tīng)得暈頭轉(zhuǎn)向,賊煩达吞,突然領(lǐng)導(dǎo)問(wèn)她张弛,有啥疑問(wèn)沒(méi)。
她說(shuō):我想結(jié)婚(啊 哈啊哈哈哈哈哈)
我接了句:是不是最近孤獨(dú)了酪劫,想要二人世界了
她回了句:對(duì)啊吞鸭,最近寂寞空虛冷(啊哈哈哈哈啊哈哈哈哈)
學(xué)習(xí)本文你會(huì)掌握以下知識(shí)點(diǎn):
1.Android從底層到上層,電池高低溫提醒流程源碼研究
2.電池高低溫提醒客制化
二覆糟、從底層到上層刻剥,電池高低溫提醒流程源碼研究
1.kernel層
系統(tǒng)開(kāi)機(jī)的時(shí)候,會(huì)調(diào)用充電入口函數(shù)BAT_thread()去初始化一些相關(guān)參數(shù)滩字,獲取電量造虏,檢測(cè)cpu溫度和電池溫度
路徑:kernel-3.18/drivers/power/mediatek/battery_common.c
void BAT_thread(void)
{
//第一次調(diào)用時(shí),battery_meter_initilized 為false麦箍,表示沒(méi)初始化
static kal_bool battery_meter_initilized = KAL_FALSE;
這里省略部分源碼漓藕,只關(guān)注我們需要的代碼流程
mt_battery_thermal_check();//cup溫度檢測(cè)
mt_battery_notify_check();//電池溫度檢測(cè)
}
路徑:kernel-3.18/drivers/power/mediatek/battery_common.c
接下來(lái)去跟進(jìn)mt_battery_notify_check()函數(shù)
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");
//充電電壓檢測(cè)(充電器插入的時(shí)候會(huì)抬高電壓)
mt_battery_notify_VCharger_check();
//電池溫度檢測(cè)
mt_battery_notify_VBatTemp_check();
//充電電流檢測(cè)
mt_battery_notify_ICharging_check();
//電池電壓檢測(cè)
mt_battery_notify_VBat_check();
//總體充電時(shí)間檢測(cè)
mt_battery_notify_TotalChargingTime_check();
} else { /* for UI test */
//用于測(cè)試提醒功能
mt_battery_notify_UI_test();
}
}
分析:
這里的g_BatteryNotifyCode = 0x0000;初始化為0,表示系統(tǒng)一切正常挟裂,不需要任何提醒享钞,這個(gè)變量會(huì)被寫(xiě)入節(jié)點(diǎn)中,(后面會(huì)介紹怎么寫(xiě)入節(jié)點(diǎn)的)系統(tǒng)會(huì)根據(jù)這個(gè)變量來(lái)提醒高低溫诀蓉,電壓電流等栗竖。
g_BN_TestMode 默認(rèn)是0x0000,所以一開(kāi)始一定會(huì)去走if分支
其中
//充電電壓檢測(cè)(充電器插入的時(shí)候會(huì)抬高電壓)
mt_battery_notify_VCharger_check();
//電池溫度檢測(cè)
mt_battery_notify_VBatTemp_check();
//充電電流檢測(cè)
mt_battery_notify_ICharging_check();
//電池電壓檢測(cè)
mt_battery_notify_VBat_check();
//總體充電時(shí)間檢測(cè)
mt_battery_notify_TotalChargingTime_check();
我們主要客制化電池溫度渠啤,所以就關(guān)注mt_battery_notify_VBatTemp_check()函數(shù)划滋。
后續(xù)如果有需求要客制化充電電流、充電電壓埃篓、電池電壓,充電時(shí)間根资,可以關(guān)注相應(yīng)的函數(shù)去客制化架专。
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;//設(shè)置提醒碼為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;//設(shè)置提醒碼為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;//設(shè)置提醒碼為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
其余兩個(gè)宏都沒(méi)有定義或者被注釋了
CONFIG_MTK_JEITA_STANDARD_SUPPORT//這個(gè)宏沒(méi)有定義
BAT_LOW_TEMP_PROTECT_ENABLE//這個(gè)宏還被注釋了
變量含義解析:
BMT_status.temperature:電池溫度
batt_cust_data.max_charge_temperature:最大充電溫度同窘,默認(rèn)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:最低充電溫度,默認(rèn)為0度
kernel-3.18/drivers/misc/mediatek/include/mt-plat/battery_common.h
#define TEMP_NEG_10_THRESHOLD 0
到這里部脚,我們?nèi)绻肟椭苹瘻囟认氚睿诤瘮?shù)mt_battery_notify_VBatTemp_check()中添加相應(yīng)的客制化邏輯就行了,但是有一個(gè)問(wèn)題還沒(méi)弄清楚就是:
g_BatteryNotifyCode |= 0x0002;//設(shè)置提醒碼為0x0002 高溫提醒碼
g_BatteryNotifyCode |= 0x0020;//設(shè)置提醒碼為0x0020 低溫提醒碼
這里g_BatteryNotifyCode 默認(rèn)為0x0000委刘,那么
g_BatteryNotifyCode |= 0x0002
-> g_BatteryNotifyCode = g_BatteryNotifyCode | 0x002
也就是g_BatteryNotifyCode = 0x0000 | 0x0002 (任何數(shù)和0按位或等于本身)
g_BatteryNotifyCode = 0x0002 = 2(十進(jìn)制)
同理
g_BatteryNotifyCode |= 0x0020 ->
g_BatteryNotifyCode = 0x0020 = 32(十進(jìn)制)
這個(gè)g_BatteryNotifyCode 對(duì)應(yīng)的十進(jìn)制分別是2和32丧没,那么因?yàn)檫€有兩個(gè)需求,應(yīng)該改怎么定制這個(gè)g_BatteryNotifyCode 呢?
就是高溫關(guān)機(jī)提醒碼锡移,低溫關(guān)機(jī)提醒碼呕童,應(yīng)該設(shè)置成多少,難道可以隨便設(shè)置淆珊,比如0x0003夺饲,答案肯定是不行的,需要分析上層代碼來(lái)決定怎么去設(shè)置這個(gè)提醒碼
我們繼續(xù)往下研究
路徑:kernel-3.18/drivers/power/mediatek/battery_common.c
代表中調(diào)用了
DEVICE_ATTR(BatteryNotify, 0664, show_BatteryNotify, store_BatteryNotify);
BatteryNotify 名稱
0664 讀寫(xiě)權(quán)限
show_BatteryNotify 讀函數(shù)
store_BatteryNotify 寫(xiě)函數(shù)
創(chuàng)建了節(jié)點(diǎn)
/sys/devices/platform/charger/BatteryNotify
或者
/sys/devices/platform/mt-battery/BatteryNotify
具體創(chuàng)建的哪個(gè)就要看系統(tǒng)中定義的是charger還是mt-battery啦
例如這里定義的就是
kernel-3.18/drivers/power/mediatek/battery_common.c
關(guān)于DEVICE_ATTR更加詳細(xì)的分析使用:
DEVICE_ATTR的使用
DEVICE_ATTR的實(shí)例分析
DEVICE_ATTR的原理及用法
在show_BatteryNotify()函數(shù)中施符,可以看到
return sprintf(buf, "%u\n", g_BatteryNotifyCode);
說(shuō)明操作節(jié)點(diǎn)/sys/devices/platform/charger/BatteryNotify
實(shí)際上就是操作變量g_BatteryNotifyCode
所以改變g_BatteryNotifyCode的值相當(dāng)于改變節(jié)點(diǎn)BatteryNotify的值
那么在哪里調(diào)用函數(shù)去讀取這個(gè)節(jié)點(diǎn)呢往声?
frameworks層
vendor/mediatek/proprietary/frameworks/opt/batterywarning/batterywarning.cpp
分析:
在main函數(shù)中,會(huì)死循環(huán)調(diào)用readType()函數(shù)去讀取type的值.
readType()函數(shù)中
先調(diào)用 pFile = fopen(FILE_NAME, "r");
打開(kāi)節(jié)點(diǎn)/sys/devices/platform/mt-battery/BatteryNotify
調(diào)用fgets(buffer, MAX_CHAR, pFile) 把type的值寫(xiě)入buffer中
int type = atoi(buffer);把讀到的buffer字符串轉(zhuǎn)成數(shù)字
如果 type>0戳吝,就發(fā)送廣播sendBroadcastMessage(String16(ACTION), type);
上層會(huì)接受這個(gè)廣播浩销,根據(jù)type去提醒系統(tǒng)。
注意到這里的ACTION
#define ACTION "mediatek.intent.action.BATTERY_WARNING"
有人發(fā)送廣播听哭,就有人接收廣播慢洋,因此繼續(xù)跟蹤誰(shuí)接收了這個(gè)廣播。
廣播接收者
路徑:
vendor/mediatek/proprietary/packages/apps/BatteryWarning/src/com/mediatek
/batterywarning/BatteryWarningReceiver.java
分析:這里面欢唾,獲取發(fā)送的廣播且警,然后啟動(dòng)activity(BatteryWarningActivity),并且把type的值放在inten中傳遞過(guò)去
這里需要注意的就是
type = (int) (Math.log(type) / Math.log(2));
高中學(xué)的數(shù)學(xué)沒(méi)忘記吧 哈哈哈
相當(dāng)于type = log2 type(2是底數(shù))
當(dāng)傳過(guò)來(lái)的type= 2時(shí) ->轉(zhuǎn)換后 type = 1
當(dāng)傳過(guò)來(lái)的type= 32時(shí) ->轉(zhuǎn)換后 type = 5
轉(zhuǎn)換前的2和32代表底層的type礁遣,轉(zhuǎn)換后的1和5是上層需要的type
我們繼續(xù)去跟進(jìn)BatteryWarningActivity.java斑芜,弄明白 轉(zhuǎn)換后type=1 和type=5到底啥意思
type的含義類(lèi)型
路徑:
vendor/mediatek/proprietary/packages/apps/BatteryWarning/src/com/mediatek
/batterywarning/BatteryWarningActivity.java
CHARGER_OVER_VOLTAGE_TYPE = 0;//充電電壓過(guò)高
BATTERY_OVER_TEMPERATURE_TYPE = 1;//電池溫度過(guò)高
CURRENT_OVER_PROTECTION_TYPE = 2;//超過(guò)電流保護(hù)
BATTERY_OVER_VOLTAGE_TYPE = 3;//電池電壓過(guò)高
SAFETY_OVER_TIMEOUT_TYPE = 4;//充電時(shí)間過(guò)長(zhǎng)
BATTERY_LOW_TEMPERATURE_TYPE = 5;//電池溫度過(guò)低
路徑: 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">"充電電壓過(guò)高"</string>
<string name="title_battery_over_temperature">"電池溫度過(guò)高"</string>
<string name="title_over_current_protection">"超過(guò)電流保護(hù)"</string>
<string name="title_battery_over_voltage">"電池電壓過(guò)高"</string>
<string name="title_safety_timer_timeout">"充電時(shí)間過(guò)長(zhǎng)"</string>
<string name="title_battery_low_temperature">"電池溫度過(guò)低"</string>
<string name="msg_charger_over_voltage">"您的充電器電壓過(guò)高,請(qǐng)斷開(kāi)充電器祟霍!"</string>
<string name="msg_battery_over_temperature">"您的電池溫度過(guò)高杏头,請(qǐng)移除電池!"</string>
<string name="msg_over_current_protection">"您的電池超過(guò)電流保護(hù)沸呐,請(qǐng)移除電池醇王!"</string>
<string name="msg_battery_over_voltage">"您的電池電壓過(guò)高,請(qǐng)移除電池崭添!"</string>
<string name="msg_safety_timer_timeout">"充電時(shí)間過(guò)長(zhǎng)寓娩,請(qǐng)斷開(kāi)充電器!"</string>
<string name="msg_battery_low_temperature">"您的電池溫度過(guò)低,請(qǐng)斷開(kāi)充電器!"</string>
<string name="btn_ok_msg">"稍后提醒"</string>
<string name="btn_cancel_msg">"忽略"</string>
</resources>
因此
底層0x0002(十六進(jìn)制)->type= 2 (十進(jìn)制)->轉(zhuǎn)換后 type = 1
代表電池溫度過(guò)高
底層0x0020(十六進(jìn)制)->type= 32(十進(jìn)制)時(shí) ->轉(zhuǎn)換后 type = 5
代表電池溫度過(guò)低
在onCreate()方法中棘伴,如果mType類(lèi)型在0-5之間寞埠,就調(diào)用showWarningDialog()彈出提醒框。
分析到這里焊夸,整體的流程已經(jīng)弄明白了仁连,那么客制化的實(shí)現(xiàn)也很簡(jiǎn)單了。
三阱穗、電池高低溫提醒客制化
1.確定底層type
type=32 (0x0020) :電池溫度過(guò)低饭冬,即將關(guān)機(jī),溫度0
type=2 (0x0002) : 電池溫度過(guò)高揪阶,即將關(guān)機(jī)昌抠,溫度58
這里的功能系統(tǒng)已經(jīng)存在了
新增功能
type=64 (0x0040) :您的電池溫度過(guò)高,請(qǐng)勿充電 溫度53
type=128(0x0080):您的電池溫度過(guò)低遣钳,請(qǐng)勿充電溫度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度
//我這邊只是懶人寫(xiě)法 真正寫(xiě)的時(shí)候 還是要按照系統(tǒng)源碼習(xí)慣去寫(xiě)
if(BMT_status.temperature >=58){
// 電池溫度過(guò)高扰魂,即將關(guān)機(jī),溫度58
g_BatteryNotifyCode |= 0x0002;
}
else if (BMT_status.temperature >= 53) {
//:您的電池溫度過(guò)高蕴茴,請(qǐng)勿充電 溫度53
g_BatteryNotifyCode |= 0x0040;
}
else if (BMT_status.temperature < -18) {
//電池溫度過(guò)低劝评,即將關(guān)機(jī),溫度0
g_BatteryNotifyCode |= 0x0020;
}
else if(BMT_status.temperature < 2){
//您的電池溫度過(guò)低倦淀,請(qǐng)勿充電,溫度2
g_BatteryNotifyCode |= 0x0080;
}
#endif
}
這里代碼很簡(jiǎn)單蒋畜,需要注意的就是判斷條件
先判斷58度,在判斷53度(先判斷53 在判斷58 那永遠(yuǎn)都都不進(jìn)58這個(gè)分支)
先判斷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">
"電池溫度過(guò)低撞叽,請(qǐng)勿充電"
</string>
<string name="title_battery_over_temperature_can_not_charger">
"電池溫度過(guò)高姻成,請(qǐng)勿充電"
</string>
<string name="msg_battery_low_temperature_can_not_charger">
"您的電池溫度過(guò)低,請(qǐng)勿充電"
</string>
<string name="msg_battery_over_temperature_can_not_charger">
"您的電池溫度過(guò)高愿棋,請(qǐng)勿充電科展!"
</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);
//原來(lái)是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度時(shí)插入充電器重啟糠雨,不插充電器自動(dòng)關(guān)機(jī)功能
這個(gè)功能MTK已經(jīng)默認(rèn)實(shí)現(xiàn)了才睹,我們分析一下源碼即可
kernel-3.18/drivers/power/mediatek/battery_common.c
static void mt_battery_thermal_check(void)
{
//省略部分代碼
//如果溫度>=60度
if (BMT_status.temperature >= 60) {
//判斷開(kāi)機(jī)模式
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 */
//重啟系統(tǒng)
battery_charging_control(CHARGING_CMD_SET_PLATFORM_RESET, NULL);
}
/* avoid SW no feedback */
//關(guān)機(jī)
battery_charging_control(CHARGING_CMD_SET_POWER_OFF, NULL);
/* mt_power_off(); */
}
}
}
分析:
如果開(kāi)機(jī)模式為:
META_BOOT琅攘、 ADVMETA_BOOT、 ATE_FACTORY_BOOT
這三種模式松邪,只打印log 啥也不干
否則
如果充電器存在BMT_status.charger_exist == KAL_TRUE
調(diào)用
battery_charging_control (CHARGING_CMD_SET_PLATFORM_RESET, NULL);去重啟系統(tǒng)
否則 調(diào)用
battery_charging_control(CHARGING_CMD_SET_POWER_OFF, NULL);
去關(guān)機(jī)
Stay hungry坞琴,Stay foolish!
荊軻刺秦王