在 Android 10 电源管理分析之PowerManagerService的启动(一)一文中,我们对PWMS的启动流程做了一个简单的介绍,其中有一个重要的概念——唤醒锁,即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);
}
}
}
在上一篇文章,笔者有对updatePowerStateLocked
这个方法做过简单的介绍,它的第七步是非常重要的一步——更新锁状态,根据条件获取或释放唤醒锁。重点来看一下第七步对应的方法:
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
,这里出现了一个新的知识点,自动挂起模式。什么是自动挂起模式呢?为了更好的解释它,接下来将追溯Native层源码去做一个深入的分析。
自动挂起模式
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");
}
}
}
来看一下开启自动挂起模式的实现方法——enableAutoSuspend
,关闭的方法由于文章篇幅的原因,感兴趣的读者可以自行分析:
com_android_server_power_PowerManagerService.cpp
void enableAutoSuspend() {
static bool enabled = false;
if (!enabled) {
sp<ISuspendControlService> suspendControl = getSuspendControl();
suspendControl->enableAutosuspend(&enabled);
}
{
std::lock_guard<std::mutex> lock(gSuspendMutex);
if (gSuspendBlocker) {
gSuspendBlocker->release();
gSuspendBlocker.clear();
}
}
}
sp<ISuspendControlService> getSuspendControl() {
static std::once_flag suspendControlFlag;
std::call_once(suspendControlFlag, [](){
while(gSuspendControl == nullptr) {
sp<IBinder> control =
defaultServiceManager()->getService(String16("suspend_control"));
if (control != nullptr) {
gSuspendControl = interface_cast<ISuspendControlService>(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<SuspendControlService> 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 智能指针相关的知识点
const auto suspendService = mSuspend.promote();
//调用到了 SystemSuspend.cpp 的enableAutosuspend 方法
return retOk(suspendService != nullptr && suspendService->enableAutosuspend(), _aidl_return);
}
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()) {
continue;
}
读取wakeup count值,如果成功,下一步尝试将读取的值回写。否则说明linux内核有正在处理的wakeup events,不适合进入挂起状态,continue,默认等待100ms(随着失败次数的增加而增长,最多1分钟)后重新尝试。
if (!WriteStringToFd(wakeupCount, mWakeupCountFd)) {
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设备中广泛使用,它的机制并不完美,却是当前能够找到的最佳解决方案了。
aquire wakelock
在这一小节,我们来看一下 aquire wakelock 后,系统做了哪些处理,为什么持有锁系统就无法进入休眠状态了,我们从获取锁后的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<IWakeLock>> 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
的节点。
继续深入到linux的内核,看看接下来发生了什么。
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;
}
上述方法做了两件事情:
-
将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的值,这样就可以实现阻止系统休眠了。
读到这里,可以发现wakelock 的作用仅仅只是阻止系统进入休眠状态。还记得文章一开始举例的持有锁的代码吗,在应用层创建锁的时候,有一个type属性,不同的type表现的形式也并不相同。在这里给各位读者留一个思考作业,PARTIAL_WAKE_LOCK
允许屏幕息屏,保持cpu的唤醒,而SCREEN_BRIGHT_WAKE_LOCK
会让屏幕和cpu都保持唤醒的状态,framework是如何去控制这种差异的?简单地提示一下,各位在PowerManagerService中就能找到答案,不需要深入到native层。
关于android的电源管理的WakeLock分析到此就告一段落了,其实android的电源策略和linux内核的电源策略息息相关,由于文章篇幅的原因并没有详细地展开来讲,只选择了一些关键性的内容,如果看完本篇文章的读者仍感到云里雾里,可以先去对linux的power 机制先去做一下了解。