本文将从底层到上层介绍android系统中电量显示这一块,电池检测采用的是ADC采样,不是使用市场上封装好的电量计芯片;
1.驱动层
linux内核中提供power_supply_core.c电池管理核心,我们驱动程序主要完成核心给我们提供的接口:battery.get_property,在这个程序中,我们主要获得电池的电压、是否正常、容量、充放电状态等;通过这些值来改变文件系统中/sys/class/power_supply/目录下的文件值,下面主要介绍以上几个参数的获取方法:
1)电池充放电状态gBatChargeStatus:
当DC插入时,会产生一个中断,在这个中断处理程序中,我们主要完成给全局变量gBatChargeStatus赋值,如果DC插入,则gBatChargeStatus=1,否则为0,同事,在中断处理程序中,调用send_wakeup_key()唤醒触摸屏;
2)电池状态
电池状态主要有:充电、放电、电量满三种状态;当DC没插入时,电池处于放电状态,当DC插入时,如果电池电池芯片没有报电量满切电量达到100%则为充电状态,否则为充电状态
3)电压值
电压值采用ADC模块对电池电压的采样,获得电池的电压值,因为系统在使用过程中可能存在跳变,所以,对电池电压的获得最好采取多次采样取平均值的方法。在通过adc读电压值的时候,可以采取同步读或者异步读,区别是异步读在第二次执行这个程序的时候获取第一次执行的结果,这样做的好处就是效率高点;
4)电量值
电量值是用户实际看的的值,是用户最关心的,也是最重要的值,在处理电量值的时候注意,充电状态下电量值不能降低,放电状态下电量值不能升高,而且电量值不能大弧度跨越,基本是按照1%这样的变化;特别要注意的是在低电量和电量接近100%的时候的处理。
电量值主要是根据测得的电压值获取,在开始写驱动前,得获得两组数据,分别是充电和没充电下没10%对应的电压值,切两组数据需要对应上,不如在电量为50%的时候,如果没接DC测得的电压值可能为3700而接着DC测得的电压值可能为3800,所以每一个百分比最好都能对应上。
获取这些值后,驱动里每隔50ms就会调用一次比较函数,确认以上的几个值是否改变,如果改变了,则调用power_supply_changed()->kobject->uevent()上报uevent事件。同事电池管理核心就会调用接口battery.get_property更新/sys/class/power_supply目录下的值拱上层调用。
2.获取电池状态值
BatteryService作为电池及充电相关的服务,它的实现非常简单:监听UEvent,读取sysfs里中的状态。实现了一个UEvent的观察者,uevent是Linux内核用来向用户空间主动上报事件的机制,对于JAVA程序来说,只实现UEventObserver的虚函数onUEvent,然后注册即可。
private UEventObserver mUEventObserver = newUEventObserver() {
@Override
public voidonUEvent(UEventObserver.UEvent event) {
String strOnline =event.get("POWER_SUPPLY_ONLINE"); //电源连接状态
String strBatteryState =event.get("POWER_SUPPLY_STATUS");
//电池状态,"Discharging","Charging","Notcharging","Full","Unknown"
String strBatteryLevel =event.get("POWER_SUPPLY_CAPACITY");//电池容量
...
}
}
1).BatteryService通过JNI(com_android_server_BatteryService.cpp)读取数据。BatteryService通过JNI注册的不仅有函数,还有变量。 如下:
//##############在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;
2).在BatteryService.java中声明的变量,在com_android_server_BatteryService.cpp中共用,即在com_android_server_BatteryService.cpp中其实操作的也是BatteryService.java中声明的变量
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).上面这些变量的值,对应是从下面的文件中读取的,一个文件存储一个数值。
#define AC_ONLINE_PATH"/sys/class/power_supply/ac/online" AC电源连接状态
#defineUSB_ONLINE_PATH "/sys/class/power_supply/usb/online"USB电源连接状
#defineBATTERY_STATUS_PATH "/sys/class/power_supply/battery/status"充电状
#define BATTERY_HEALTH_PATH"/sys/class/power_supply/battery/health"电池状
#define BATTERY_PRESENT_PATH"/sys/class/power_supply/battery/present"状态
#define BATTERY_CAPACITY_PATH"/sys/class/power_supply/battery/capacity" 电池容量
#define BATTERY_VOLTAGE_PATH"/sys/class/power_supply/battery/batt_vol" 电池电压
#defineBATTERY_TEMPERATURE_PATH"/sys/class/power_supply/battery/batt_temp" 电池温度
#defineBATTERY_TECHNOLOGY_PATH "/sys/class/power_supply/battery/technology" 电池技术
update然后根据读到的状态更新BatteryService的成员变量,并广播一个Intent来通知其它关注电源状态的组件。
private final void sendIntent() {
// Pack up the values and broadcast them to everyone
Intent intent = newIntent(Intent.ACTION_BATTERY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
try {
mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE,mBatteryLevel);
} catch (RemoteException e) {
// Should never happen.
}
int icon = getIcon(mBatteryLevel);
intent.putExtra("status",mBatteryStatus);
intent.putExtra("health",mBatteryHealth);
intent.putExtra("present",mBatteryPresent);
intent.putExtra("level",mBatteryLevel);
...........
ActivityManagerNative.broadcastStickyIntent(intent,null);
}
3.根据电池状态值改变UI
关注ACTION_BATTERY_CHANGED的地方有好几个:
1) KeyguardUpdateMonitor 这里主要是用来更新锁屏界面下的电池状态和低电关机
private void handleBatteryUpdate(intpluggedInStatus, int batteryLevel) {
if (DEBUG) Log.d(TAG,"handleBatteryUpdate");
final boolean pluggedIn =isPluggedIn(pluggedInStatus);
if(isBatteryUpdateInteresting(pluggedIn, batteryLevel)) {
mBatteryLevel = batteryLevel;
mDevicePluggedIn = pluggedIn;
for (int i = 0; i < mInfoCallbacks.size();i++) {
mInfoCallbacks.get(i).onRefreshBatteryInfo(
shouldShowBatteryInfo(), pluggedIn, batteryLevel);
}
}
// shut down gracefully if our batteryis critically low and we are not powered
if (batteryLevel == 0 &&
pluggedInStatus !=BATTERY_STATUS_CHARGING &&
pluggedInStatus !=BATTERY_STATUS_UNKNOWN) {
ShutdownThread.shutdownAfterDisablingRadio(mContext, false);
}
}
2 )NotificationManagerService 用来更新充电状态(LED)
if(action.equals(Intent.ACTION_BATTERY_CHANGED)) {
boolean batteryCharging =(intent.getIntExtra("plugged", 0) != 0);
int level =intent.getIntExtra("level", -1);
boolean batteryLow = (level >= 0&& level <= Power.LOW_BATTERY_THRESHOLD);
int status =intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
boolean batteryFull = (status ==BatteryManager.BATTERY_STATUS_FULL || level >= 90);
if (batteryCharging !=mBatteryCharging ||
batteryLow != mBatteryLow||
batteryFull !=mBatteryFull) {
mBatteryCharging =batteryCharging;
mBatteryLow = batteryLow;
mBatteryFull = batteryFull;
updateLights();
}
}
3 )PowerManagerService 这里主要是做两件事件,先是检查是否在充电时不允许睡眠,并采用相应的行动,其次是触发一个用户行为(会影响下一次睡眠的时间)。
private final class BatteryReceiver extendsBroadcastReceiver {
@Override
public void onReceive(Context context,Intent intent) {
synchronized (mLocks) {
boolean wasPowered =mIsPowered;
mIsPowered = mBatteryService.isPowered();
if (mIsPowered != wasPowered) {
updateWakeLockLocked();.
synchronized (mLocks) {
booleansavedActivityAllowed = mUserActivityAllowed;
mUserActivityAllowed =true;
userActivity(SystemClock.uptimeMillis(), false);
mUserActivityAllowed =savedActivityAllowed;
}
}
}
}
}
4 )LocationManagerService 这里似乎没有什么用处,我没找到mCollector赋值的地方。
if(action.equals(Intent.ACTION_BATTERY_CHANGED)) {
log("PowerStateBroadcastReceiver: Battery changed");
synchronized(mLocationListeners) {
int scale =intent.getIntExtra(BATTERY_EXTRA_SCALE, 100);
int level =intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0);
boolean plugged =intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0;
if (mCollector != null) {
mCollector.updateBatteryState(scale, level, plugged);
}
}
}
5 )WifiService 根据电源状态来决定是否需要定时唤醒
if(action.equals(Intent.ACTION_BATTERY_CHANGED)) {
int pluggedType =intent.getIntExtra("plugged", 0);
if (mScreenOff &&shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&!shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
long triggerTime =System.currentTimeMillis() + idleMillis;
mAlARMManager.set(AlARMManager.RTC_WAKEUP,triggerTime, mIdleIntent);
mPluggedType = pluggedType;
return;
}
mPluggedType = pluggedType;
}
6 )StatusBarPolicy用来更新状态栏上的充电图标。
if (action.equals(Intent.ACTION_BATTERY_CHANGED)){
updateBattery(intent);
}
7)发送低电警报
首先需要判断是否需要发送低电量信息:Intent.ACTION_BATTERY_LOW
final boolean sendBatteryLow = !plugged && mBatteryStatus !=BatteryManager.BATTERY_STATUS_UNKNOWN
&&mBatteryLevel <= mLowBatteryWarningLevel
&&(oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
可以看到发送低电量信息会有两个条件,
1)当前不在充电状态,上次update时处于充电状态,并且电池电量小于等于mLowBatteryWarningLevel(低电量警告值)
2)当前不在充电状态,电池电量小于等于mLowBatteryWarningLevel(低电量警告值),并且上次update时,电量大于mLowBatteryWarningLevel(低电量警告值)
下面代码是具体发送
if(sendBatteryLow) {
mSentLowBatteryBroadcast = true;
statusIntent.setAction(Intent.ACTION_BATTERY_LOW);
mContext.sendBroadcast(statusIntent);
}else if(mSentLowBatteryBroadcast && mLastBatteryLevel >=mLowBatteryCloseWarningLevel) {
mSentLowBatteryBroadcast = false;
statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);
mContext.sendBroadcast(statusIntent);
}