由于项目需要,我们的android手机及上网本都需要做一个电池的电量检测与电池管理,所以,在开始动手做这一块之前,先整理一下电源管理方面的资料。
首先,看看一下电源芯片驱动在内核的位置:kernel/driver/power/ 路径下,看了一下samsung I9000的kernel用的是max17040+max8989,max1740是I2C接口,需要烧写一些寄存器和通过读写寄存器来获取电池的信息,max8998则是通过控制它的GPIO口来工作。两者相互协调完成底层电源管理的驱动,通过接口与android的中间层交互。里注册了充电器(ac)、 usb和电池(battery)三个power_supply。各个power_supply提供的属性和上述文件是对应的,从这些文件中可以读到充电器 (ac)、usb和电池(battery)三个power_supply的相应状态。
BatteryService作为电池及充电相关的服务
监听UEvent,读取sysfs里中的状态。
实现了一个UEvent的观察者。uevent是Linux内核用来向用户空间主动上报事件的机制,对于JAVA程序来说,只实现UEventObserver的虚函数onUEvent,然后注册即可。
private UEventObserver mUEventObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
update();
}
};
这里只关注power_supply的事件:
mUEventObserver.startObserving("SUBSYSTEM=power_supply");
当有power_supply相关的事件上报时,就会调用update函数。
update先调用native_update从sysfs中读取相关状态(com_android_server_BatteryService.cpp)
Linux驱动提供了下列文件,供应用程序获取电源相关状态:
#define AC_ONLINE_PATH "/sys/class/power_supply/ac/online"
#define USB_ONLINE_PATH "/sys/class/power_supply/usb/online"
#define BATTERY_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"
#define BATTERY_TEMPERATURE_PATH "/sys/class/power_supply/battery/batt_temp"
#define BATTERY_TECHNOLOGY_PATH "/sys/class/power_supply/battery/technology"
update然后根据读到的状态更新BatteryService的成员变量,并广播一个Intent来通知其它关注电源状态的组件。
BatteryService.java
private final void sendIntent() {
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(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(BatteryManager.EXTRA_STATUS, mBatteryStatus);
intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryHealth);
intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryPresent);
intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryLevel);
intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryVoltage);
intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryTemperature);
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
if (false) {
Log.d(TAG, "updateBattery level:" + mBatteryLevel +
" scale:" + BATTERY_SCALE + " status:" + mBatteryStatus +
" health:" + mBatteryHealth + " present:" + mBatteryPresent +
" voltage: " + mBatteryVoltage +
" temperature: " + mBatteryTemperature +
" technology: " + mBatteryTechnology +
" AC powered:" + mAcOnline + " USB powered:" + mUsbOnline +
" icon:" + icon );
}
ActivityManagerNative.broadcastStickyIntent(intent, null);
}
kernel将所得到的电池信息放到/sys/class/power_supply/Battery中,通过frameworks/base/services/jni文件夹下的com_android_server_BatteryService.cpp进行读取。
监听UEvent,读取sysfs里中的状态。
实现了一个UEvent的观察者。uevent是Linux内核用来向用户空间主动上报事件的机制,对于JAVA程序来说,只实现UEventObserver的虚函数onUEvent,然后注册即可。
下面我们看一下总路径的定义:
#define POWER_SUPPLY_PATH "/sys/class/power_supply"注意是与上面kernel路径相对应的。
在int register_android_server_BatteryService(JNIEnv* env)中对每个特定路径赋值
如给定batteryCapacityPath的路径:
snprintf(path, sizeof(path), "%s/%s/capacity", POWER_SUPPLY_PATH, name);
if (access(path, R_OK) == 0)
gPaths.batteryCapacityPath = strdup(path);
最后在static void android_server_BatteryService_update(JNIEnv* env, jobject obj)中读取路径文件中的信息
setIntField(env, obj, gPaths.batteryCapacityPath, gFieldIds.mBatteryLevel);
关于ACTION_BATTERY_CHANGED的地方有好几个:
BatteryService.java
StatusBarPolicy.java 用来更新状态栏上的充电图标。
else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
updateBattery(intent);
}
NotificationManagerService.java用来更新充电状态(LED)
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
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();
}
} else if (action.equals(Intent.ACTION_UMS_CONNECTED)) {
mUsbConnected = true;
updateAdbNotification();
} else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
mUsbConnected = false;
updateAdbNotification();
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Uri uri = intent.getData();
if (uri == null) {
return;
}
String pkgName = uri.getSchemeSpecificPart();
if (pkgName == null) {
return;
}
cancelAllNotificationsInt(pkgName, 0, 0);
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
mScreenOn = true;
updateNotificationPulse();
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
mScreenOn = false;
updateNotificationPulse();
}
}
};
PowerManagerService.java这里主要是做两件事件,先是检查是否在充电时不允许睡眠,并采用相应的行动,其次是触发一个用户行为(会影响下一次睡眠的时间)。
private final class BatteryReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLocks) {
boolean wasPowered = mIsPowered;
mIsPowered = mBatteryService.isPowered();
if (mIsPowered != wasPowered) {
// update mStayOnWhilePluggedIn wake lock
updateWakeLockLocked();
// treat plugging and unplugging the devices as a user activity.
// users find it disconcerting when they unplug the device
// and it shuts off right away.
// temporarily set mUserActivityAllowed to true so this will work
// even when the keyguard is on.
synchronized (mLocks) {
forceUserActivityLocked();
}
}
}
}
WifiService.java 根据电源状态来决定是否需要定时唤醒
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
/*
* Set a timer to put Wi-Fi to sleep, but only if the screen is off
* AND we are transitioning from a state in which the device was supposed
* to stay awake to a state in which it is not supposed to stay awake.
* If "stay awake" state is not changing, we do nothing, to avoid resetting
* the already-set timer.
*/
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;
}
在KeyguardUpdateMonitor.java文件中:KeyguardUpdateMonitor 这里主要是用来更新锁屏界面下的电池状态。还有低电警告和关机也是在这里做的。
/**
* Handle {@link #MSG_BATTERY_UPDATE}
*/
private void handleBatteryUpdate(int pluggedInStatus, 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);
}
}
}
在我们的开发板上AC是由美信的max8677控制,而DC是由美信的DS2784控制。