Android 8.0 MTK平台 电池高低温提醒客制化

一、综述

最近在做传音的项目,提了一个电池高低温提醒需求。

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

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

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

mt-battery


关于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

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
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
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
代表电池温度过低


BatteryWarningActivity.java

在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!
荆轲刺秦王

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 选择题部分 1.(),只有在发生短路事故时或者在负荷电流较大时,变流器中才会有足够的二次电流作为继电保护跳闸之用。...
    skystarwuwei阅读 12,738评论 0 7
  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 8,970评论 0 13
  • 安卓系统通过广播来接受电池状态,下面是我写的一个简单电池管理项目。 广播接受类BatteryReceiver如下:...
    简单Liml阅读 1,548评论 0 1
  • 姓名:房宇棠 学号:14020120013 转载自:http://www.ednchina.com/news/ar...
    YutangF阅读 1,139评论 0 2
  • 分享一个网友的段子: 王宝强去马场选了一匹马准备骑,这时候马场的师傅阻止了王宝强,说宝宝选的这匹马不好。宝宝问为啥...
    玛丽莲懵露阅读 398评论 0 0