Android定义了几种低功耗的状态:earlysuspend(挂起早期),suspend(挂起),hibernation(休眠)。之所以增加了节能的复杂性,主要考虑到android设备在有程序运行时同样需要进行节能这种特殊需求,以及考虑了外设(比如wifi、led等)单独节能的需求。
其中earlysuspend是一种低功耗的状态,某些设备可以选择进入某种功耗较低的状态,比如LCD可以降低亮度或灭掉。此状态为android新增状态。
syspend指除电源管理以外的其它外围模块以及cpu均不工作,只有内存保持自刷新的状态。此状态的进入方式android有修改。
Hibernation指所有内存镜像都被写入磁盘中,然后系统关机,恢复后系统能恢复到关机前状态。
电源管理源代码主要在/kernel/power/文件夹下。用户可以通过读写sys文件/sys/power/state实现控制系统进入低功耗状态。用户通过将const char * const pm_states[] 中定义的字符串, 比如“on”对应非节能状态,“mem”,“standby”对应earlysuspend,“disk”对应hibernation,wake lock 机制进入suspend状态。写入/sys/power/state进行功耗控制,会调用到state_store():
static ssize_tstate_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf,size_t n)
{
#ifdef CONFIG_SUSPEND
#ifdefCONFIG_EARLYSUSPEND
suspend_state_t state = PM_SUSPEND_ON;
#else
suspend_state_t state = PM_SUSPEND_STANDBY;
#endif
const char * const *s;
#endif
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
首先判断用户写入的是否是“disk”字符串,如果是则调用hibernate()函数命令系统进入hibernation状态
/* First, check if we are requested to hibernate */
if (len == 4 && !strncmp(buf, "disk", len)) {
error = hibernate();
goto Exit;
}
如果是其他字符串则且定义了CONFIG_EARLYSUSPEND调用request_suspend_state(),如果未定义了CONFIG_EARLYSUSPEND调用enter_state()
#ifdef CONFIG_SUSPEND
for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++,state++) {
if (*s && len == strlen(*s) && !strncmp(buf, *s,len))
break;
}
if (state < PM_SUSPEND_MAX && *s)
#ifdefCONFIG_EARLYSUSPEND
if (state == PM_SUSPEND_ON || valid_state(state)) {
error = 0;
request_suspend_state(state);
}
#else
error =enter_state(state);
#endif
#endif
Exit:
return error ? error : n;
}
request_suspend_state()函数是android相对标准linux改动的地方。在标准linux内核中,用户通过 sysfs 写入“mem”和“standby”时,会直接调用enter_state()进入suspend模式,但在android中则会调用request_suspend_state()函数进入early suspend状态。request_suspend_state()函数代码如下:
void request_suspend_state(suspend_state_tnew_state)
{
unsigned long irqflags;
int old_sleep;
spin_lock_irqsave(&state_lock, irqflags);
old_sleep = state & SUSPEND_REQUESTED;
if (debug_mask & DEBUG_USER_STATE) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec, &tm);
pr_info("request_suspend_state: %s (%d->%d) at %lld"
"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
new_state != PM_SUSPEND_ON ? "sleep" :"wakeup",
requested_suspend_state, new_state,
ktime_to_ns(ktime_get()),<