Android休眠流程

  1. 概述
    设备的休眠是功耗分析中的重要篇章,有些问题经常会涉及到系统无法休眠,经常被唤醒等情况。在这里将从代码角度来分析下Android系统休眠和唤醒的机制,而本篇将着重阐述休眠机制。

Android系统的电源管理子系统是已Linux的电源管理系统为基础,针对现实问题又进行了改造,而这里的现实问题来自于PC和移动设备的差异,其核心在于什么时候休眠,什么时候唤醒。在PC系统中,什么时候让系统进入休眠(通常是STR、Standby、Hibernate等suspend操作)这个问题很容易理解:答案就是由用户在其不再使用时触发。

然而移动设备的特性决定了用户可能随时随地会使用设备,那么休眠的时机就变得非常不明确了。于是Android上提出了Opportunistic suspend这个概念,通俗的讲就是逮住机会就赶紧休眠一会儿直到下次被唤醒,而这个机会就是系统没有任务需要处理的时候。由此就衍生除了autosleep机制和wake source的概念。Android的休眠是一种自动休眠,当系统中不存在唤醒源时就进行系统整体休眠。相比于Linux内核中复杂的单独模块休眠管理(Dynamic PM),Android的此套设计方式简单粗暴,但是实实在在的解决了移动设备上面临的燃眉之急。

2.自动休眠
2. 1.Frameworks层
备注:自动休眠一定不能插上usb抓取log,因插上usb不会进入休眠模式。

系统休眠又用户主动发起的关系并不是很大,在用户和应用的行为中,主动通过power键发起灭屏或者是调用PowerManager的goToSleep接口来进行系统休眠是较常用的操作,其他也没有更多的手段。而系统休眠大多是系统主动检测并发起休眠流程,所以本篇中就不涉及上层的流程了,直接从Frameworks开始。

Frameworks层这部分的设计思想比较简单,主要是:电源管理通过监测display的状态,当灭屏时则发起休眠的流程,调用native层的方法继续往下走。这里就很好的体现了前文提到的Opportunistic suspend概念,伺机休眠这里的“机”的第一层就是以灭屏作为契机的。果然是简单粗暴的判断,但是非常实用,从用户角度来将,灭屏的现象确实是让设备休眠的理想触发。当然,在灭屏时需要后台处理任务的情况也是存在,所以在后面流程中会继续判断是否真正能够进入休眠。接下来从代码角度解读下frameworks层的实现。
  流程就先从灭屏开始看,系统中除了PowerManagerService外还有个叫做DisplayManagerService的核心服务来管理display相关的电源状态,它也是在SystemServer中启动起来的。在PowerManagerService中systemReady方法中会去初始化DisplayManagerService

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

// Initialize display power management.
mDisplayManagerInternal.initPowerManagement(
        mDisplayPowerCallbacks, mHandler, sensorManager);

DisplayManagerService中的initPowerManagement相关部分如下:

private final class LocalService extends DisplayManagerInternal {
    @Override
    public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,
            SensorManager sensorManager) {
        synchronized (mSyncRoot) {
            DisplayBlanker blanker = new DisplayBlanker() {
                @Override
                public void requestDisplayState(int state, int brightness) {
                    // The order of operations is important for legacy reasons.
                    if (state == Display.STATE_OFF) {
                        requestGlobalDisplayStateInternal(state, brightness);
                    }

                    callbacks.onDisplayStateChange(state);

                    if (state != Display.STATE_OFF) {
                        requestGlobalDisplayStateInternal(state, brightness);
                    }
                }
            };
            mDisplayPowerController = new DisplayPowerController(
                    mContext, callbacks, handler, sensorManager, blanker);
        }

        mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION);
    }

实现了DisplayBlanker接口,并重写了其requestDisplayState方法,在此方法中又调用DisplayPowerCallbacks接口的onDisplayStateChange方法来进行display发生变化之后的操作。这一段为整个frameworks流程中的核心部分也是流程的中间部分,接下来补上它的头和尾。
首先是display改变的消息传递,也就是代码流程中如何会调用到DisplayBlanker接口的requestDisplayState,来看图就可以了:

首先是在DisplayPowerController中收到MSG_UPDATE_POWER_STATE消息,随后经过一系列的回调了DisplayBlanker接口的requestDisplayState,其中每个细节不是这里的重点就不展开一一分析了。然后是尾,DisplayPowerCallbacks接口的onDisplayStateChange方法在PowerManagerService中

public void onDisplayStateChange(int state) {
    // This method is only needed to support legacy display blanking behavior
    // where the display's power state is coupled to suspend or to the power HAL.
    // The order of operations matters here.
    synchronized (mLock) {
        if (mDisplayState != state) {// display的状态发生改变
            mDisplayState = state;
            if (state == Display.STATE_OFF) {// 灭屏
                if (!mDecoupleHalInteractiveModeFromDisplayConfig) {// 默认false
                    setHalInteractiveModeLocked(false);// 关闭交互模式
                }
                if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {// 默认false
                    setHalAutoSuspendModeLocked(true);// 开启autosleep模式
                }
            } else {// 亮屏
                if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
                    setHalAutoSuspendModeLocked(false);// 关闭autosleep模式
                }
                if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
                    setHalInteractiveModeLocked(true);// 开启交互模式
                }
            }
        }
    }
}

这一段中有一个非常重要的核心:前面提到了将灭屏作为休眠发起的条件,也就是说将灭屏和休眠作为了耦合的关系,而这里提供了解耦的操作,可以在代码配置中来选择是否要解耦。判断的依据就是mDecoupleHalAutoSuspendModeFromDisplayConfig这个变量,它的默认值定义在frameworks/base/core/res/res/values/config.xml中:

<!-- Power Management: Specifies whether to decouple the auto-suspend state of the
     device from the display on/off state.

     When false, autosuspend_disable() will be called before the display is turned on
     and autosuspend_enable() will be called after the display is turned off.
     This mode provides best compatibility for devices using legacy power management
     features such as early suspend / late resume.

     When true, autosuspend_display() and autosuspend_enable() will be called
     independently of whether the display is being turned on or off.  This mode
     enables the power manager to suspend the application processor while the
     display is on.

     This resource should be set to "true" when a doze component has been specified
     to maximize power savings but not all devices support it.

     Refer to autosuspend.h for details.
-->
<bool name="config_powerDecoupleAutoSuspendModeFromDisplay">false</bool>

解读:默认情况下将suspend流程和display的状态耦合在一起,默认为false(不解耦),在默认情况下,在屏幕off之后调用启用autosuspend,在屏幕on之前关闭autosuspend_enable。而如果解耦,那么suspend的流程将被独立调用而不管屏幕状态,在此模式下就算是屏幕on也允许系统休眠。

这一段代码中还包括了另外一个操作,setHalInteractiveModeLocked来设置系统可交互模式,由mDecoupleHalInteractiveModeFromDisplayConfig这个变量来控制和display的耦合关系。同样附上这个默认值的定义:

<!-- Power Management: Specifies whether to decouple the interactive state of the
     device from the display on/off state.

     When false, setInteractive(..., true) will be called before the display is turned on
     and setInteractive(..., false) will be called after the display is turned off.
     This mode provides best compatibility for devices that expect the interactive
     state to be tied to the display state.

     When true, setInteractive(...) will be called independently of whether the display
     is being turned on or off.  This mode enables the power manager to reduce
     clocks and disable the touch controller while the display is on.

     This resource should be set to "true" when a doze component has been specified
     to maximize power savings but not all devices support it.

     Refer to power.h for details.
-->
<bool name="config_powerDecoupleInteractiveModeFromDisplay">false</bool>

解读:这里描述了系统的可交互模式和display的耦合关系。当设置为false时,两者为耦合的关系,在display状态on之前setInteractive(…, true)设置为可交互;在display状态为off之后设置为不可交互。而如果为解耦状态,那么交互模式也将可以被独立设置不论display的状态如何。

