searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Android 系统对手机电池保护机制设计方法

2024-04-08 01:35:59
22
0

本文以MTK 平台方案为例,从驱动底层到上层应用来说明在Android系统中对电池保护的机制的设计和实现方法。

 

驱动层:
 
在"kernel-4.4/drivers/power/mediatek/battery_common.c" 文件中
 
电池初始化时会设置电池的最大充电温度:
#if defined(MAX_CHARGE_TEMPERATURE)
         batt_cust_data.max_charge_temperature = MAX_CHARGE_TEMPERATURE;
#endif
 
MAX_CHARGE_TEMPERATURE   在文件中设置的值是50度
drivers/misc/mediatek/include/mt-plat/mt6771/include/mach/mtk_charging.h
#define MAX_CHARGE_TEMPERATURE  50
 
在函数 mt_battery_notify_VBatTemp_check 中,当 检查到 电池的温度超过MAX_CHARGE_TEMPERATURE这个设置时,就会将g_BatteryNotifyCode值设置为2,我们记住这个数字2,这个数字会在后续的应用中用到
 
static void mt_battery_notify_VBatTemp_check(void)
{
......
 
        if (BMT_status.temperature >= batt_cust_data.max_charge_temperature) {
                g_BatteryNotifyCode |= 0x0002;
                battery_log(BAT_LOG_CRTI, "[BATTERY] bat_temp(%d) out of range(too high)\n",
                            BMT_status.temperature);
        }
......
BMT_status.temperature 的值通过读取电池的热感寄存器的值来获取的。
 
同时对g_BatteryNotifyCode 这个变量进行赋值的还有mt_battery_notify_VBat_check、mt_battery_notify_ICharging_check、mt_battery_notify_TotalChargingTime_check等函数。
 
 
 
g_BatteryNotifyCode 这个变量的值会存到sys文件系统下的一个属性文件中:"/sys/devices/platform/charger/BatteryNotify"
 
在驱动中对这个文件的读写操作进行实现:
 
static ssize_t show_BatteryNotify(struct device *dev, struct device_attribute *attr, char *buf)
{
        battery_log(BAT_LOG_CRTI, "[Battery] show_BatteryNotify : %x\n", g_BatteryNotifyCode);
 
        return sprintf(buf, "%u\n", g_BatteryNotifyCode);
}
 
static ssize_t store_BatteryNotify(struct device *dev, struct device_attribute *attr,
                                   const char *buf, size_t size)
{
        /*char *pvalue = NULL;*/
        int rv;
        unsigned long reg_BatteryNotifyCode = 0;
 
        battery_log(BAT_LOG_CRTI, "[Battery] store_BatteryNotify\n");
        if (buf != NULL && size != 0) {
                battery_log(BAT_LOG_CRTI, "[Battery] buf is %s and size is %Zu\n", buf, size);
                rv = kstrtoul(buf, 0, &reg_BatteryNotifyCode);
                if (rv != 0)
                        return -EINVAL;
                g_BatteryNotifyCode = reg_BatteryNotifyCode;
                battery_log(BAT_LOG_CRTI, "[Battery] store code : %x\n", g_BatteryNotifyCode);
        }
        return size;
}
 
static DEVICE_ATTR(BatteryNotify, 0664, show_BatteryNotify, store_BatteryNotify);
 
 
HAL 层:
 
这里有一个进程叫做batteryWarning 的进程对电池的情况进行实时监控,其实这个进程监控的就是上面讲解的驱动里面写好的文件
 
"vendor/mediatek/proprietary/frameworks/opt/batterywarning/batterywarning.cpp"
 
 
#define FILE_NAME "/sys/devices/platform/charger/BatteryNotify"
 
void readType(char* buffer) {
    FILE * pFile;
    pFile = fopen(FILE_NAME, "r"); //打开sys文件: BatteryNotify
    if (pFile == NULL) {
        ALOGE("error opening file");
        return;
    } else {
        if (fgets(buffer, MAX_CHAR, pFile) == NULL) {
            fclose(pFile);
            ALOGE("can not get the string from the file");
            return;
        }
    }
    fclose(pFile);
    int type = atoi(buffer);
    if (type > 0)
    {
        ALOGD("start activity by send intent to BatteryWarningReceiver, type = %d\n", type);
        sendBroadcastMessage(String16(ACTION), type); //将读到的 BatteryNotify 文件值通过广播发送给更上层
    }
}
 
int main()
{
    char *buffer = (char*) malloc(MAX_CHAR * sizeof(char));
    if (buffer == NULL) {
        ALOGD("malloc memory failed");
        return 0;
    }
    while(1) {
        readType(buffer);  //死循环,隔10秒读取一次
        sleep(10);
    }
    free(buffer);
        return 0;
}
 
#define ACTION "mediatek.intent.action.BATTERY_WARNING"
 
在readType中,将读到的BatteryNotify的值会通过sendBroadcastMessage 这个函数发送广播ACTION出去,并且是带BatteryNotify的值的参数。
 
值得注意的是,这里面实现一种在CPP里面 实现发送广播的方法,非常值得借鉴,有兴趣的同学可以好好研究一下
 
bool sendBroadcastMessage(String16 action, int value)
 
{
    ALOGD("sendBroadcastMessage(): Action: %s, Value: %d ", (char *)(action.string()), value);
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> am = sm->getService(String16("activity"));
    if (am != NULL) {
        Parcel data, reply;
        data.writeInterfaceToken(String16("android.app.IActivityManager"));
        data.writeStrongBinder(NULL);
        // Add for match AMS change on O
        data.writeInt32(1);
        // intent begin
        data.writeString16(action); // action
        data.writeInt32(0); // URI data type
        data.writeString16(NULL, 0); // type
        data.writeInt32(0x04000000); // flags: FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
        data.writeString16(NULL, 0); // package name
        data.writeString16(NULL, 0); // component name
        data.writeInt32(0); // source bound - size
        data.writeInt32(0); // categories - size
        data.writeInt32(0); // selector - size
        data.writeInt32(0); // clipData - size
        data.writeInt32(-2); // contentUserHint: -2 -> UserHandle.USER_CURRENT
        data.writeInt32(-1); // bundle extras length
        data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L'
        int oldPos = data.dataPosition();
        data.writeInt32(1);  // size
        // data.writeInt32(0); // VAL_STRING, need to remove because of analyze common intent
        data.writeString16(String16(TYPE));
        data.writeInt32(1); // VAL_INTEGER
        data.writeInt32(value);
        int newPos = data.dataPosition();
        data.setDataPosition(oldPos - 8);
        data.writeInt32(newPos - oldPos); // refill bundle extras length
        data.setDataPosition(newPos);
...
}
 
 
上层APP:
 
上层有个BatteryWarning 的APP在接收这个广播mediatek.intent.action.BATTERY_WARNING:
 
"vendor/mediatek/proprietary/packages/apps/BatteryWarning/AndroidManifest.xml"
 
<receiver android:name="com.mediatek.batterywarning.BatteryWarningReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
            <intent-filter>
                <action android:name="mediatek.intent.action.BATTERY_WARNING" />
            </intent-filter>
            <intent-filter>
                <action android:name="mediatek.intent.action.THERMAL_DIAG" />
            </intent-filter>
</receiver>
 
 
 
vendor/mediatek/proprietary/packages/apps/BatteryWarning/src/com/mediatek/batterywarning/BatteryWarningReceiver.java
中会对相应广播传来的值进行处理:
 
public void onReceive(Context context, Intent intent) {
        ......
        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
            Log.d(TAG, action + " clear battery_warning_settings shared preference");
            SharedPreferences.Editor editor = getSharedPreferences().edit();
            editor.clear();
            editor.apply();
        } else if (ACTION_BATTERY_WARNING.equals(action)) {
            Log.d(TAG, action + " start activity according to shared preference");
            int type = intent.getIntExtra("type", -1);
            Log.d(TAG, "type = " + type);
            type = (int) (Math.log(type) / Math.log(2));
 
            if (type < 0 || type >= BatteryWarningActivity.sWarningTitle.length) { 
                return;
            }
            boolean showDialogFlag = getSharedPreferences().getBoolean(
                    Integer.toString(type), true);
            Log.d(TAG, "type = " + type + "showDialogFlag = " + showDialogFlag);
            if (showDialogFlag) {
                Intent activityIntent = new Intent();
                activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                        | Intent.FLAG_ACTIVITY_CLEAR_TOP
                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                activityIntent.setClass(mContext, BatteryWarningActivity.class);
                activityIntent.putExtra(BatteryWarningActivity.KEY_TYPE, type);
                mContext.startActivity(activityIntent);
            }
        } else if (ACTION_THERMAL_WARNING.equals(action)) {
            int typeValue = intent.getIntExtra(ThermalWarningActivity.KEY_TYPE, -1);
            Log.d(TAG, "typeValue = " + typeValue);
            if (typeValue == 0 || typeValue == 1) {
                Intent thermalIntent = new Intent();
                thermalIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
......
}
 
 
摘出一段进行说明:
在条件if (ACTION_BATTERY_WARNING.equals(action)) 中
 
// 这个地方会对传来的值范围进行判断,如果没有超过BatteryWarningActivity 中的 数组sWarningTitle 的范围值,就可以直接返回了,否则才跳转到BatteryWarningActivity 进行处理。
if (type < 0 || type >= BatteryWarningActivity.sWarningTitle.length) { 
                return;
}
 
 
在BatteryWarningActivity  中,我们就可以看到,当充电charger的状态在如下范围时,就会根据不同的mType 值而显示不同的警告dialog给用户,同时会主动断开充电
 
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;  //这个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;
 
 
    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);
        if (mType >= CHARGER_OVER_VOLTAGE_TYPE  && mType <= BATTERY_LOW_TEMPERATURE_TYPE) {
            showWarningDialog(mType);
            registerReceiver(mReceiver, new IntentFilter(
                    Intent.ACTION_POWER_DISCONNECTED));
        } else {
            finish();
        }
    }
0条评论
0 / 1000
朱****春
17文章数
3粉丝数
朱****春
17 文章 | 3 粉丝
原创

Android 系统对手机电池保护机制设计方法

2024-04-08 01:35:59
22
0

本文以MTK 平台方案为例,从驱动底层到上层应用来说明在Android系统中对电池保护的机制的设计和实现方法。

 

驱动层:
 
在"kernel-4.4/drivers/power/mediatek/battery_common.c" 文件中
 
电池初始化时会设置电池的最大充电温度:
#if defined(MAX_CHARGE_TEMPERATURE)
         batt_cust_data.max_charge_temperature = MAX_CHARGE_TEMPERATURE;
#endif
 
MAX_CHARGE_TEMPERATURE   在文件中设置的值是50度
drivers/misc/mediatek/include/mt-plat/mt6771/include/mach/mtk_charging.h
#define MAX_CHARGE_TEMPERATURE  50
 
在函数 mt_battery_notify_VBatTemp_check 中,当 检查到 电池的温度超过MAX_CHARGE_TEMPERATURE这个设置时,就会将g_BatteryNotifyCode值设置为2,我们记住这个数字2,这个数字会在后续的应用中用到
 
static void mt_battery_notify_VBatTemp_check(void)
{
......
 
        if (BMT_status.temperature >= batt_cust_data.max_charge_temperature) {
                g_BatteryNotifyCode |= 0x0002;
                battery_log(BAT_LOG_CRTI, "[BATTERY] bat_temp(%d) out of range(too high)\n",
                            BMT_status.temperature);
        }
......
BMT_status.temperature 的值通过读取电池的热感寄存器的值来获取的。
 
同时对g_BatteryNotifyCode 这个变量进行赋值的还有mt_battery_notify_VBat_check、mt_battery_notify_ICharging_check、mt_battery_notify_TotalChargingTime_check等函数。
 
 
 
g_BatteryNotifyCode 这个变量的值会存到sys文件系统下的一个属性文件中:"/sys/devices/platform/charger/BatteryNotify"
 
在驱动中对这个文件的读写操作进行实现:
 
static ssize_t show_BatteryNotify(struct device *dev, struct device_attribute *attr, char *buf)
{
        battery_log(BAT_LOG_CRTI, "[Battery] show_BatteryNotify : %x\n", g_BatteryNotifyCode);
 
        return sprintf(buf, "%u\n", g_BatteryNotifyCode);
}
 
static ssize_t store_BatteryNotify(struct device *dev, struct device_attribute *attr,
                                   const char *buf, size_t size)
{
        /*char *pvalue = NULL;*/
        int rv;
        unsigned long reg_BatteryNotifyCode = 0;
 
        battery_log(BAT_LOG_CRTI, "[Battery] store_BatteryNotify\n");
        if (buf != NULL && size != 0) {
                battery_log(BAT_LOG_CRTI, "[Battery] buf is %s and size is %Zu\n", buf, size);
                rv = kstrtoul(buf, 0, &reg_BatteryNotifyCode);
                if (rv != 0)
                        return -EINVAL;
                g_BatteryNotifyCode = reg_BatteryNotifyCode;
                battery_log(BAT_LOG_CRTI, "[Battery] store code : %x\n", g_BatteryNotifyCode);
        }
        return size;
}
 
static DEVICE_ATTR(BatteryNotify, 0664, show_BatteryNotify, store_BatteryNotify);
 
 
HAL 层:
 
这里有一个进程叫做batteryWarning 的进程对电池的情况进行实时监控,其实这个进程监控的就是上面讲解的驱动里面写好的文件
 
"vendor/mediatek/proprietary/frameworks/opt/batterywarning/batterywarning.cpp"
 
 
#define FILE_NAME "/sys/devices/platform/charger/BatteryNotify"
 
void readType(char* buffer) {
    FILE * pFile;
    pFile = fopen(FILE_NAME, "r"); //打开sys文件: BatteryNotify
    if (pFile == NULL) {
        ALOGE("error opening file");
        return;
    } else {
        if (fgets(buffer, MAX_CHAR, pFile) == NULL) {
            fclose(pFile);
            ALOGE("can not get the string from the file");
            return;
        }
    }
    fclose(pFile);
    int type = atoi(buffer);
    if (type > 0)
    {
        ALOGD("start activity by send intent to BatteryWarningReceiver, type = %d\n", type);
        sendBroadcastMessage(String16(ACTION), type); //将读到的 BatteryNotify 文件值通过广播发送给更上层
    }
}
 
int main()
{
    char *buffer = (char*) malloc(MAX_CHAR * sizeof(char));
    if (buffer == NULL) {
        ALOGD("malloc memory failed");
        return 0;
    }
    while(1) {
        readType(buffer);  //死循环,隔10秒读取一次
        sleep(10);
    }
    free(buffer);
        return 0;
}
 
#define ACTION "mediatek.intent.action.BATTERY_WARNING"
 
在readType中,将读到的BatteryNotify的值会通过sendBroadcastMessage 这个函数发送广播ACTION出去,并且是带BatteryNotify的值的参数。
 
值得注意的是,这里面实现一种在CPP里面 实现发送广播的方法,非常值得借鉴,有兴趣的同学可以好好研究一下
 
bool sendBroadcastMessage(String16 action, int value)
 
{
    ALOGD("sendBroadcastMessage(): Action: %s, Value: %d ", (char *)(action.string()), value);
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> am = sm->getService(String16("activity"));
    if (am != NULL) {
        Parcel data, reply;
        data.writeInterfaceToken(String16("android.app.IActivityManager"));
        data.writeStrongBinder(NULL);
        // Add for match AMS change on O
        data.writeInt32(1);
        // intent begin
        data.writeString16(action); // action
        data.writeInt32(0); // URI data type
        data.writeString16(NULL, 0); // type
        data.writeInt32(0x04000000); // flags: FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
        data.writeString16(NULL, 0); // package name
        data.writeString16(NULL, 0); // component name
        data.writeInt32(0); // source bound - size
        data.writeInt32(0); // categories - size
        data.writeInt32(0); // selector - size
        data.writeInt32(0); // clipData - size
        data.writeInt32(-2); // contentUserHint: -2 -> UserHandle.USER_CURRENT
        data.writeInt32(-1); // bundle extras length
        data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L'
        int oldPos = data.dataPosition();
        data.writeInt32(1);  // size
        // data.writeInt32(0); // VAL_STRING, need to remove because of analyze common intent
        data.writeString16(String16(TYPE));
        data.writeInt32(1); // VAL_INTEGER
        data.writeInt32(value);
        int newPos = data.dataPosition();
        data.setDataPosition(oldPos - 8);
        data.writeInt32(newPos - oldPos); // refill bundle extras length
        data.setDataPosition(newPos);
...
}
 
 
上层APP:
 
上层有个BatteryWarning 的APP在接收这个广播mediatek.intent.action.BATTERY_WARNING:
 
"vendor/mediatek/proprietary/packages/apps/BatteryWarning/AndroidManifest.xml"
 
<receiver android:name="com.mediatek.batterywarning.BatteryWarningReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
            <intent-filter>
                <action android:name="mediatek.intent.action.BATTERY_WARNING" />
            </intent-filter>
            <intent-filter>
                <action android:name="mediatek.intent.action.THERMAL_DIAG" />
            </intent-filter>
</receiver>
 
 
 
vendor/mediatek/proprietary/packages/apps/BatteryWarning/src/com/mediatek/batterywarning/BatteryWarningReceiver.java
中会对相应广播传来的值进行处理:
 
public void onReceive(Context context, Intent intent) {
        ......
        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
            Log.d(TAG, action + " clear battery_warning_settings shared preference");
            SharedPreferences.Editor editor = getSharedPreferences().edit();
            editor.clear();
            editor.apply();
        } else if (ACTION_BATTERY_WARNING.equals(action)) {
            Log.d(TAG, action + " start activity according to shared preference");
            int type = intent.getIntExtra("type", -1);
            Log.d(TAG, "type = " + type);
            type = (int) (Math.log(type) / Math.log(2));
 
            if (type < 0 || type >= BatteryWarningActivity.sWarningTitle.length) { 
                return;
            }
            boolean showDialogFlag = getSharedPreferences().getBoolean(
                    Integer.toString(type), true);
            Log.d(TAG, "type = " + type + "showDialogFlag = " + showDialogFlag);
            if (showDialogFlag) {
                Intent activityIntent = new Intent();
                activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                        | Intent.FLAG_ACTIVITY_CLEAR_TOP
                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                activityIntent.setClass(mContext, BatteryWarningActivity.class);
                activityIntent.putExtra(BatteryWarningActivity.KEY_TYPE, type);
                mContext.startActivity(activityIntent);
            }
        } else if (ACTION_THERMAL_WARNING.equals(action)) {
            int typeValue = intent.getIntExtra(ThermalWarningActivity.KEY_TYPE, -1);
            Log.d(TAG, "typeValue = " + typeValue);
            if (typeValue == 0 || typeValue == 1) {
                Intent thermalIntent = new Intent();
                thermalIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
......
}
 
 
摘出一段进行说明:
在条件if (ACTION_BATTERY_WARNING.equals(action)) 中
 
// 这个地方会对传来的值范围进行判断,如果没有超过BatteryWarningActivity 中的 数组sWarningTitle 的范围值,就可以直接返回了,否则才跳转到BatteryWarningActivity 进行处理。
if (type < 0 || type >= BatteryWarningActivity.sWarningTitle.length) { 
                return;
}
 
 
在BatteryWarningActivity  中,我们就可以看到,当充电charger的状态在如下范围时,就会根据不同的mType 值而显示不同的警告dialog给用户,同时会主动断开充电
 
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;  //这个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;
 
 
    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);
        if (mType >= CHARGER_OVER_VOLTAGE_TYPE  && mType <= BATTERY_LOW_TEMPERATURE_TYPE) {
            showWarningDialog(mType);
            registerReceiver(mReceiver, new IntentFilter(
                    Intent.ACTION_POWER_DISCONNECTED));
        } else {
            finish();
        }
    }
文章来自个人专栏
Android系统开发
17 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0