1. 介绍.... 4

2. 电池管理.... 4

2.1. FrameWork.. 5

2.1.1. 监听... 5

2.1.1.1. UEventObserver的实现... 6

2.1.1.2. uevent 8

2.1.2. 状态读取... 9

2.1.3. 更新显示... 9

2.2. 驱动... 11

3. 用电统计.... 20


1. 介绍

首先区分一下电源管理, 电池管理和用电统计三个概念。

l 电源管理:前一篇文章讲的是电源管理, 电源管理的目的是节电, 让设备在空闲的时间进入睡眠状态。

l 电池管理: 电池管理的目的是对电池电量和状态的管理, 比如电量变化时和进入充电状态时更新任务栏上的进度条, 当系统低电量时通知用户, 电量低到一定程度时自动关机等。

l 用电统计:用电统计的目的则是统计系统中一些模块, 服务和应用程序的耗电情况, 并反馈给用户。

这篇文章是关于电池管理和用电统计的。

2. 电池管理

Android电池管理的功能其实很简单:

l 监测电池电量的变化并更新显示界面

l 监听进入充电状态和退出充电状态消息并更新界面

2.1. FrameWork



2.1.1. 监听

在BatteryService定义了UEventObserver, uevent是Linux内核用来向用户空间主动上报事件的机制,关于uevent请参见2.1.1.2节, JAVA中的UEventObserver就用来监听uevent的。

private UEventObserver mUEventObserver = new UEventObserver() {

@Override

public void onUEvent(UEventObserver.UEvent event) {

update();

}

};

然后调用mUEventObserver.startObserving("SUBSYSTEM=power_supply")只监听属性SUBSYSTEM 为“power_supply”的消息。 也就是说当系统进入充电状态和退出充电状态时, 电量变化时就会调用到这个onUEvent函数来处理变化。

2.1.1.1. UEventObserver的实现

UEventObserver的实现分为三层,

UEventObserver.java->android_os_UEventObserver.cpp->hardware\libhardware_legacy\uevent\uevent.c

l API层是com.android.os.UEventObserver

l JNI层是android_os_UEventObserver.cpp, 很浅

l Hal层是hardware\libhardware_legacy\uevent\uevent.c, 直接和内核通信

当第一次调用mUEventObserver.startObserving会启动一个UEventThread, next_event会返回监听到的uevent事件, 然后再判断是不是我们所关心的, 如果是就会回调我们注册的onUEvent函数。如下:

private static class UEventThread extends Thread {

public void run() {

native_setup();

byte[] buffer = new byte[1024];

int len;

while (true) {

len = next_event(buffer);

if (len > 0) {

String bufferStr = new String(buffer, 0, len); // easier to search a String

synchronized (mObservers) {

for (int i = 0; i < mObservers.size(); i += 2) {

if (bufferStr.indexOf((String)mObservers.get(i)) != -1) {

((UEventObserver)mObservers.get(i+1))

.onUEvent(new UEvent(bufferStr));

}

}

}

}

}

}

native_setup()是jni 如下:

static void

android_os_UEventObserver_native_setup(JNIEnv *env, jclass clazz)

{

if (!uevent_init()) {

jniThrowException(env, "java/lang/RuntimeException",

"Unable to open socket for UEventObserver");

}

}

uevent_init()是hal,创建用于监听内核uevent事件的socket, 如下:

int uevent_init()

{

struct sockaddr_nl addr;

int sz = 64*1024;

int s;

memset(&addr, 0, sizeof(addr));

addr.nl_family = AF_NETLINK;

addr.nl_pid = getpid();

addr.nl_groups = 0xffffffff;

s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

if(s < 0)

return 0;

setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));

if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {

close(s);

return 0;

}

fd = s;

return (fd > 0);

}

next_event是jni,如下:

static int

android_os_UEventObserver_next_event(JNIEnv *env, jclass clazz, jbyteArray jbuffer)

