在分析电源管理时,提到设备休眠时,由应用写/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同步,实现思路是这样的:

  1. 任何想发起电源状态切换的实体(可以是用户空间电源管理进程,也可以是内核线程,简称C),在发起状态切换前,读取系统的wakeup counts(该值记录了当前的wakeup event总数),并将读取的counts告知wakeup events framework。
  2. wakeup events framework记录该counts到一个全局变量中(saved_count)。
  3. 随后C发起电源状态切换(如STR),执行suspend过程。
  4. 在suspend的过程中,wakeup events framework照旧工作(直到系统中断被关闭),上报wakeup events,增加wakeup events counts。
  5. suspend执行的一些时间点(可参考“Linux电源管理(6)_Generic PM之Suspend功能”),会调用wakeup events framework提供的接口(pm_wakeup_pending),检查是否有wakeup没有处理。
  6. 检查逻辑很简单,就是比较当前的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空间完成。

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