我们还是来继续看auto sleep的流程,当灭屏时,调用PowerManagerService的setHalAutoSuspendModeLocked(true)

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void setHalAutoSuspendModeLocked(boolean enable) {
    if (enable != mHalAutoSuspendModeEnabled) {
        if (DEBUG) {
            Slog.d(TAG, "Setting HAL auto-suspend mode to " + enable);
        }
        mHalAutoSuspendModeEnabled = enable;
        Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalAutoSuspend(" + enable + ")");
        try {
            mNativeWrapper.nativeSetAutoSuspend(enable);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
    }
}

这里只是做了些判断和加调试代码,然后将流程通过nativeSetAutoSuspend带入到了native库中。

2.2. Native层
根据JNI的机制可以找到代码进入到了com_android_server_power_PowerManagerService.cpp中的nativeSetAutoSuspend中:

frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

static void nativeSetAutoSuspend(JNIEnv* /* env /, jclass / clazz */, jboolean enable) {
if (enable) {
android::base::Timer t;
enableAutoSuspend();
if (t.duration() > 100ms) {
ALOGD(“Excessive delay in autosuspend_enable() while turning screen off”);
}
} else {
android::base::Timer t;
disableAutoSuspend();
if (t.duration() > 100ms) {
ALOGD(“Excessive delay in autosuspend_disable() while turning screen on”);
}
}
}
当灭屏时(默认情况下)调用autosuspend_enable(),然后进一步的实现在hal文件中,源代码位于system/hardware/interfaces/suspend/1.0/default/目录下。

2.2.1 休眠机制初始化
来看一下开启自动挂起模式的实现方法——enableAutoSuspend

com_android_server_power_PowerManagerService.cpp
void enableAutoSuspend() {
static bool enabled = false;
//suspendService != nullptr && suspendService->enableAutosuspend():从后面的代码可以看到这个值决定了enlable的值,在开机执行一次
if (!enabled) {
sp suspendControl = getSuspendControl();
suspendControl->enableAutosuspend(&enabled);
}

{
    std::lock_guard<std::mutex> lock(gSuspendMutex);
    if (gSuspendBlocker) {
        gSuspendBlocker->release();
        gSuspendBlocker.clear();
    }
}

}

sp getSuspendControl() {
static std::once_flag suspendControlFlag;
std::call_once(suspendControlFlag, {
while(gSuspendControl == nullptr) {
sp control =
defaultServiceManager()->getService(String16(“suspend_control”));
if (control != nullptr) {
gSuspendControl = interface_cast(control);
}
}
});
return gSuspendControl;
}

这里有一个难点,通过getSuspendControl方法获得的ISuspendControlService实现类是什么? 请注意getSuspendControl方法里的这条语句: defaultServiceManager()->getService(String16(“suspend_control”)),这是一个典型的Binder调用,它请求了服务名称为 suspend_control 的远端服务。注册名称为suspend_control的服务到底是谁,我们可以在/system/hardware/interfaces/suspend/1.0/default/main.cpp中找到答案。

sp suspendControl = new SuspendControlService();
auto controlStatus = android::defaultServiceManager()->addService(
android::String16(“suspend_control”), suspendControl);
可以清楚的看到,SuspendControlService就是我们要寻找的答案。

SuspendControlService.cpp
binder::Status SuspendControlService::enableAutosuspend(bool* _aidl_return) {
//wp 升级为 sp,这条语句牵涉到 android 智能指针相关的知识点 promote方法可以将弱指针升级为强指针
const auto suspendService = mSuspend.promote();
//调用到了 SystemSuspend.cpp 的enableAutosuspend 方法
return retOk(suspendService != nullptr && suspendService->enableAutosuspend(), _aidl_return);
}

SuspendControlService.cpp
binder::Status retOk(const T& value, T* ret_val) {
*ret_val = value;
return binder::Status::ok();
}

SystemSuspend.cpp
bool SystemSuspend::enableAutosuspend() {
static bool initialized = false;
if (initialized) {
LOG(ERROR) << “Autosuspend already started.”;
return false;
}

initAutosuspend();
initialized = true;
return true;

}
自动挂起的核心逻辑是由 SystemSuspend.cpp 的 initAutosuspend方法来实现的:

SystemSuspend.cpp
void SystemSuspend::initAutosuspend() {
std::thread autosuspendThread([this] {
while (true) {
std::this_thread::sleep_for(mSleepTime);
lseek(mWakeupCountFd, 0, SEEK_SET);
const string wakeupCount = readFd(mWakeupCountFd);
if (wakeupCount.empty()) {
PLOG(ERROR) << “error reading from /sys/power/wakeup_count”;
continue;
}

        auto counterLock = std::unique_lock(mCounterLock);
        mCounterCondVar.wait(counterLock, [this] { return mSuspendCounter == 0; });
        // The mutex is locked and *MUST* remain locked until we write to /sys/power/state.
        // Otherwise, a WakeLock might be acquired after we check mSuspendCounter and before we
        // write to /sys/power/state.

        if (!WriteStringToFd(wakeupCount, mWakeupCountFd)) {
            PLOG(VERBOSE) << "error writing from /sys/power/wakeup_count";
            continue;
        }
        bool success = WriteStringToFd(kSleepState, mStateFd);
        counterLock.unlock();

        if (!success) {
            PLOG(VERBOSE) << "error writing to /sys/power/state";
        }

        mControlService->notifyWakeup(success);

        updateSleepTime(success);
    }
});
//在子线程运行
autosuspendThread.detach();
LOG(INFO) << "automatic system suspend enabled";

}
首先说说这一段代码:

auto counterLock = std::unique_lock(mCounterLock);
mCounterCondVar.wait(counterLock, [this] { return mSuspendCounter == 0; });
这段代码本质上是Native层实现的计数锁,当mSuspendCounter不等于0时,代码执行到wait方法时线程会进入挂起状态,直到满足条件后才会继续往下执行。 而mSuspendCounter这个变量,它会在new WakeLock时值会加一,在release时值会减一。 然而,android native层默认是没有启动计数锁的,计数锁的逻辑交给了Java层去控制,因此,我们只需要简单的了解一下就行了,不需要深入地去追究细节。

在上述计数锁等待代码前,首先执行了 从/sys/power/wakeup_count这一文件中读取了WakeCount的变量,在计数等待代码片段后,可以看到这个WakeCount又被原封不动地写回到/sys/power/wakeup_count中。从刚才的介绍可知,native默认是没有启动计数锁机制的,也就是说,这个读取的操作,连等待的过程都没有就又原封不动地写回去。为什么google要写出如此令人迷惑的代码,是不是它写错了呢?

先把这个疑问搁置一下,待会再回过头来解释,让我们接着往下看:

bool success = WriteStringToFd(kSleepState, mStateFd);
这句话的意思是,向/sys/power/state写入mem这一字符串。/sys/power/state本质是linux内核的power驱动向sysfs中注册的接口节点名称,当power驱动收到mem这条消息时,linux内核将进入特定的休眠状态。

linux系统有四种休眠状态,mem只是其中定义的一种,四种休眠模式的定义分别如下:

freeze(suspend to idle):这种方式会冻结系统中的进程,挂起所有需要挂起的设备,然后将cpu切换为idle进程,使其进入idle状态。它不会将cpu从内核中移除,因此一旦被唤醒只需从idle状态退出,恢复挂起的设备和被冻结的进程即可。

standby(suspend to standby):这种方式除了执行所有freeze相关的流程外,还会将secondary cpu从内核中移除,然后primary cpu进入standby睡眠模式。standby模式睡眠较浅,不会对cpu断电,因此在睡眠时不需要保存cpu上下文。当其一旦被唤醒,cpu就能马上投入工作,并依次恢复系统运行。(至于什么是secondary cpu、什么是primary cpu,还请感兴趣的读者自己去翻翻资料,看看linux内核对多核cpu引导的知识点)

mem(suspend to mem):相对于standby方式,这种方式下primary cpu需要先将cpu上下文保存到内存中,然后将自身断电。因此它不能直接被唤醒,而是需要先通过其它模块为其上电,然后再执行恢复cpu上下文以及其它模块的工作。由于这种方式,内核整个都已经睡着了,因此也不会有访问ddr的需求,因此也可以将ddr设置为自刷新模式,以进一步降低功耗。

disk(suspend to disk或hibernate):这是最深的一种睡眠模式,与suspend to mem将系统相关上下文保存到ddr中不同,它将系统上下文保存到磁盘中。由于所有上下文都已经保存到磁盘中,因此不仅外设、cpu可以下电,而且此时ddr也可以被断电。

当然系统并不需要支持以上所有休眠模式,一般来说,android系统支持的是freeze和mem的休眠模式,如果你的手机可以root的话,可以通过adb shell cat /sys/power/state这条命令查询到手机系统支持的休眠模式。

介绍完linux的休眠模式后,让我们重新回到上一个问题,为什么android要在休眠前做出读取又写入的这种令人迷惑的行为?
其实这和linux的suspend状态同步机制有关。在这就简单说一下,如果想了解更详细的内容,网上搜linux wakeup count就能找到一堆资料。
拿framework的源码来解释一下:

        const string wakeupCount = readFd(mWakeupCountFd);
        if (wakeupCount.empty()) {
            PLOG(ERROR) << "error reading from /sys/power/wakeup_count";
            continue;
        }

读取wakeup count值,如果成功,下一步尝试将读取的值回写。否则说明linux内核有正在处理的wakeup events,不适合进入挂起状态,continue,默认等待100ms(随着失败次数的增加而增长,最多1分钟)后重新尝试。

        if (!WriteStringToFd(wakeupCount, mWakeupCountFd)) {
            PLOG(VERBOSE) << "error writing from /sys/power/wakeup_count";
            continue;
        }

向内核写入刚才读取的wakeup count的值,如果不成功(说明读、写的过程中产生了wakeup events),continue,默认等待100ms(随着失败次数的增加而增长,最多1分钟)后重新尝试,直到成功后才能触发电源状态切换。
为什么还要再写入一次?
有两个原因,一是二次校验,这个是顺带的,不做这次校验问题也不大,后续内核在autosleep 的try_to_suspend时还会再做一次校验;二是 改变内核的events_check_enabled的值,对 wake up时的逻辑做一些控制,具体的细节这里就不展开讲了。

这个小结的最后来回答一下在节点一开始就提出来的问题,什么是 自动挂起模式?
为了更好地解释自动挂起的概念,我先请各位读者思考一个问题:设备的休眠(通常是STR、Standby、Hibernate等suspend操作),应当在什么时候、由谁触发?在传统的操作场景下,如PC、笔记本电脑,这个问题很好回答:由用户、在其不想或不再使用时,比如用户按下电源键、或者用户长时间未操作时。但在移动设备上时,这个挂起的时机就比较难确定了,移动设备相比笔记本、PC等设备,增加了大量的传感器、用户使用的频率更加频繁,时间也更加碎片化,这些增加的传感器以及用户交互的行为都有可能导致系统频繁处于唤醒状态,系统很难找到一个完美的时间节点,让设备进入休眠状态,怎么办?

这时,Android提出了“Opportunistic suspend”的理论,通俗的讲,就是“逮到机会就睡”,这就是我们的自动挂起模式。

“Opportunistic suspend”是非常简单的,只要检测到系统没有事情在做(逮到机会),就suspend整个系统。这对系统的开发人员(特别是driver开发者)来说,很容易实现,几乎不需要特别处理。

但困难的是,“系统没有事情在做”的判断依据是什么?能判断准确吗?会不会浪费过多的资源在"susend->resume-supsend…”的无聊动作上?如果只有一个设备在做事情,其它设备岂不是也得陪着耗电?等等…

所以,实现“Opportunistic suspend”机制的autosleep功能,是充满争议的。说实话,也是不优雅的。但它可以解燃眉之急,因而虽然受到非议,却在Android设备中广泛使用,它的机制并不完美,却是当前能够找到的最佳解决方案了。

2.3.Kernel层
在标准Linux中,休眠主要分三个主要的步骤:(1)冻结用户态进程和内核态任务;(2)调用注册的设备的suspend的回调函数,其调用顺序是按照驱动加载时的注册顺序。(3)休眠核心设备和使CPU进入休眠态冻结进程是内核把进程列表中所有的进程的状态都设置为停止,并且保存下所有进程的上下文。

在上面的流程中在休眠时完成了对设备节点/sys/power/state写入mem的操作,在kernel中注册了对这个设备节点操作的函数。代码位于/kernel3.18/kernel/power/main.c中,这个函数里注册了和power相关的节点的处理函数。关于state这个节点,注册了两个函数为state_store和state_show,分别对应写入和读取两个操作的处理函数。

2.3.1.休眠唤醒机制及其用户空间接口
Linux系统支持如下休眠唤醒等级

const char *const pm_states[PM_SUSPEND_MAX] = {
#ifdef CONFIG_EARLYSUSPEND
[PM_SUSPEND_ON] = “on”,
#endif
[PM_SUSPEND_STANDBY] = “standby”,
[PM_SUSPEND_MEM] = “mem”,
};
但在Android中一般只支持"on"和"mem",其中"on"为唤醒设备,"mem"为休眠设备。/sys/power/state节点的读写操作如下:

static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char s = buf;
#ifdef CONFIG_SUSPEND
int i;
pr_err(“%s: mbtk state_show: %s \n”, func,buf);
for (i = 0; i < PM_SUSPEND_MAX; i++) {
if (pm_states[i] && valid_state(i))
s += sprintf(s,"%s ", pm_states[i]); // 打印系统支持的休眠等级
}
#endif
#ifdef CONFIG_HIBERNATION
s += sprintf(s, “%s\n”, “disk”);
#else
if (s != buf)
/
convert the last space to a newline */
*(s-1) = ‘\n’;
#endif
return (s - buf);
}

static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_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;

