admin管理员组

文章数量:1575955

在分析电源管理时,提到设备休眠时,由应用写/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空间完成。

接下来的内核空间的工作流程,在下一篇中介绍。

本文标签: 简介android