{

int buf_sz = env->GetArrayLength(jbuffer);

char *buffer = (char*)env->GetByteArrayElements(jbuffer, NULL);

int length = uevent_next_event(buffer, buf_sz - 1);

env->ReleaseByteArrayElements(jbuffer, (jbyte*)buffer, 0);

return length;

}

uevent_next_event是hal, 等待uevent事件, 如下:

int uevent_next_event(char* buffer, int buffer_length)

{

while (1) {

struct pollfd fds;

int nr;

fds.fd = fd;

fds.events = POLLIN;

fds.revents = 0;

nr = poll(&fds, 1, -1);

if(nr > 0 && fds.revents == POLLIN) {

int count = recv(fd, buffer, buffer_length, 0);

if (count > 0) {

return count;

}

}

}

// won't get here

return 0;

}

至于netlink socket的原理不做过多解释, 我们现在更关心的是我们的电池驱动如何发送出uevent。

2.1.1.2. uevent

关于linux设备模型请参考《LDD3》的第14章。

如果对节点的动态动态创建过程有了解的话, 那么对uevent一定不会陌生。 当发现设备调用device_add函数时, device_add便会调用kobject_uevent函数发出一个uevent。 应用层的udev(当然android是没有udev的, 动态节点创建时由init进程做的)就会监听这个uevent来创建设备节点。 当然当设备移除时也会调用device_remove也会发出一个uevent,udev收到这个消息就删除设备节点。

每个uevent都包含一些属性, 所有uevent都有如下属性, 下面只列出几个重要的:

l ACTION: add, remove或change等。

l SUBSYSTEM:“devices”,“input”,“power_supply”等。

l DEVPATH:发出uevent的kobject的sys对应路径。

当然不同的子系统中发出的uevent又会可能有相应的属性发出, 还记得每个kset对应一个子系统吧, 比如/sys/devices, /sys/block, /sys/class/input, /sys/class/power_supply, 当添加一个kset时会为这个kset注册一个kset_uevent_ops。 当一个kobject发出一个uevent时, 就会去寻找这个kobject所属的kset, 然后调用这个kset里面的kset_uevent_ops来添加子系统相关的一些属性。比如power_supply子系统里面发出的uevent都会有POWER_SUPPLY_NAME这个属性, 值可以是“ac”, “usb”或者“battery”。

当然驱动也是可以为uevent添加属性的, 调用kobject_uevent_env函数发uevent即可,kobject_uevent_env声明如下:

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,char *envp_ext[])

发出去uevent, 应用层就可以通过netlink socket的方式来接收。

2.1.2. 状态读取

update()函数里会调用nativeUpdate()通过进入c++层, nativeUpdate在com_android_server_BatteryService.cpp中。 Android的Linux 内核中的电池驱动会提供如下sysfs接口给

FrameWork:

/sys/class/power_supply/ac/online
/sys/class/power_supply/ac/type
 
/sys/class/power_supply/usb/online
/sys/class/power_supply/usb/type
 
/sys/class/power_supply/battery/status
/sys/class/power_supply/battery/health
/sys/class/power_supply/battery/present
/sys/class/power_supply/battery/capacity
/sys/class/power_supply/battery/batt_vol
/sys/class/power_supply/battery/batt_temp
/sys/class/power_supply/battery/technology
/sys/class/power_supply/battery/type

当监听到power_supply变化的消息后, nativeUpdate函数就会重新读取以上sysfs文件获得当前状态。这里应该注意的是读到的capacity为0-100之间的数值, 100代表满电。

2.1.3. 更新显示

当发现有变化时就会发出BroadCast, 关心电池变化事件的模块就会定义BroadCastReceiver来响应, 比如状态栏。有如下intent会在相应情况下通过BroadCast发出:

l Intent.ACTION_BATTERY_CHANGED:不仅是电量变换, 包括connected和disconnected等所有变化, 都会发出这个broadcast。

l Intent.ACTION_POWER_CONNECTED

l Intent.ACTION_POWER_DISCONNECTED

l Intent.ACTION_BATTERY_LOW

l Intent.ACTION_BATTERY_OKAY

通过在Eclipse中进行search, 找到关心Intent.ACTION_BATTERY_CHANGED的地方有:

l NotificationManagerService:更新电源灯的更亮度和让通知灯闪几下。

l PowerManagerService: 代码如下

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.

// to avoid turning on the screen when unplugging, we only trigger

// user activity when screen was already on.

// temporarily set mUserActivityAllowed to true so this will work

// even when the keyguard is on.

synchronized (mLocks) {

if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0) {

forceUserActivityLocked();

}

}

}

}

}

}

l UiModeManagerService: 会申请一个FULL_WAKE_LOCK, keep screen on when charging and in car mode。

l WifiService:注释如下

l com.android.server.Connectivity.Tethering: 代码如下,还不清楚什么用。

if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {

mUsbConnected = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)

== BatteryManager.BATTERY_PLUGGED_USB);

Tethering.this.updateUsbStatus();

}

l packages.SystemUI.src.com.android.systemui.statusbar: 更新状态栏电池量的显示。

l KeyguardUpdateMonitor:代码如下, 还不清楚作用。

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);

}

}

}

关心Intent.ACTION_POWER_CONNECTED的地方有:

l packages.SystemUI.src.com.android.systemui.statusbar: 更新状态栏电池量的显示。

关心Intent.ACTION_POWER_DISCONNECTED的地方有:

关心Intent.ACTION_BATTERY_LOW的地方有:

l packages.SystemUI.src.com.android.systemui.statusbar: 更新状态栏电池量的显示, 弹出dialog并发出声音提示用户低电量。

关心Intent.ACTION_BATTERY_OKAY的地方有:

l packages.SystemUI.src.com.android.systemui.statusbar: 更新状态栏电池量的显示。

2.2 驱动

关于驱动实现遵循linux kernel power-supply子系统规范即可, 可参考示例代码较多。

3. 用电统计

上图是显示Android系统用电统计结果的界面, 这个界面在Settings->About Phone->Battery use里。

用电统计是对Android电源管理系统缺点的弥补, Android电源管理系统的缺点是只有所有程序设计良好电源管理系统才能工作的出色, 一坏就会坏一锅粥。 当你发现你的系统莫名其妙很快没电了时, 你就会可以通过用电统计找到原因, 以便禁掉某些功能或卸载某些程序。

com.android.server.BatteryStatsService负责统计, 其它模块比如WakeLock和PowerManagerService会向BatteryStatsService喂数据。BatteryStatsService在ActivityManagerService中创建, 如下所示:

mBatteryStatsService = new BatteryStatsService(new File(systemDir, "batterystats.bin").toString());

可见统计的数据保存在batterystats.bin里面。

Settings程序也是通过BatteryStatsService获得统计数据, 然后把统计数据传入BatteryStatsImpl来分析。

这部分细节比较复杂就先点到为止, 看以后是不是有深入的需要。

4.实际问题

当由充电状态转到电池供电时,读到电池电压会有0。2v左右跳降。当由电池供电转到充电状态时,读到电池电压会有0。2v左右跳升。这就需要驱动或hal层维护两张电压电量对应表。


更多相关文章

  1. android perimission 和 user-perimission
  2. Android(安卓)SwitchButton(滑动开关)
  3. Android(安卓)Jetpack架构组件(七)之WorkManager
  4. Android(安卓)USB状态监控(解决scheme="file")
  5. 第三方程序调用Android(安卓)Telephony 接口机制
  6. Android(安卓)中 shape 图形的使用
  7. 解决Android(安卓)Toobar与状态栏重叠
  8. Android(安卓)进度条 ProgressBar (模拟图片加载过程)
  9. android 键盘属性设置总结

随机推荐

  1. android sqlite3查询
  2. TSLib ported to Android(安卓)for touch
  3. android sqlite 增删查 demo
  4. Android所有系统资源图标android.R.drawa
  5. android 中判断有无网络连接
  6. 启动android默认浏览器
  7. Android客户端GPS定位源码
  8. Android(安卓)7.0,8.0拍照loadXmlMetaData
  9. mtk android sd卡调试
  10. 关于android菜单