/* First, check if we are requested to hibernate */
if (len == 4 && !strncmp(buf, "disk", len)) {
	error = hibernate();

goto Exit;
}

#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)
#ifdef CONFIG_EARLYSUSPEND
if (state == PM_SUSPEND_ON || valid_state(state)) {
error = 0;
request_suspend_state(state); // 请求进入android的休眠流程
}
#else
error = enter_state(state); // linux的标准休眠流程
#endif
#endif

Exit:
return error ? error : n;
}

power_attr(state);
其中state_show()为节点的读函数,主要打印出系统支持的休眠等级;state_store()为节点的写函数,根据参数请求休眠或者唤醒流程。节点的创建代码如下:

static struct attribute * g[] = {
&state_attr.attr, // state节点
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
#endif
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)
&pm_test_attr.attr, // pm_test节点
#endif
#ifdef CONFIG_USER_WAKELOCK
&wake_lock_attr.attr, // wake_lock节点
&wake_unlock_attr.attr, // wake_unlock节点
#endif
NULL,
};

static struct attribute_group attr_group = {
.attrs = g,
};

static int __init pm_init(void)
{
int error = pm_start_workqueue();
if (error)
return error;
power_kobj = kobject_create_and_add(“power”, NULL); // 创建power节点
if (!power_kobj)
return -ENOMEM;
return sysfs_create_group(power_kobj, &attr_group); // 创建一组属性节点
}

core_initcall(pm_init);
查看此attr_group数组的赋值就能看到实际调用的是g[]这个数组里面声明的节点。

可以网g[]里面增加一个节点wake_mbtk,如下图所示:

做如下修改就行:

代码如下:

static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
&pm_trace_dev_match_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP
&pm_async_attr.attr,
&wakeup_count_attr.attr,
#ifdef CONFIG_SUSPEND
&mem_sleep_attr.attr,
#endif
#ifdef CONFIG_PM_AUTOSLEEP
&autosleep_attr.attr,
#endif
#ifdef CONFIG_PM_WAKELOCKS
&wake_lock_attr.attr,
&wake_unlock_attr.attr,
&wake_mbtk_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP_DEBUG
&pm_test_attr.attr,
&pm_print_times_attr.attr,
&pm_wakeup_irq_attr.attr,
&pm_debug_messages_attr.attr,
#endif
#endif
#ifdef CONFIG_FREEZER
&pm_freeze_timeout_attr.attr,
#endif
NULL,
};

static ssize_t wake_mbtk_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return pm_show_wakelocks(buf, true);
}

static ssize_t wake_mbtk_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = pm_wake_lock(buf);
return error ? error : n;
}

power_attr(wake_mbtk);
在framework层已经执行到写"/sys/power/state"这个动作,那就会触发kernel里面的逻辑,也就是节点文件store逻辑。

2.3.2.深度休眠入口

static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
suspend_state_t state;
int error;

error = pm_autosleep_lock();
if (error)
	return error;

if (pm_autosleep_state() > PM_SUSPEND_ON) {
	error = -EBUSY;
	goto out;
}
//将"mem"转换成对应的数字
state = decode_state(buf, n); 
if (state < PM_SUSPEND_MAX) {
	if (state == PM_SUSPEND_MEM)
		state = mem_sleep_current;
	//这里才是真正开启kernel的休眠流程
	error = pm_suspend(state);
} else if (state == PM_SUSPEND_MAX) {
	error = hibernate();
} else {
	error = -EINVAL;
}

out:
pm_autosleep_unlock();
return error ? error : n;
}
查看pm_suspend函数:

bsp/kernel/kernel4.14/kernel/power/suspend.c
int pm_suspend(suspend_state_t state)
{
int error;

if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
	return -EINVAL;

pr_info("suspend entry (%s)\n", mem_sleep_labels[state]);
error = enter_state(state);
if (error) {
	suspend_stats.fail++;
	dpm_save_failed_errno(error);
} else {
	suspend_stats.success++;
}
pr_info("suspend exit\n");
return error;

}
进入深度休眠流程

bsp/kernel/kernel4.14/kernel/power/suspend.c
static int enter_state(suspend_state_t state)
{
int error;
unsigned long dirty;

trace_suspend_resume(TPS("suspend_enter"), state, true);
if (state == PM_SUSPEND_TO_IDLE) {

#ifdef CONFIG_PM_DEBUG
if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
pr_warn(“Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n”);
return -EAGAIN;
}
#endif
} else if (!valid_state(state)) { // 判断平台是否支持该状态
return -EINVAL;
}
if (!mutex_trylock(&pm_mutex))
return -EBUSY;

dirty = (global_node_page_state(NR_FILE_DIRTY)
		+ global_node_page_state(NR_WRITEBACK)) << (PAGE_SHIFT - 10);
spin_lock(&suspend_sys_sync_lock);
if (sync_start == 1) {
	spin_unlock(&suspend_sys_sync_lock);
	error = -EBUSY;
	pr_info("PM: suspend sync-queue syncing(%lu kB)...\n", dirty);
	goto Unlock;
}
spin_unlock(&suspend_sys_sync_lock);
if (dirty > DEEP_SLEEP_RETRY_DIRTY_WRITEBACK_THRESHOLD) {
	if (dirty < DEEP_SLEEP_RETRY_TRIGGER_SYNC_QUEUE_THRESHOLD)
		suspend_sys_sync_queue();
	error = -EBUSY;
	pr_info("PM: dirty and writeback data is %lu kB, "
		"it's too much for sys_sync, try again!\n", dirty);
	goto Unlock;
}

if (state == PM_SUSPEND_TO_IDLE)
	s2idle_begin();

#ifndef CONFIG_SUSPEND_SKIP_SYNC、
// 同步缓存
trace_suspend_resume(TPS(“sync_filesystems”), 0, true);
pr_info("Syncing filesystems … ");
sys_sync();
pr_cont(“done.\n”);
trace_suspend_resume(TPS(“sync_filesystems”), 0, false);
#endif

pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]);
pm_suspend_clear_flags();
// 做好休眠准备
error = suspend_prepare(state);
if (error)
	goto Unlock;

if (suspend_test(TEST_FREEZER))
	goto Finish;

trace_suspend_resume(TPS("suspend_enter"), state, false);
pm_pr_dbg("Suspending system (%s)\n", mem_sleep_labels[state]);
pm_restrict_gfp_mask();
// 设备休眠
error = suspend_devices_and_enter(state);
pm_restore_gfp_mask();

Finish:
events_check_enabled = false;
pm_pr_dbg(“Finishing wakeup.\n”);
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
在enter_state()中首先进入状态的判断,根据平台的特性判断是否支持此状态;然后再同步缓存;接着调用suspend_prepare()冻结大部分进程;然后再通过suspend_devices_and_enter()开始挂起设备。

2.3.3.冻结进程
bsp/kernel/kernel4.14/kernel/power/suspend.c
static int suspend_prepare(void)
{
int error;

if (!suspend_ops || !suspend_ops->enter)
	return -EPERM;

pm_prepare_console();

// 通知进行休眠准备
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if (error)
	goto Finish;
// 禁止usermodehelper
error = usermodehelper_disable();
if (error)
	goto Finish;
// 冻结所有可以冻结的进程
error = suspend_freeze_processes();
if (!error)
	return 0;

// 解冻所有进程
suspend_thaw_processes();
// 使能usermodehelper
usermodehelper_enable();

Finish:
// 通知休眠结束
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return error;
}
这里有一个notifier机制后面要专门分析下。

2.3.4.挂起设备
bsp/kernel/kernel4.14/kernel/power/suspend.c
int suspend_devices_and_enter(suspend_state_t state)
{
int error;

if (!suspend_ops)
	return -ENOSYS;
// 处理器的休眠开始函数
if (suspend_ops->begin) {
	error = suspend_ops->begin(state);
	if (error)
		goto Close;
}
// 休眠串口
suspend_console();
suspend_test_start();
// 设备休眠
error = dpm_suspend_start(PMSG_SUSPEND);
if (error) {
	printk(KERN_ERR "PM: Some devices failed to suspend\n");
	goto Recover_platform;
}
suspend_test_finish("suspend devices");
if (suspend_test(TEST_DEVICES))
	goto Recover_platform;
// 处理器休眠
suspend_enter(state);

Resume_devices:
suspend_test_start();
// 设备唤醒
dpm_resume_end(PMSG_RESUME);
suspend_test_finish(“resume devices”);
// 唤醒串口
resume_console();
Close:
// 处理器的休眠结束函数
if (suspend_ops->end)
suspend_ops->end();
return error;

Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices;
}
上述函数主要处理以下逻辑:

  1. 调用platform_suspend_begin,这个区分平台,看下平台是否有在suspend开始时执行操作的需求。略过。

  2. suspend console,ptintk将不能打印。

  3. suspend所有非系统设备,即调用设备注册的suspend回调(dpm_suspend_start)。device节点的流转过程就是device在整个休眠唤醒过程中的流程,对文字敏感的朋友可以直接看下图。对单个设备来讲,先从dpm_list链表中取出device并执行该 device的prepare回调,成功后会把该device节点移动到dpm_prepared_list链表,后面在做 dpm_suspend时,会从这个链表里面取device,执行suspend的回调,成功后会把该device移动到dpm_suspended_list链表,dpm_suspend_late中会从dpm_suspended_list链表中取出device执行suspend_late回调,然后把device节点移动到dpm_late_early_list链表,dpm_noirq_suspend_devices里会从dpm_late_early_list链表中取出device执行suspend_noirq回调,并把device节点移动到dpm_noirq_list链表,在后续resume过程中dpm_resume_noirq中又会从dpm_noirq_list链表中取出device执行resume_noirq回调,并把device节点移动到dpm_late_early_list链表,dpm_resume_early时会从dpm_late_early_list链表中取出device并执行resume_early回调,然后把device节点链入dpm_suspended_list链表,在dpm_resume时会从dpm_suspended_list链表中拿到device执行resume回调,并把device节点再移动到dpm_prepared_list链表,最后执行dpm_complete时,会从dpm_prepared_list链表中拿到device执行complete回调,并把device重新链入dpm_list链表。完成device节点的流转。设备的休眠和唤醒是系统休眠唤醒的重要组成部分,流转流程如下图:

4) 把系统进入到要求的sleep状态,然后停止运行(suspend_enter)。

可以看到设备挂起流程先从处理器自身开始,平台一般不需要做特殊的处理;接着关闭串口,然后调用 dpm_suspend_start()开始挂起设备,如果成功挂起所有设备则调用suspend_enter()挂起处理器。挂起设备部分的代码在linux/driver/base/power/main.c中:

bsp/kernel/kernel4.14/drivers/base/power/main.c
int dpm_suspend_start(pm_message_t state)
{
int error;

might_sleep();
error = dpm_prepare(state);
if (!error)
	error = dpm_suspend(state);
return error;

}
EXPORT_SYMBOL_GPL(dpm_suspend_start);
挂起设备分为2个步骤,首先执行设备的prepare函数,然后再执行suspend函数。

