1. 介绍
这是一篇关于Android电源管理的文章, 适合应用程序开发人员和驱动开发人员和FrameWork开发人员阅读。
2. 前言
Android系统出于节电的需要, 一般应用在用户一段时间无操作的情况下屏幕变暗, 然后进后休眠状态, 此时cpu处于挂起状态, 屏幕处于关闭状态。 用户只能在”设置->声音和显示”中设置所有应用默认的屏幕亮度和系统无操作多久进入睡眠的时间。 当然这种管理模式并不适合于所有的应用, 比如当看电影的时候总不希望系统隔一段时间就进入睡眠吧。所以Android系统为这种有特殊需求的应用提供了可以不遵守规矩的Api, 通过使用这种Api可以打破默认情况下的电源管理模式。
3. 应用程序开发相关 3.1. Api的使用
只需要熟悉android.os.PowerManager和android.os.PowerManager.WakeLock这两个类的Api就足够了, 当需要用到这些Api时, 需要在Manifest.xml添加如下权限: <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.DEVICE_POWER" /> 很重要的一个概念就是WakeLock, 系统中定义了六种WakeLock, 常用的有4个:
CPU | Screen | Keyboard Light | |
PARTIAL_WAKE_LOCK | on | off | off |
SCREEN_DIM_WAKE_LOCK | on | dim | off |
SCREEN_BRIGHT_WAKE_LOCK | on | on | off |
FULL_WAKE_LOCK | on | on | on |
PowerManager mPm;
WakeLock mWakeLock;
mWakeLock = mPm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "WhatEver");
mWakeLock.acquire();
//cpu on, screen dim, keyboard light off
//...
mWakeLock.release();
如上面代码所示, 如果系统在获取锁后一段时间无操作就会进入SCREEN_DIM_WAKE_LOCK锁所对应的状态。
另外还有两个比较特殊的锁:
l ACQUIRE_CAUSES_WAKEUP:一旦有请求锁时强制打开Screen和keyboard light
l ON_AFTER_RELEASE:当lock被释放后,通过reset user activity timer使屏幕多亮一会儿
关于api更加详细的内容请参考官方文档。
3.24. 注意事项
l 使用WakeLock会增加系统功耗,所以原则上是只有必要的时候才用
l 所有的锁必须成对的使用, 如申请了PARTIAL_WAKE_LOCK, 而没有及时释放,那系统就永远进不了Sleep模式,这会严重影响手持设备的待机时间。
4. 驱动程序开发相关
值得注意的是,写Android的linux驱动时也要记得系统随时都可能suspend。有些驱动需要关心early suspend和later resume, 比如early suspend时framebuffer驱动的dma应该可以休息了。有些驱动则需要在某个过程中系统不能睡眠,比如usb gadget driver向pc传文件的过程中系统就不能睡眠,睡了就传不完了, 这就需要在这个过程前后获得而后释放锁。 有些驱动向某个设备发送命令过程中如果发生suspend, 等到唤醒的时候再接着发可能外设早已timeout,这时也需要锁。
驱动相关的wakelock接口如下, 在inclulde/linux/wakelock.h中定义:
void wake_lock_init(struct wake_lock *lock, int type, const char *name);
void wake_lock_destroy(struct wake_lock *lock);
void wake_lock(struct wake_lock *lock);
void wake_lock_timeout(struct wake_lock *lock, long timeout);
void wake_unlock(struct wake_lock *lock);
int wake_lock_active(struct wake_lock *lock);
long has_wake_lock(int type);
示例代码如下:
#include <linux/earlysuspend.h>
#include <linux/module.h>
struct wake_lock my_wake_lock;
static int __init my_init(void)
{
wake_lock_init(&my_wake_lock, WAKE_LOCK_SUSPEND, "mylock");
wake_lock(&my_wake_lock);
//we do not want to suspend here!
wake_unlock(&my_wake_lock);
return 0;
}
static void __exit my_exit(void)
{
wake_lock_destroy(&my_wake_lock)
}
early suspend和later resume是Android对linux内核的扩展机制, 当系统持有wakelock时,如果一段时间无用户操作, 就会进入early suspend状态, 所有驱动注册的early suspend回调函数会被调用。当用户开始操作设备时,所有驱动注册的later resume函数会被调用。 当系统中无wakelock时, 驱动注册的early suspend会先于suspend函数调用, 唤醒时later resume会再resume函数后调用。 early suspend机制给系统提供了一种折中的省电模式,让系统在不能睡眠的情况下各个驱动模块也能有机会调整工作模式。 驱动注册early suspend和later resume回调的代码如下:
module_init(my_init);
module_exit(my_exit);
#include <linux/earlysuspend.h>
#include <linux/module.h>
static my_early_suspend(struct early_suspend *h)
{
}
static void my_late_resume(struct early_suspend *h)
{
}
static struct early_suspend my_early_suspend_desc = {
.suspend = my_early_suspend,
.resume = my_late_resume,
};
static int __init my_init(void)
{
register_early_suspend(&my_early_suspend_desc);
return 0;
}
static void __exit my_exit(void)
{
unregister_early_suspend(&my_ early_suspend_desc);
}
module_init(my_init);
module_exit(my_exit);
另外,请慎重在驱动的suspend和resume函数中和内核线程(workqueue或tasklet)中操作wakelock。
5. FrameWork相关
5.1. Linux内核的电源管理
Linux内核的电源管理有两个阶段suspend和resume, 分别在设备睡眠和唤醒的时候进行,需要关心电源管理的驱动程序可以注册suspend和resume函数在睡眠和唤醒时执行。Linux内核提供给应用层的接口是/sys/power/state, 有如下命令:
l echo mem>/sys/power/state:睡眠,状态保存进ram
l echo on>/sys/power/state:唤醒
l echo standby>/sys/power/state
l echo disk>/sys/power/state:睡眠, 状态保存进disk
还有一些其他命令请查阅网上资料, 有的命令不一定支持。
5.2. Android对linux内核的修改
Android linux内核的电源管理系统是基于Linux内核的电源管理系统的扩展,添加了锁的概念, 多出了两个阶段early suspend和later resume,在有锁的情况下进入的睡眠是partial lock状态, 只会执行注册的early suspend函数, 唤醒时只会执行注册的later resume函数。只有在无锁时进入睡眠才会真正suspend,注册的early suspend函数会在suspend函数前执行,注册的later resume函数会在resume函数后执行 。 关心电源管理的android驱动程序可以注册early suspend和later resume和suspend和resume函数在相应阶段执行。 Android linux内核提供给应用层得接口如下:
l /sys/power/state:只支持mem(睡眠)和on(唤醒)两个参数
l /sys/power/power_lock: 写入锁的名字即获得锁
l /sys/power/power_unlock:写入锁的名字即解锁