针对以上流程图做以下几点说明:
1.
电池的充电状态、温度、电压以及各种状态信息是由!BatteryService来显示的,但真正的数据来源是由内核提供的,它运行在system_process进程当中。
2.
!BatteryService通过JNI读取内核数据信息。它通过JNI注册的不仅有函数,而且也有变量,应该说他们是通用的,JNI的com_android_server_BatteryService.cpp中操作的其实也是BatteryService.java中声明的变量。
在BatteryService.java中的变量声明:
private boolean mAcOnline;
private boolean mUsbOnline;
private int mBatteryStatus;
private int mBatteryHealth;
private boolean mBatteryPresent;
private int mBatteryLevel;
private int mBatteryVoltage;
private int mBatteryTemperature;
private String mBatteryTechnology;
在JNI com_android_server_BatteryService.cpp
struct FieldIds {
jfieldID mAcOnline;
jfieldID mUsbOnline;
jfieldID mBatteryStatus;
jfieldID mBatteryHealth;
jfieldID mBatteryPresent;
jfieldID mBatteryLevel;
jfieldID mBatteryVoltage;
jfieldID mBatteryTemperature;
jfieldID mBatteryTechnology;
};
gFieldIds.mAcOnline = env->GetFieldID(clazz, “mAcOnline”, “Z”);
gFieldIds.mUsbOnline = env->GetFieldID(clazz, “mUsbOnline”, “Z”);
gFieldIds.mBatteryStatus = env->GetFieldID(clazz, “mBatteryStatus”, “I”);
gFieldIds.mBatteryHealth = env->GetFieldID(clazz, “mBatteryHealth”, “I”);
gFieldIds.mBatteryPresent = env->GetFieldID(clazz, “mBatteryPresent”, “Z”);
gFieldIds.mBatteryLevel = env->GetFieldID(clazz, “mBatteryLevel”, “I”);
gFieldIds.mBatteryTechnology = env->GetFieldID(clazz, “mBatteryTechnology”, “Ljava/lang/String;”);
gFieldIds.mBatteryVoltage = env->GetFieldID(clazz, “mBatteryVoltage”, “I”);
gFieldIds.mBatteryTemperature = env->GetFieldID(clazz, “mBatteryTemperature”, “I”);
3.
以上数据信息是通过sys文件系统的属性文件来获得的,对应每一个数据值都有一个指定的路径;
struct PowerSupplyPaths {
char* acOnlinePath;
char* usbOnlinePath;
char* batteryStatusPath;
char* batteryHealthPath;
char* batteryPresentPath;
char* batteryCapacityPath;
char* batteryVoltagePath;
char* batteryTemperaturePath;
char* batteryTechnologyPath;
};
那么batterystatusPath对应的路径:”/sys/class/power_supply/battery/status”;
batteryHealthPath 对应的路径:”/sys/class/power_supply/battery/health”
…..
4.
电池的信息会随着时间不停变化,这在上层需要考虑实时的更新电池的数据信息。所以在!BatteryService启动的时候,通过UEventObserver启动了一个UEventThread线程。
每一个Process最多只能一个UEventThread,即使这个process有多个UEventObserver的实例。当在一个Process中,第一次调用startObserving()方法后,这个UEventThread就启动了,
而这个线程一旦启动之后,将不会停止。从图中我们看到,线程启动后,它会监视是否有事件上报,一旦kernel有uevent报上来之后,线程就会触发onUEvent,而onUEvent调用的是update函数,再次调用native_update,通过JNI访问sysfs系统下的Power_supply的属性信息,我们知道sysfs提供了内核与用户空间信息交流的接口,而内核空间的属性操作show函数会及时的把最新的数据信息编码后放到指定的buffer,以供用户层访问。在内核空间对此进行处理的函数为:power_supply_show_property ;
define POWER_SUPPLY_ATTR(_name) \
{ \
.attr = { .name = #_name, .mode = 0444 }, \
.show = power_supply_show_property, \
.store = NULL, \
}
static ssize_t power_supply_show_property(struct device *dev,
struct device_attribute *attr,
char *buf) {
static char *status_text[] = {
“Unknown”, “Charging”, “Discharging”, “Not charging”, “Full”
};
struct power_supply *psy = dev_get_drvdata(dev);
const ptrdiff_t off = attr - power_supply_attrs;
union power_supply_propval value;
ret = psy->get_property(psy, off, &value);
if (off == POWER_SUPPLY_PROP_STATUS)
return sprintf(buf, “%s\n”, status_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE)
return sprintf(buf, “%s\n”, charge_type[value.intval]);
else if (off == POWER_SUPPLY_PROP_HEALTH)
return sprintf(buf, “%s\n”, health_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
return sprintf(buf, “%s\n”, technology_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
return sprintf(buf, “%s\n”, capacity_level_text[value.intval]);
else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
return sprintf(buf, “%s\n”, value.strval);
return sprintf(buf, “%d\n”, value.intval);
}
当用户层想要读取文件信息时,会调用属性信息的show函数,在这里也就是power_supply_show_property()函数将变化的信息提供给应用层。
何时去更新数据
在UEventThread线程中,在onUevent函数之前会一直调用一个next_event函数,这个函数会一直等待接受来自内核的通过netlink socket上报的battery信息。一旦内核空间向用户空间广播uevent,它就会接受,接受后会去update数据信息,以上已经讲过update的过程。
内核是如何发送uevent事件的?
在battery驱动里面,需要实时的更新数据信息,并且调用power_supply_core.c的一个工作队列power_supply_changed_work,在这个里面会发送一个uevent事件,它会将ACTION=CHANGE以及SUBSYSTEM=POWER_SUPPLY 的信息通过socket的netlink_broadcast广播到用户空间,当observer监测到后会作进一步的处理。
以上只是对框图中的信息进行一下简要说明,关于更多的细节信息不在这里一一陈述,一切都在源码里。