bsp/kernel/kernel4.14/drivers/base/power/main.c
// 函数将会调用所有的非sysdev设备的prepare()接口
static int dpm_prepare(pm_message_t state)
{
struct list_head list;
int error = 0;

INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
transition_started = true;
// 遍历设备链表
while (!list_empty(&dpm_list)) {
	// 从最先初始化的节点开始遍历
	struct device *dev = to_device(dpm_list.next);
	// 获取设备
	get_device(dev);
	// 更新设备状态
	dev->power.status = DPM_PREPARING;
	mutex_unlock(&dpm_list_mtx);

	pm_runtime_get_noresume(dev);
	// 在系统休眠期间有可能受到唤醒请求
	if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
		/* Wake-up requested during system sleep transition. */
		pm_runtime_put_noidle(dev);
		error = -EBUSY;
	} else {  // 执行prepare()函数
		error = device_prepare(dev, state);
	}

	mutex_lock(&dpm_list_mtx);
	// 如果出错则跳出循环
	if (error) {
		dev->power.status = DPM_ON;
		if (error == -EAGAIN) {
			put_device(dev);
			error = 0;
			continue;
		}
		printk(KERN_ERR "PM: Failed to prepare device %s "
			"for power transition: error %d\n",
			kobject_name(&dev->kobj), error);
		put_device(dev);
		break;
	}
	// 更新状态
	dev->power.status = DPM_SUSPENDING;
	if (!list_empty(&dev->power.entry))
		// 将设备节点移动到list链表中
		list_move_tail(&dev->power.entry, &list);
	put_device(dev);
}
// 拼接链表
list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx);
return error;

}
可以看到函数将遍历 dpm_list链表,并执行每个设备的prepare函数,内核规定prepare函数的实现不能改变硬件的状态;系统中每一个设备注册时都将被加入dpm_list链表的尾部,所以链表排序为设备注册的顺序。

bsp/kernel/kernel4.14/drivers/base/power/main.c
int dpm_suspend(pm_message_t state)
{
ktime_t starttime = ktime_get();
int error = 0;

trace_suspend_resume(TPS("dpm_suspend"), state.event, true);
might_sleep();

cpufreq_suspend();

mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
while (!list_empty(&dpm_prepared_list)) {
	// 逆序遍历链表,即先suspend后注册的设备,符合设备与父设备电源挂起的先后原则
	struct device *dev = to_device(dpm_prepared_list.prev);

	get_device(dev);
	mutex_unlock(&dpm_list_mtx);

	error = device_suspend(dev); //这里挂起设备

	mutex_lock(&dpm_list_mtx);
	if (error) {
		pm_dev_err(dev, state, "", error);
		dpm_save_failed_dev(dev_name(dev));
		put_device(dev);
		break;
	}
	if (!list_empty(&dev->power.entry))
		list_move(&dev->power.entry, &dpm_suspended_list);
	put_device(dev);
	if (async_error)
		break;
}
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
if (!error)
	error = async_error;
if (error) {
	suspend_stats.failed_suspend++;
	dpm_save_failed_step(SUSPEND_SUSPEND);
}
dpm_show_time(starttime, state, error, NULL);
trace_suspend_resume(TPS("dpm_suspend"), state.event, false);
return error;

}
函数将设备按照注册顺序反向挂起,挂起执行的流程如下:

bsp/kernel/kernel4.14/drivers/base/power/main.c
static int device_suspend(struct device *dev)
{
reinit_completion(&dev->power.completion);

if (is_async(dev)) {
	get_device(dev);
	async_schedule(async_suspend, dev);
	return 0;
}

return __device_suspend(dev, pm_transition, false);

}
继续看看详细执行代码:

bsp/kernel/kernel4.14/drivers/base/power/main.c
static int __device_suspend(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback = NULL;
const char *info = NULL;
int error = 0;
char suspend_abort[MAX_SUSPEND_ABORT_LEN];
DECLARE_DPM_WATCHDOG_ON_STACK(wd);

TRACE_DEVICE(dev);
TRACE_SUSPEND(0);

dpm_wait_for_subordinate(dev, async);

if (async_error) {
	dev->power.direct_complete = false;
	goto Complete;
}

/*
 * If a device configured to wake up the system from sleep states
 * has been suspended at run time and there's a resume request pending
 * for it, this is equivalent to the device signaling wakeup, so the
 * system suspend operation should be aborted.
 */
if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
	pm_wakeup_event(dev, 0);

if (pm_wakeup_pending()) {
	pm_get_active_wakeup_sources(suspend_abort,
		MAX_SUSPEND_ABORT_LEN);
	log_suspend_abort_reason(suspend_abort);
	dev->power.direct_complete = false;
	async_error = -EBUSY;
	goto Complete;
}

if (dev->power.syscore)
	goto Complete;

/* Avoid direct_complete to let wakeup_path propagate. */
if (device_may_wakeup(dev) || dev->power.wakeup_path)
	dev->power.direct_complete = false;

if (dev->power.direct_complete) {
	if (pm_runtime_status_suspended(dev)) {
		pm_runtime_disable(dev);
		if (pm_runtime_status_suspended(dev))
			goto Complete;

		pm_runtime_enable(dev);
	}
	dev->power.direct_complete = false;
}

dpm_watchdog_set(&wd, dev);
device_lock(dev);

if (dev->pm_domain) {
	info = "power domain ";
	callback = pm_op(&dev->pm_domain->ops, state);
	goto Run;
}

if (dev->type && dev->type->pm) { // type的suspend优先
	info = "type ";
	callback = pm_op(dev->type->pm, state);
	goto Run;
}

if (dev->class) {  //class次之
	if (dev->class->pm) {
		info = "class ";
		callback = pm_op(dev->class->pm, state);
		goto Run;
	} else if (dev->class->suspend) {
		pm_dev_dbg(dev, state, "legacy class ");
		error = legacy_suspend(dev, state, dev->class->suspend,
					"legacy class ");
		goto End;
	}
}

if (dev->bus) {  // bus优先级最低
	if (dev->bus->pm) {
		info = "bus ";
		callback = pm_op(dev->bus->pm, state);
	} else if (dev->bus->suspend) {
		pm_dev_dbg(dev, state, "legacy bus ");
		error = legacy_suspend(dev, state, dev->bus->suspend,
					"legacy bus ");
		goto End;
	}
}

Run:
if (!callback && dev->driver && dev->driver->pm) {
info = "driver ";
callback = pm_op(dev->driver->pm, state);
}

error = dpm_run_callback(callback, dev, state, info);

End:
if (!error) {
struct device *parent = dev->parent;

	dev->power.is_suspended = true;
	if (parent) {
		spin_lock_irq(&parent->power.lock);

		dev->parent->power.direct_complete = false;
		if (dev->power.wakeup_path
		    && !dev->parent->power.ignore_children)
			dev->parent->power.wakeup_path = true;

		spin_unlock_irq(&parent->power.lock);
	}
	dpm_clear_suppliers_direct_complete(dev);
}

device_unlock(dev);
dpm_watchdog_clear(&wd);

Complete:
if (error)
async_error = error;

complete_all(&dev->power.completion);
TRACE_SUSPEND(error);
return error;

}
可以看到type的suspend优先级最高,之后是class的,最后是bus的,大部分设备只注册了bus下的suspend。

2.3.5.挂起处理器
bsp/kernel/kernel4.14/kernel/power/suspend.c
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
char suspend_abort[MAX_SUSPEND_ABORT_LEN];
int error, last_dev;
// 处理器的休眠准备函数
error = platform_suspend_prepare(state);
if (error)
goto Platform_finish;
// 处理器休眠最后的准备
error = dpm_suspend_late(PMSG_SUSPEND);
if (error) {
last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
last_dev %= REC_FAILED_NUM;
pr_err(“late suspend of devices failed\n”);
log_suspend_abort_reason(“%s device failed to power down”,
suspend_stats.failed_devs[last_dev]);
goto Platform_finish;
}

error = platform_suspend_prepare_late(state);
if (error)
	goto Devices_early_resume;

if (state == PM_SUSPEND_TO_IDLE && pm_test_level != TEST_PLATFORM) {
	s2idle_loop();
	goto Platform_early_resume;
}
// 执行非sysdev的late suspend函数
error = dpm_suspend_noirq(PMSG_SUSPEND);
if (error) {
	last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
	last_dev %= REC_FAILED_NUM;
	pr_err("noirq suspend of devices failed\n");
	log_suspend_abort_reason("noirq suspend of %s device failed",
		suspend_stats.failed_devs[last_dev]);
	goto Platform_early_resume;
}
error = platform_suspend_prepare_noirq(state);
if (error)
	goto Platform_wake;

if (suspend_test(TEST_PLATFORM))
	goto Platform_wake;
// 关闭非启动cpu
error = disable_nonboot_cpus();
if (error || suspend_test(TEST_CPUS)) {
	log_suspend_abort_reason("Disabling non-boot cpus failed");
	goto Enable_cpus;
}
// 挂起中断
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
// 挂起sysdev
error = syscore_suspend();
if (!error) {
	*wakeup = pm_wakeup_pending();
	if (!(suspend_test(TEST_CORE) || *wakeup)) {
		trace_suspend_resume(TPS("machine_suspend"),
			state, true);
		// 处理器的休眠进入函数,休眠流程运行至此
		error = suspend_ops->enter(state);
		trace_suspend_resume(TPS("machine_suspend"),
			state, false);
	} else if (*wakeup) {
		pm_get_active_wakeup_sources(suspend_abort,
			MAX_SUSPEND_ABORT_LEN);
		log_suspend_abort_reason(suspend_abort);
		error = -EBUSY;
	}
	// 唤醒sysdev
	syscore_resume();
}
// 使能中断
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());

Enable_cpus:
// 使能非启动cpu
enable_nonboot_cpus();

Platform_wake:
// 处理器开始唤醒
platform_resume_noirq(state);
dpm_resume_noirq(PMSG_RESUME);

Platform_early_resume:
platform_resume_early(state);

Devices_early_resume:
//执行非sysdev的early resume函数
dpm_resume_early(PMSG_RESUME);

Platform_finish:
// 处理器休眠结束
platform_resume_finish(state);
return error;
}
在这个阶段首先看处理器是否需要做一些准备,接下来执行非sysdev的late suspend函数,然后处理器做休眠前最后的准备、关闭非启动cpu、挂起中断,再挂起sysdev,最后进入处理器的挂起函数,至此休眠流程结束,处理器等待用户或者RTC唤醒。

