在分析电源管理时,提到设备休眠时,由应用写/sys/power/state来实现休眠。在Android系统中,当系统因为一次网络包唤醒后,将会很快再次进入休眠,已达到节省电量目的,这次休眠是系统自动发起的。我们现在分析这次自动休眠的流程。
在PowerManagerService.java中,有检测亮灭屏的一个类DisplayBlankerImpl。在DisplayBlankerImpl中,通过检测屏的状态,来打开和关闭自动suspend功能。在Android7已经更高版本,检测屏的亮灭移到了healthd进程中。在亮屏息屏时,将设置nativeSetInteractive(true)。对应方法为:
@com_android_server_power_PowerManagerService.cpp
{ "nativeSetAutoSuspend", "(Z)V",
(void*) nativeSetAutoSuspend },
static void nativeSetAutoSuspend(JNIEnv *env, jclass clazz, jboolean enable) {
if (enable) {
ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_enable() while turning screen off");
autosuspend_enable();
} else {
ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_disable() while turning screen on");
autosuspend_disable();
}
}
通过autosuspend_enable方法来打开和关闭autosuspend. 我们看autosuspend_enable功能。
@/system/core/libsuspend/autosuspend.c
int autosuspend_enable(void){
int ret;
ret = autosuspend_init();
ret = autosuspend_ops->enable();
autosuspend_enabled = true;
return 0;
}
我们看autosuspend_init和autosuspend_ops->enable()这两个方法。在autosuspend_init中会选择自动休眠的方式,设置autosuspend_ops结构体,通过不同休眠方式设置不同autosuspend_ops结构体,来选择不同自动休眠方法。对于Android4.4如下:
@/system/core/libsuspend/autosuspend.c
static int autosuspend_init(void)
{
autosuspend_ops = autosuspend_wakeup_count_init();
if (autosuspend_ops) {
goto out;
}
autosuspend_ops = autosuspend_autosleep_init();
if (autosuspend_ops) {
goto out;
}
autosuspend_ops = autosuspend_earlysuspend_init();
if (autosuspend_ops) {
goto out;
}
if (!autosuspend_ops) {
ALOGE("failed to initialize autosuspend\n");
return -1;
}
out:
autosuspend_inited = true;
return 0;
}
此处我们关注第一种方式,autosuspend_wakeup_count_init(),我们将使用wake count方式。看其实现:
@/system/core/libsuspend/autosuspend_wakeup_count.c
#define SYS_POWER_STATE "/sys/power/state"
#define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count"
struct autosuspend_ops *autosuspend_wakeup_count_init(void)
{
int ret;
char buf[80];
state_fd = open(SYS_POWER_STATE, O_RDWR);
if (state_fd < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error opening %s: %s\n", SYS_POWER_STATE, buf);
goto err_open_state;
}
wakeup_count_fd = open(SYS_POWER_WAKEUP_COUNT, O_RDWR);
if (wakeup_count_fd < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error opening %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
goto err_open_wakeup_count;
}
//初始化信号量
ret = sem_init(&suspend_lockout, 0, 0);
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error creating semaphore: %s\n", buf);
goto err_sem_init;
}
ret = pthread_create(&suspend_thread, NULL, suspend_thread_func, NULL);
if (ret) {
strerror_r(ret, buf, sizeof(buf));
ALOGE("Error creating thread: %s\n", buf);
goto err_pthread_create;
}
ALOGI("Selected wakeup count\n");
return &autosuspend_wakeup_count_ops;
}
这里首先打开了两个节点,一个是/sys/power/state,用来控制休眠的节点。一个是/sys/power/wakeup_count,wake_count用来是wake_source的使用计数,一个wake_source被activite,表示是有唤醒事件,wake_count将自加,设备需要系统保持唤醒。因此/sys/power/state用来设置休眠操作,但前提是/sys/power/wakeup_count已经为空。
wakeup_count用法如下:
另外autosuspend_wakeup_count_init创建了一线程,我们看线程的方法:
wakeup count的功能是suspend同步,实现思路是这样的:
任何想发起电源状态切换的实体(可以是用户空间电源管理进程,也可以是内核线程,简称C),在发起状态切换前,读取系统的wakeup counts(该值记录了当前的wakeup event总数),并将读取的counts告知wakeup events framework。
wakeup events framework记录该counts到一个全局变量中(saved_count)。
随后C发起电源状态切换(如STR),执行suspend过程。
在suspend的过程中,wakeup events framework照旧工作(直到系统中断被关闭),上报wakeup events,增加wakeup events counts。
suspend执行的一些时间点(可参考“Linux电源管理(6)_Generic PM之Suspend功能”),会调用wakeup events framework提供的接口(pm_wakeup_pending),检查是否有wakeup没有处理。
检查逻辑很简单,就是比较当前的wakeup counts和saved wakeup counts(C发起电源状态切换时的counts),如果不同,就要终止suspend过程。
@/system/core/libsuspend/autosuspend_wakeup_count.c
static void *suspend_thread_func(void *arg __attribute__((unused)))
{
char buf[80];
char wakeup_count[20];
int wakeup_count_len;
int ret;
while (1) {
usleep(100000); // 100ms轮询一次
lseek(wakeup_count_fd, 0, SEEK_SET);
wakeup_count_len = read(wakeup_count_fd, wakeup_count, sizeof(wakeup_count));
// 通过信号量控制线程运行
ret = sem_wait(&suspend_lockout);
ret = write(wakeup_count_fd, wakeup_count, wakeup_count_len);
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
}else {
ret = write(state_fd, sleep_state, strlen(sleep_state));
}
ret = sem_post(&suspend_lockout);
}
return NULL;
}
在线程中完成wake_count的检查和休眠节点state的写入。创建好线程后,就返回autosuspend_wakeup_count_ops结构:
@/system/core/libsuspend/autosuspend_wakeup_count.c
struct autosuspend_ops autosuspend_wakeup_count_ops = {
.enable = autosuspend_wakeup_count_enable,
.disable = autosuspend_wakeup_count_disable,
};
在autosuspend_enable中将调用autosuspend_wakeup_count_ops->enable函数指针。
static int autosuspend_wakeup_count_enable(void)
{
int ret;
ret = sem_post(&suspend_lockout);
return ret;
}
autosuspend_wakeup_count_enable通过sem_post来启动线程,是autosuspend线程工作起来。
这样,在通过以上流程后,libsuspend就可以在息屏后,自动息屏的线程工作起来,让唤醒的系统及时休眠。这些控制过程全部在user空间完成。
接下来的内核空间的工作流程,在下一篇中介绍。