2.3.5.挂起处理器
static int suspend_enter(suspend_state_t state)
{
int error;
// 处理器的休眠准备函数
if (suspend_ops->prepare) {
error = suspend_ops->prepare();
if (error)
return error;
}
// 执行非sysdev的late suspend函数
error = dpm_suspend_noirq(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR “PM: Some devices failed to power down\n”);
goto Platfrom_finish;
}
// 处理器休眠最后的准备
if (suspend_ops->prepare_late) {
error = suspend_ops->prepare_late();
if (error)
goto Power_up_devices;
}

if (suspend_test(TEST_PLATFORM))
	goto Platform_wake;
// 关闭非启动cpu
error = disable_nonboot_cpus();
if (error || suspend_test(TEST_CPUS))
	goto Enable_cpus;
// 挂起中断
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
// 挂起sysdev
error = sysdev_suspend(PMSG_SUSPEND);
if (!error) {
	if (!suspend_test(TEST_CORE))
		// 处理器的休眠进入函数,休眠流程运行至此
		error = suspend_ops->enter(state);
	// 唤醒sysdev
	sysdev_resume();
}
// 使能中断
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());

Enable_cpus:
// 使能非启动cpu
enable_nonboot_cpus();

Platform_wake:
// 处理器开始唤醒
if (suspend_ops->wake)
suspend_ops->wake();

Power_up_devices:
// 执行非sysdev的early resume函数
dpm_resume_noirq(PMSG_RESUME);

Platfrom_finish:
// 处理器休眠结束
if (suspend_ops->finish)
suspend_ops->finish();

return error;

}
可以看到类中的suspend优先级最高,之后是device_type的,最后是bus的,大部分设备只注册了bus下的suspend。

2.3.6.late suspend
在这里我们看到了一种新的suspend机制 — late suspend,是在所有的suspend执行完后再开始执行,接口为dev->bus->pm->suspend_noirq;这样early_suspend、suspend以及late suspend构成了suspend的三部曲,late suspend是在中断关闭的情况下进行的;前面我们分析的wake_lock就有用到,用于检测在suspend阶段是否有锁被激活。late suspend的实现如下:

bsp/kernel/kernel4.14/drivers/base/power/main.c
int dpm_suspend_noirq(pm_message_t state)
{
int ret;

dpm_noirq_begin();
ret = dpm_noirq_suspend_devices(state);
if (ret)
	dpm_resume_noirq(resume_event(state));

return ret;

}
2.3.7.中断关闭流程
在late suspend机制中我们看到了休眠流程中关闭系统中断的地方:

bsp/kernel/kernel4.14/kernel/irq/pm.c
void suspend_device_irqs(void)
{
struct irq_desc *desc;
int irq;

for_each_irq_desc(irq, desc) { // 遍历系统的中断
	unsigned long flags;
	bool sync;

	if (irq_settings_is_nested_thread(desc))
		continue;
	raw_spin_lock_irqsave(&desc->lock, flags);
	sync = suspend_device_irq(desc);//关闭中断
	raw_spin_unlock_irqrestore(&desc->lock, flags);

	if (sync)
		synchronize_irq(irq);
}

}
EXPORT_SYMBOL_GPL(suspend_device_irqs);
查看suspend_device_irq函数:

bsp/kernel/kernel4.14/kernel/irq/pm.c
static bool suspend_device_irq(struct irq_desc *desc)
{
if (!desc->action || irq_desc_is_chained(desc) ||
desc->no_suspend_depth)
return false;

if (irqd_is_wakeup_set(&desc->irq_data)) {
	irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
	/*
	 * We return true here to force the caller to issue
	 * synchronize_irq(). We need to make sure that the
	 * IRQD_WAKEUP_ARMED is visible before we return from
	 * suspend_device_irqs().
	 */
	return true;
}

desc->istate |= IRQS_SUSPENDED;
__disable_irq(desc); // 关闭中断

/*
 * Hardware which has no wakeup source configuration facility
 * requires that the non wakeup interrupts are masked at the
 * chip level. The chip implementation indicates that with
 * IRQCHIP_MASK_ON_SUSPEND.
 */
if (irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND)
	mask_irq(desc);
return true;

}

函数调用了__disable_irq()来关闭中断,我们看一下这个函数的实现:

bsp/kernel/kernel4.14/kernel/irq/manage.c
void __disable_irq(struct irq_desc *desc)
{
if (!desc->depth++)
irq_disable(desc);//关闭
}
2.3.8.dpm_list链表
dpm_list是内核中用于设备电源管理的链表,设备注册时通过一系列的调用 device_register() -> device_add() -> device_pm_add() 最后在device_pm_add()中将设备加入dpm_list链表中:

bsp/kernel/kernel4.14/drivers/base/power/main.c
// 设备创建时都会调用的函数,将设备加入dpm_list链表
void device_pm_add(struct device *dev)
{
pr_debug(“PM: Adding info for %s:%s\n”,
dev->bus ? dev->bus->name : “No Bus”, dev_name(dev));
device_pm_check_callbacks(dev);
mutex_lock(&dpm_list_mtx);
if (dev->parent && dev->parent->power.is_prepared)
dev_warn(dev, “parent %s should not be sleeping\n”,
dev_name(dev->parent));
// 将设备节点添加到链表尾部,即设备按注册的先后顺序从链表头部到尾部
list_add_tail(&dev->power.entry, &dpm_list);
dev->power.in_dpm_list = true;
mutex_unlock(&dpm_list_mtx);
}
而设备注销的时候会调用device_pm_remove()将设备从dpm_list链表中移除:

bsp/kernel/kernel4.14/drivers/base/power/main.c
// 设备注销时都会调用的函数,将设备从dpm_list链表中移除
void device_pm_remove(struct device *dev)
{
pr_debug(“PM: Removing info for %s:%s\n”,
dev->bus ? dev->bus->name : “No Bus”, dev_name(dev));
complete_all(&dev->power.completion);
mutex_lock(&dpm_list_mtx);
list_del_init(&dev->power.entry);
dev->power.in_dpm_list = false;
mutex_unlock(&dpm_list_mtx);
device_wakeup_disable(dev);
pm_runtime_remove(dev);
device_pm_check_callbacks(dev);
}
suspend_devices_and_enter3.power键aquire wakelock流程
3.1.WakeLock介绍
WakeLock是android系统中一种锁的机制,只要有进程持有这个锁,系统就无法进入休眠状态。应用程序要申请WakeLock时,需要在清单文件中配置android.Manifest.permission.WAKE_LOCK权限。

根据作用时间,WakeLock可以分为永久锁和超时锁,永久锁表示只要获取了WakeLock锁,必须显式的进行释放;后者表示在到达给定时间后,自动释放WakeLock锁。

根据释放原则,WakeLock可以分为计数锁和非计数锁,默认为计数锁,如果一个WakeLock对象为计数锁,只有当计数为0时锁才会被释放;如果为非计数锁,则不管申请多少次,一次就可以释放该WakeLock。

我们来看看开发中可能用到的一种让屏幕保持常亮的方式,代码如下:

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, “My Tag”);
wl.acquire();
//wl.acquire(int timeout);//申请的超时锁
接下来便以PowerManger的newWakeLock方法作为切入点,探究WakeLock的工作原理。

newWakeLock需要传入两个参数,第一个参数表示 锁的类别,具体有以下可选项:

PARTIAL_WAKE_LOCK 确保CPU运行;允许熄灭屏幕和键盘的背光。如果持有该类型的锁,即使按Power键灭屏后,CPU也不会进入休眠状态.

SCREEN_DIM_WAKE_LOCK 确保屏幕点亮(但可能变暗);允许熄灭键盘背光。按Power键灭屏后,会立即释放该锁。一般建议在application中使用android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON来代替使用此锁。

SCREEN_BRIGHT_WAKE_LOCK 类似SCREEN_DIM_WAKE_LOCK,但屏幕不会变暗。按Power键灭屏后,会立即释放该锁。一般建议在application中使用android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON来代替使用此锁。

FULL_WAKE_LOCK 确保屏幕和键盘背光灯处于点亮状态(不会进入DIM状态)。按Power键灭屏后,会立即释放该锁。

PROXIMITY_SCREEN_OFF_WAKE_LOCK 当靠近检测传感器激活时,关闭屏幕。靠近检测传感器一般设置在前置摄像头的附近,最常见的使用场景就是,非免提状态下打电话时息屏,避免打电话时手机触碰到面部引发的误操作。 如果持有该锁,则传感器检测到有物体靠近时关闭屏幕,远离时又会亮屏,该类型锁不会阻止系统进入睡眠状态。

DOZE_WAKE_LOCK 将屏幕置于低功耗状态,如果没有其他唤醒锁,则允许CPU挂起。该锁主要被DreamManager用来实现Doze模式,除非PowerManager进入Doze状态,不然此锁不会有任何影响。

DRAW_WAKE_LOCK 如果持有该锁,则会时设备保持唤醒状态,以进行绘制屏幕,该锁常用于WindowManager中,允许应用在系统处于Doze状态下时进行绘制。

newWakeLock的第二个参数表示锁的Tag,主要用于Debug。

接下来看一下获取锁的方法,代码如下:

PowerManager.java
private void acquireLocked() {
//内部计数,mInternalCount为0时,才能释放计数锁
mInternalCount++;
//外部计数,当前为计数锁且未锁定时,调用release释放锁抛出异常的控制变量
mExternalCount++;
//如果是非计数锁或者内部计数值为1,即第一次申请该锁,才会真正去申请
if (!mRefCounted || mInternalCount == 1) {
mHandler.removeCallbacks(mReleaser);
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
try {
//通过PWMS去申请计数锁时,如果锁还没有释放,不会再去重复申请
mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
mHistoryTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mHeld = true;
}
}
通过Binder调用,申请锁的业务被转发到PWMS的acquireWakeLock方法中去实现,经过层层调用,最终实际负责实现的方法是PWMS的acquireWakeLockInternal,我们直接来看这个方法就好了。

PowerManagerService.java
private void acquireWakeLockInternal(IBinder lock, int flags, String tag, String packageName,
WorkSource ws, String historyTag, int uid, int pid) {
synchronized (mLock) {

        WakeLock wakeLock;
        int index = findWakeLockIndexLocked(lock);
        boolean notifyAcquire;
    	//是否锁已存在
        if (index >= 0) {
            wakeLock = mWakeLocks.get(index);
            if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
                // Update existing wake lock.  This shouldn't happen but is harmless.
                notifyWakeLockChangingLocked(wakeLock, flags, tag, packageName,
                        uid, pid, ws, historyTag);
	    	//更新WakeLock的属性
            wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid);
            }
            notifyAcquire = false;
        } else {
            UidState state = mUidState.get(uid);
            if (state == null) {
                state = new UidState(uid);
                state.mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
                mUidState.put(uid, state);
            }
            state.mNumWakeLocks++;
	//创建一个WakeLock,请注意这个WakeLock和PowerManager的锁的区别。一个是服务端的对象,一个是客户端的对象。
            wakeLock = new WakeLock(lock, flags, tag, packageName, ws, historyTag, uid, pid,
                    state);
            try {
                lock.linkToDeath(wakeLock, 0);
            } catch (RemoteException ex) {
                throw new IllegalArgumentException("Wake lock is already dead.");
            }
	//加入缓存
            mWakeLocks.add(wakeLock);
	//判断是否处于某些特殊的情况,禁用WakeLock
	//1.缓存的非活动进程不能持有wakelock锁               
        	//2.如果处于idle模式,则会忽略掉非白名单中的应用申请的锁
            setWakeLockDisabledStateLocked(wakeLock);
            notifyAcquire = true;
        }
    //更新WorkSource和唤醒状态
        applyWakeLockFlagsOnAcquireLocked(wakeLock, uid);
        mDirty |= DIRTY_WAKE_LOCKS;
    //更新电源状态,这个方法我们在PWMS的启动流程中有介绍过
        updatePowerStateLocked();
        if (notifyAcquire) {
            // 统计唤醒锁持有的时长
            notifyWakeLockAcquiredLocked(wakeLock);
        }
    }
}

根据条件获取或释放唤醒锁。重点来看一下updateSuspendBlockerLocked对应的方法:

PowerManagerService.java
private void updateSuspendBlockerLocked() {
//是否需要保持CPU活动状态的SuspendBlocker锁,请注意,当申请屏幕常亮时,同时也会申请cpu挂起锁
final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
//是否需要申请保持屏幕常亮的锁
final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
//是否自动挂起,如果不需要屏幕保持唤醒,则说明可以自动挂起
final boolean autoSuspend = !needDisplaySuspendBlocker;
//当屏幕点亮时,说明处于可交互模式
final boolean interactive = mDisplayPowerRequest.isBrightOrDim();

    // 禁用自动挂起
    if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
        setHalAutoSuspendModeLocked(false);
    }

    // First acquire suspend blockers if needed.
    if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.acquire();
        mHoldingWakeLockSuspendBlocker = true;
    }
    if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
        mDisplaySuspendBlocker.acquire();
        mHoldingDisplaySuspendBlocker = true;
    }

    if (mDecoupleHalInteractiveModeFromDisplayConfig) {
        // 设置HAL层的可交互模式,本文将不做分析,原因如下:
    //底层通过HAL隔离,具体的细节由各个厂商实现,逻辑各不相同
    //一般是看不到厂商的源码,厂商对外以动态库的形式集成到系统中;虽然笔者能看到"某通"厂商的源码,但不能对外公布
       //知识点偏向硬件开发,笔者能力有限,很多细节还是看不懂
        if (interactive || mDisplayReady) {
            setHalInteractiveModeLocked(interactive);
        }
    }

    // Then release suspend blockers if needed.
    if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.release();
        mHoldingWakeLockSuspendBlocker = false;
    }
    if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
        mDisplaySuspendBlocker.release();
        mHoldingDisplaySuspendBlocker = false;
    }

    // Enable auto-suspend if needed.
    if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
        setHalAutoSuspendModeLocked(true);
    }
}

请注意上面代码片段中的这条语句——setHalAutoSuspendModeLocked,这里出现自动挂起模式,就是上一节介绍的知识。

3.2.Frameworks层
按下电源键后会走到PhoneWindowManager.java 中

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {

final int keyCode = event.getKeyCode();

switch (keyCode) {

case KeyEvent.KEYCODE_POWER: { //当我们点击电源键后会走到这个分支
EventLogTags.writeInterceptPower(
KeyEvent.actionToString(event.getAction()),
mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter);
// Any activity on the power button stops the accessibility shortcut
cancelPendingAccessibilityShortcutAction();
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
interceptPowerKeyDown(event, interactive);
} else {
interceptPowerKeyUp(event, interactive, canceled);
}
break;
}

}
当我们按下电源键时,会走到interceptPowerKeyDown() 方法中:

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {

if (!mPowerKeyHandled) {
if (interactive) {
// When interactive, we’re already awake.
// Wait for a long press or for the button to be released to decide what to do.
if (hasLongPressOnPowerBehavior()) {
if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
powerLongPress();
} else {
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());

                    if (hasVeryLongPressOnPowerBehavior()) {
                        Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
                        longMsg.setAsynchronous(true);
                        mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
                    }
                }
            }
        } else {
            wakeUpFromPowerKey(event.getDownTime());

            if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
                if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
                    powerLongPress();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageDelayed(msg,
                            ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());

                    if (hasVeryLongPressOnPowerBehavior()) {
                        Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
                        longMsg.setAsynchronous(true);
                        mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
                    }
                }

                mBeganFromNonInteractive = true;
            } else {
                final int maxCount = getMaxMultiPressPowerCount();

                if (maxCount <= 1) {
                    mPowerKeyHandled = true;
                } else {
                    mBeganFromNonInteractive = true;
                }
            }
        }
    }
...

}
亮屏的触发是电源键按下时候触发的,我们来看下interceptPowerKeyDown,判断interactive为灭屏状态,则调用wakeUpFromPowerKey唤醒系统。

private void wakeUpFromPowerKey(long eventTime) {
wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,
PowerManager.WAKE_REASON_POWER_BUTTON, “android.policy:POWER”);
}

private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, String details) {

mPowerManager.wakeUp(wakeTime, reason, details);
return true;
}
wakeUpFromPowerKey调用到PowerManagerService的wakeUp函数。

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

public void wakeUp(long eventTime, @WakeReason int reason, String details, String opPackageName) {

mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);

final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
    wakeUpInternal(eventTime, reason, details, uid, opPackageName, uid);
} finally {
    Binder.restoreCallingIdentity(ident);
}

}
PowerManagerService wakeUp进行权限检查后,调用到wakeUpInternal。

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void wakeUpInternal(long eventTime, @WakeReason int reason, String details, int uid, String opPackageName, int opUid) {
synchronized (mLock) {
if (wakeUpNoUpdateLocked(eventTime, reason, details, uid, opPackageName, opUid)) {
updatePowerStateLocked();
}
}
}

private boolean wakeUpNoUpdateLocked(long eventTime, @WakeReason int reason, String details, int reasonUid, String opPackageName, int opUid) {

try {
mLastWakeTime = eventTime;
mLastWakeReason = reason;
setWakefulnessLocked(WAKEFULNESS_AWAKE, reason, eventTime);

    mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid);
    userActivityNoUpdateLocked(
            eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
} finally {
    Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return true;

}
可以看到主要做了两件事,调用wakeUpNoUpdateLocked,如果返回true,则调用updatePowerStateLocked。

我们先来看下wakeUpNoUpdateLocked里面做了哪些动作,首先做了一些状态判断和变量赋值,然后执行:

setWakefulnessLocked : 设置wakefulness为WAKEFULNESS_AWAKE唤醒状态,并发送亮屏广播;
mNotifier.onWakeUp : 通知BatteryStatsService、AppService系统服务唤醒状态变化;
userActivityNoUpdateLocked : 更新用户活动时间,重置超时灭屏时间。

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void updatePowerStateLocked() {
try {
updateIsPoweredLocked(mDirty);
updateStayOnLocked(mDirty);
updateScreenBrightnessBoostLocked(mDirty);

    final long now = SystemClock.uptimeMillis();
    int dirtyPhase2 = 0;
    for (;;) {
        int dirtyPhase1 = mDirty;
        dirtyPhase2 |= dirtyPhase1;
        mDirty = 0;

        updateWakeLockSummaryLocked(dirtyPhase1);
        updateUserActivitySummaryLocked(now, dirtyPhase1);
        if (!updateWakefulnessLocked(dirtyPhase1)) {
            break;
        }
    }

    updateProfilesLocked(now);

    final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);

    updateDreamLocked(dirtyPhase2, displayBecameReady);

    finishWakefulnessChangeIfNeededLocked();

    updateSuspendBlockerLocked();
} finally {
    Trace.traceEnd(Trace.TRACE_TAG_POWER);
}

}
我们看updatePowerStateLocked,这个里面主要是在updateDisplayPowerStateLocked里面处理亮屏的关键动作:

updateDisplayPowerStateLocked调用requestPowerState设置mPendingRequestLocked变量;
调用sendUpdatePowerStateLocked发送MSG_UPDATE_POWER_STATE消息,MSG_UPDATE_POWER_STATE的消息处理里面调用updatePowerState;
updatePowerState调用animateScreenStateChange;
根据状态判断后调用setScreenState(Display.STATE_ON)->

DisplayPowerController::setScreenState()->DisplayPowerState::setScreupdateDisplayPowerStateLockedenState(),通过scheduleScreenUpdate启动一个mScreenUpdateRunnable;

mScreenUpdateRunnable里面调用mPhotonicModulator.setState(mScreenState, brightnessState),

赋值mPendingBacklight变量后通知PhotonicModulator线程去处理,调用到mBlanker.requestDisplayState;

DisplayManagerService在initPowerManagement初始化的时候会注册requestDisplayState的处理,具体处理步骤是:requestDisplayState->requestGlobalDisplayStateInternal->applyGlobalDisplayStateLocked- >updateDisplayStateLocked;
LocalDisplayAdapter::requestDisplayStateLocked调用背光的设置函数setDisplayBrightness。
主要来看updateSuspendBlockerLocked方法:

/**
* Updates the suspend blocker that keeps the CPU alive.
*
* This function must have no other side-effects.
*/
private void updateSuspendBlockerLocked() {
final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
final boolean autoSuspend = !needDisplaySuspendBlocker;
final boolean interactive = mDisplayPowerRequest.isBrightOrDim();

    // Disable auto-suspend if needed.
    // FIXME We should consider just leaving auto-suspend enabled forever since
    // we already hold the necessary wakelocks.
    if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
        setHalAutoSuspendModeLocked(false);
    }

    // First acquire suspend blockers if needed.
    if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
		//这里会调用hal层的nativeAcquireSuspendBlocker方法
        mWakeLockSuspendBlocker.acquire();
        mHoldingWakeLockSuspendBlocker = true;
    }
    if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
		//这里会调用hal层的nativeAcquireSuspendBlocker方法
        mDisplaySuspendBlocker.acquire();
        mHoldingDisplaySuspendBlocker = true;
    }

    // Inform the power HAL about interactive mode.
    // Although we could set interactive strictly based on the wakefulness
    // as reported by isInteractive(), it is actually more desirable to track
    // the display policy state instead so that the interactive state observed
    // by the HAL more accurately tracks transitions between AWAKE and DOZING.
    // Refer to getDesiredScreenPolicyLocked() for details.
    if (mDecoupleHalInteractiveModeFromDisplayConfig) {
        // When becoming non-interactive, we want to defer sending this signal
        // until the display is actually ready so that all transitions have
        // completed.  This is probably a good sign that things have gotten
        // too tangled over here...
        if (interactive || mDisplayReady) {
            setHalInteractiveModeLocked(interactive);
        }
    }

    // Then release suspend blockers if needed.
    if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.release();
        mHoldingWakeLockSuspendBlocker = false;
    }
    if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
        mDisplaySuspendBlocker.release();
        mHoldingDisplaySuspendBlocker = false;
    }

    // Enable auto-suspend if needed.
    if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
        setHalAutoSuspendModeLocked(true);
    }
}

查看mWakeLockSuspendBlocker.acquire()方法:

private final class SuspendBlockerImpl implements SuspendBlocker {
private final String mName;
private final String mTraceName;
private int mReferenceCount;

    public SuspendBlockerImpl(String name) {
        mName = name;
        mTraceName = "SuspendBlocker (" + name + ")";
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            if (mReferenceCount != 0) {
                Slog.wtf(TAG, "Suspend blocker \"" + mName
                        + "\" was finalized without being released!");
                mReferenceCount = 0;
                mNativeWrapper.nativeReleaseSuspendBlocker(mName);
                Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
            }
        } finally {
            super.finalize();
        }
    }

    @Override
    public void acquire() {
        synchronized (this) {
            mReferenceCount += 1;
            if (mReferenceCount == 1) {
                if (DEBUG_SPEW) {
                    Slog.d(TAG, "Acquiring suspend blocker \"" + mName + "\".");
                }
                Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
				//这里会调用到hal层相关的函数
                mNativeWrapper.nativeAcquireSuspendBlocker(mName);
            }
        }
    }


}
3.3.Native层
从获取锁后的native入口方法开始跟踪:

com_android_server_power_PowerManagerService.cpp
static void nativeAcquireSuspendBlocker(JNIEnv env, jclass / clazz */, jstring nameStr) {
ScopedUtfChars name(env, nameStr);
acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());
}

/hardware/libhardware_legacy/power.cpp
int acquire_wake_lock(int, const char* id) {
ATRACE_CALL();
const auto& suspendService = getSystemSuspendServiceOnce();
if (!suspendService) {
return -1;
}

std::lock_guard<std::mutex> l{gLock};
if (!gWakeLockMap[id]) {
    auto ret = suspendService->acquireWakeLock(WakeLockType::PARTIAL, id);

    if (ret.isDeadObject()) {
        return -1;
    } else {
        gWakeLockMap[id] = ret;
    }
}
return 0;

}
直接来看/system/hardware/interfaces/suspend/1.0/default/SystemSuspend.cpp中的 acquireWakeLock方法,至于怎么定位到这个类中的,本文的前面部分做过分析,这里就不再重复了。

Return<sp> SystemSuspend::acquireWakeLock(WakeLockType /* type /,
const hidl_string& name) {
auto pid = getCallingPid();
auto wlId = getWakeLockId(pid, name);
//attention
IWakeLock
wl = new WakeLock{this, wlId, name};
{
auto l = std::lock_guard(mStatsLock);

    auto& wlStatsEntry = (*mStats.mutable_wl_stats())[wlId];
    auto lastUpdated = wlStatsEntry.last_updated();
    auto timeNow = getEpochTimeNow();
    mLruWakeLockId.erase(lastUpdated);
    mLruWakeLockId[timeNow] = wlId;

    wlStatsEntry.set_name(name);
    wlStatsEntry.set_pid(pid);
    wlStatsEntry.set_active(true);
    wlStatsEntry.set_last_updated(timeNow);

    if (mStats.wl_stats().size() > mMaxStatsEntries) {
        auto lruWakeLockId = mLruWakeLockId.begin()->second;
        mLruWakeLockId.erase(mLruWakeLockId.begin());
        mStats.mutable_wl_stats()->erase(lruWakeLockId);
    }
}
return wl;

}
请注意这句话,IWakeLock* wl = new WakeLock{this, wlId, name}。WakeLock的构造函数里有个非常关键的内容,代码如下:

WakeLock::WakeLock(SystemSuspend* systemSuspend, const WakeLockIdType& id, const string& name)
mReleased(), mSystemSuspend(systemSuspend), mId(id), mName(name) {
mSystemSuspend->incSuspendCounter(mName);
}

void SystemSuspend::incSuspendCounter(const string& name) {
auto l = std::lock_guard(mCounterLock);
if (mUseSuspendCounter) {
mSuspendCounter++;
} else {
//attention!!!
if (!WriteStringToFd(name, mWakeLockFd)) {
PLOG(ERROR) << "error writing " << name << " to " << kSysPowerWakeLock;
}
}
}
WriteStringToFd(name, mWakeLockFd)这条语句,将创建的锁的名称写入了/sys/power/wake_lock的节点。

调试log:

08-04 13:25:35.216 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::incSuspendCounter() enter 2
08-04 13:25:35.222 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::acquireWakeLock() enter 1
08-04 13:25:35.224 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::release() enter
08-04 13:25:35.224 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::decSuspendCounter() enter
08-04 13:25:43.174 718 904 E PowerManagerService-JNI: mxl: nativeAcquireSuspendBlocker enter
08-04 13:25:43.174 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::incSuspendCounter() enter 2
08-04 13:25:43.182 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::acquireWakeLock() enter 1
08-04 13:25:43.229 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::release() enter
08-04 13:25:43.229 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::decSuspendCounter() enter
08-04 13:25:43.428 718 1091 E PowerManagerService-JNI: mxl-libsuspend: nativeSetAutoSuspend go into autosuspend_enable()
08-04 13:25:43.428 718 1091 E PowerManagerService-JNI: enableAutoSuspend mxl go into enableAutoSuspend() 1
08-04 13:25:43.429 718 1091 E PowerManagerService-JNI: enableAutoSuspend mxl go into enableAutoSuspend() 1
08-04 13:25:43.429 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::release() enter
08-04 13:25:43.429 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::decSuspendCounter() enter
08-04 13:25:43.430 718 904 E PowerManagerService-JNI: mxl: nativeAcquireSuspendBlocker enter
08-04 13:25:43.435 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::incSuspendCounter() enter 2
08-04 13:25:43.444 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::acquireWakeLock() enter 1
08-04 13:25:43.445 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::release() enter
08-04 13:25:43.445 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::decSuspendCounter() enter
08-04 13:25:43.497 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::release() enter
08-04 13:25:43.497 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::decSuspendCounter() enter
08-04 13:25:48.013 718 1032 E PowerManagerService-JNI: mxl: nativeAcquireSuspendBlocker enter
08-04 13:25:48.013 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::incSuspendCounter() enter 2
08-04 13:25:48.021 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::acquireWakeLock() enter 1
08-04 13:25:48.108 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::release() enter
08-04 13:25:48.108 342 342 E /system/bin/hw/android.system.suspend@1.0-service: mxl SystemSuspend::decSuspendCounter() enter

3.4.Kernel
继续深入到linux的内核,看看接下来发生了什么。首先对wakelocks做如下介绍:

1) 一个sysfs文件: /sys/power/wake_lock, 用户程序向文件写入一个字符串,即可创建一个wake lock, 该字符串就是wakelock的名字。 该wake lock可以阻止系统进入低功耗模式。
2) 一个sysfs文件: /sys/power/wake_unlock, 用户程序向文件写入相同的字符串, 即可注销一个wakelock。
3) 当系统中所有的wakelock都注销后, 系统可以自动进入低功耗状态
4) 向内核其他driver也提供了wakelock的创建和注销接口, 允许driver创建wakelock以阻止休眠, 注销wakelock以允许睡眠。
最后执行的是wake_unlock_store函数操作:

[14:04:05:215] [ 2475.522271] c0 wake_lock_store: mbtk wake_lock_store: KeyEvents ␍␊
[14:04:05:223] [ 2475.529649] c0 wake_unlock_store: mbtk wake_unlock_store:
kernel wakelocks的功能

对比Android wakelocks要实现的功能, Linux kernel的方案是:

1) 允许driver创建wakelock以阻止睡眠, 注销wakelock以允许睡眠:已经由wakeup source取代
2) 当系统中所有的wakelock都注销后,系统可以自动进入低功耗模式:由autosleep实现
3) wake_lock和wake_unlock功能,由本文所描述的kernel wakelocks实现,其本质就是将wakeup source开发到用户空间访问。
kernel wakelocks在电源管理中的位置

相比Android wakelocks, Kernel wakelocks的实现非常简单,就是PM core中增加一个wakelock模块(kernel/power/wakelock.c), 该模块依赖wake events framework提供的wakeup source机制,实现用户空间的wakeup source(就是wakelocks), 并通过PM core main模块, 向用户空间提供两个同名的sysfs文件, wakelock和wake_unlock。

/sys/power/wake_lock & /sys/power/wake_unlock

从字面意思上,新版的wake_lock和wake_unlock和旧版的一样,都是用于创建和注销wakelock。从应用开发者的角度,确实可以这样理解。 但从底层实现的角度,却完全不是一回事。

Android的wakelock,真是一个lock, 用户程序创建一个wakelock,就是在系统suspend的路径上加了一把锁,注销就是要解开这把锁。 直到suspend路径上所有的锁都解开时,系统才可以suspend。

而kernel的wakelock, 是基于wakeup source实现的,因此创建wakelock的本质是在指定的wakup source上activate一个wakeup events, 注销wakelock的本质是deactivate wakeup event。 因此, /sys/power/wake_lock和/sys/power/wake_unlock两个sysfs文件的功能就是:

写wake_lock(以wakelock name和timeout时间<可选>为参数), 相当于以wakeup source为参数调用__pm_stay_awake(或者__pm_wake_event), 即activate wakeup event;
写wake_unlocks(以wakelock name为参数), 相当于以wakeup source为参数, 调用__pm_relax;
读wake_lock, 获取系统中所有的处于active状态的wakelock列表(也即wakeup source列表)
写wake_unlock, 返回系统中所有的处于非active状态的wakelock信息(也即wakeup source列表)。
注1: 上面有关wakeup source的操作接口:

static ssize_t wake_lock_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return pm_show_wakelocks(buf, true);
}

static ssize_t wake_lock_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = pm_wake_lock(buf);
return error ? error : n;
}

power_attr(wake_lock);

static ssize_t wake_unlock_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return pm_show_wakelocks(buf, false);
}

static ssize_t wake_unlock_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = pm_wake_unlock(buf);
return error ? error : n;
}

1) wakelocks功能不是linux kernel的必选功能,可以通过CONFIG_PM_WAKELOCKS开关
2) wake_lock的写接口,直接调用pm_wake_lock; wake_unlock的写接口,直接调用pm_wake_unlock; 他们的读接口,直接调用pm_show_wakelocks(参数不同), 这三个接口均在kernel/power/wakelock.c中实现。
如上节所述,hal层会往/sys/power/wake_lock的节点里面写值。查看如下节点的代码:

bsp/kernel/kernel4.14/kernel/power/main.c
static ssize_t wake_lock_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = pm_wake_lock(buf);
return error ? error : n;
}
继续往下看,pm_wake_lock位于kernel/power/wakelock.c, 用于上报一个wakeup event(从另一个角度,就是阻止 系统suspend):

wakelock.c
int pm_wake_lock(const char *buf)
{

//调用wakelock_lookup_add函数,查找是否有相同的name的wakelock,如果有直接返回。
//如果没有,重新创建wakelock,然后将此wakelock加入到wakelocks_tree中,同时创建该wakelock对应的wakeup source
wl = wakelock_lookup_add(buf, len, true);
if (IS_ERR(wl)) {
ret = PTR_ERR(wl);
goto out;
}
//如果该wakelock有超时时间,则调用__pm_wakeup_event函数上报一个timeout_ns的wakeup events。
//否则调用__pm_stay_awake函数上报一个没有超时的wakeup events。
if (timeout_ns) {
u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;

	do_div(timeout_ms, NSEC_PER_MSEC);
	__pm_wakeup_event(&wl->ws, timeout_ms);
} else {
	__pm_stay_awake(&wl->ws);
}

wakelocks_lru_most_recent(wl);

out:
mutex_unlock(&wakelocks_lock);
return ret;
}
a) 输入参数为一个字符串, 如“wake_lock_test 100", 该字符串指定上报wakeup event的wakelock name, 可以在name后用空格隔开, 添加一个时间值(单位ns), 表示该event的timeout值
b) 调用capable, 查看当前进程是否具备阻止系统suspend的权限 注2: capable是Linux security子系统的一个接口,用于权限判断。 我们说过,power是系统的核心资源, 理应由OS全权掌握, 但wakelock违反了这一个原则, 将阻止系统睡眠的权力给了用户空间。 这样一来, 用户空间程序将可以随心所欲的占用power资源, 特别是用户态的程序员, 天生对资源占用不敏感的(这是对的), 就导致该接口有被滥用的风险, 不过还好,通过系统的权限管理机制,可以改善这种状态(其实不是改善,而是矛盾转移,很有可能吧最终的裁决权给到用户,太糟糕了);
c)解析字符串,将timeout值保存在timeout_ns中, 解析name长度,并将name保存在原来的buf中。
d) 调用wakelock_lookup_add接口, 查找是否有相同name的wakelock。 如果有,直接返回wakelock的指针; 如果没有,分配一个wakelock, 同时调用wakeup events framework提供的接口,创建该wakelock对应的wakeup source结构。
e) 如果指定timeout值,以wakelock的wakeup source指针为参数, 调用__pm_wakeup_event接口,上报一个具有时限的wakeup event; 否则调用__pm_stay_awake,上报一个没有时限的wakeup event。
上述方法做了两件事情:

将wakelock加入到wakelocks_tree中,将wakelock的active状态置为true,同时创建对应的wakeup_source。这一步的目的是缓存wakelock信息,记录wakelock状态,避免重复创建。

根据wakelock是否有超时时间,分别调用__pm_wakeup_event或__pm_stay_awake方法,阻止系统进入休眠状态。__pm_wakeup_event超时机制,是利用内核动态定时器jiffies实现的,当处罚计时器后,wakelock的active状态会被置为false,允许系统进入休眠。不管是上述哪种方法,它都会调用wakeup_source_report_event方法,上报唤醒事件,并将wakeup_count加一。linux在auto sleep的过程中,会对wakeup event进行检查,只要wakeup count 不为0,则会阻止系统休眠。

在此,用通俗易懂的语言总结一下WakeLock的机制:android 用了个死循环,一直尝试让系统进入休眠状态,休眠前,会检查wakeup_count的值是否为0,为0则进入休眠。当其他进程持有锁的时候,会增加wakeup_count的值,这样就可以实现阻止系统休眠了。

static struct wakelock *wakelock_lookup_add(const char *name, size_t len,
bool add_if_not_found)
{
struct rb_node **node = &wakelocks_tree.rb_node;
struct rb_node *parent = *node;
struct wakelock *wl;

while (*node) {
	int diff;

	parent = *node;
	wl = rb_entry(*node, struct wakelock, node);
	diff = strncmp(name, wl->name, len);
	if (diff == 0) {
		if (wl->name[len])
			diff = -1;
		else
			return wl;
	}
	if (diff < 0)
		node = &(*node)->rb_left;
	else
		node = &(*node)->rb_right;
}
if (!add_if_not_found)
	return ERR_PTR(-EINVAL);

if (wakelocks_limit_exceeded())
	return ERR_PTR(-ENOSPC);

/* Not found, we have to add a new one. */
wl = kzalloc(sizeof(*wl), GFP_KERNEL);
if (!wl)
	return ERR_PTR(-ENOMEM);

wl->name = kstrndup(name, len, GFP_KERNEL);
if (!wl->name) {
	kfree(wl);
	return ERR_PTR(-ENOMEM);
}
wl->ws.name = wl->name;
wakeup_source_add(&wl->ws);
rb_link_node(&wl->node, parent, node);
rb_insert_color(&wl->node, &wakelocks_tree);
wakelocks_lru_add(wl);
increment_wakelocks_number();
return wl;

}
在wakelock.c中,维护一个名称为wakelocks_tree的红黑树所有的wakelock都保存在该tree上。因此该接口的动作是:

a) 查找红黑树,如果找到name相同的wakelock, 就返回wakelock指针
b) 如果没找到,切add_if_not_fount为false,返回错误
c) 如果add_if_not_found为true, 分配一个struct wakelock变量, 并初始化它的名称,他的wakeup source的名称。 调用wakeup_source_add接口,将wakeup source添加到wakeup events framework中
d) 将wakelock添加到红黑树
e) 最后调用wakelocks_lru_add接口,将新分配的wakeup添加到一个名称为wakelocks_lru_list的链表前端。
再看一下wakelock的结构

struct wakelock {
char *name;
struct rb_node node;
struct wakeup_source ws;
#ifdef CONFIG_PM_WAKELOCKS_GC
struct list_head lru;
#endif
};
非常简单:一个name指针,保存wakelock的名称;一个rb node节点,用于组成红黑树;一个wakeup source变量;如果开启了wakelocks垃圾回收功能,一个用于GC的list head。

pm_wake_unlock

int pm_wake_unlock(const char *buf)
{
struct wakelock *wl;
size_t len;
int ret = 0;

if (!capable(CAP_BLOCK_SUSPEND))
	return -EPERM;

len = strlen(buf);
if (!len)
	return -EINVAL;

if (buf[len-1] == '\n')
	len--;

if (!len)
	return -EINVAL;

mutex_lock(&wakelocks_lock);

wl = wakelock_lookup_add(buf, len, false);
if (IS_ERR(wl)) {
	ret = PTR_ERR(wl);
	goto out;
}
__pm_relax(&wl->ws);

wakelocks_lru_most_recent(wl);
wakelocks_gc();

out:
mutex_unlock(&wakelocks_lock);
return ret;
}
a)输入参数为一个字符串,如"wake_lock_test”,该字符串指定一个wakelock name。
b)调用capable,检查当前进程是否具备阻止系统suspend的权限。
c)解析字符串
d)调用wakelock_lookup_add接口,查找是否有相同name的wakelock。如果有,直接返回wakelock的指针;如果没有,退出。
e)调用__pm_relax接口,deactive wakelock对应的wakeup source。
f)调用wakelocks_lru_most_recent接口,将盖wakelock移到wakelocks_lru_list链表的前端(表示它是最近一个被访问到的,和GC有关,后面重点描述)。
g)调用wakelocks_gc,执行wakelock的垃圾回收动作。
pm_show_wakelocks

该接口很简单,查询红黑树,返回处于acvtive或者deactive状态的wakelock,如下:

ssize_t pm_show_wakelocks(char *buf, bool show_active)
{
struct rb_node *node;
struct wakelock *wl;
char *str = buf;
char *end = buf + PAGE_SIZE;

mutex_lock(&wakelocks_lock);

for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) {
	wl = rb_entry(node, struct wakelock, node);
	if (wl->ws.active == show_active)
		str += scnprintf(str, end - str, "%s ", wl->name);
}
if (str > buf)
	str--;

str += scnprintf(str, end - str, "\n");

mutex_unlock(&wakelocks_lock);
return (str - buf);

}
1)遍历红黑树,拿到wakelock指针,判断其中的wakeup source的active变量,如果和输入变量(show_active)相符,将该wakelock的名字添加在buf中。
2)调整buf的长度和结束符,返回长度值。
wakelocks的垃圾回收机制

由上面的逻辑可知,一个wakelock的生命周期,应只存于wakeup event的active周期内,因此如果它的wakeup source状态为deactive, 应该销毁该wakelock。 但销毁后,如果又产生wakeup events, 就得重新建立。 如果这种建立->销毁->建立的过程太频繁, 效率就会降低。

因此,最好不销毁, 保留系统所有的wakelocks(同时可以完整的保留wakelock信息), 但如果wakelocks太多(特别是不活动的),将会占用很多内存,也不合理。

折衷方案: 保留一些非active状态的wakelock, 到一定时机时再销毁,这就是wakelocks的垃圾回收机制。

wakelocks GC功能可以开关(由CONFIG_PM_WAKELOCK_GC控制), 如果关闭,系统会保留所有的wakelocks,如果打开,它的处理逻辑也很简单:

1) 定义一个list head, 保存所有的wakelock指针,如下
static LIST_HEAD(wakelocks_lru_list);
static unsigned int wakelocks_gc_count;
2) 在wakelock结构中,嵌入一个list head(lru), 用于挂入wakelocks_lru_list
3) wakelocks_lru_list中的wakelock是按访问顺序排列的, 最近访问的,靠近head位置,这是由三种操作保证的
a)wakelock创建时,调用wakelocks_lru_add接口,将改wakelock挂到wakelocks_lru_list的head处(利用list_add接口),表示它是最近被访问的。

b)pm_wake_lock或者pm_wake_unlock时,调用wakelocks_lru_most_recent接口,将该wakelcok移到链表的head处,表示最近访问。

c)每当pm_wake_unlock时,调用wakelocks_gc,执行wakelock的垃圾回收动作。

4.后记
可以发现wakelock 的作用仅仅只是阻止系统进入休眠状态。还记得文章一开始举例的持有锁的代码吗,在应用层创建锁的时候,有一个type属性,不同的type表现的形式也并不相同。在这里给各位读者留一个思考作业,PARTIAL_WAKE_LOCK允许屏幕息屏,保持cpu的唤醒,而SCREEN_BRIGHT_WAKE_LOCK会让屏幕和cpu都保持唤醒的状态,framework是如何去控制这种差异的?简单地提示一下,各位在PowerManagerService中就能找到答案,不需要深入到native层。

1、关于休眠发起的时机——在亮屏但是无任务时也能发起休眠流程
–如何判断无任务?
—-亮屏状态下一段时间无用户操作&无除display以外的blocker
–休眠行为?
—-设备整体进入suspend,屏幕单独处理

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lion_heart001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值