在 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 机制先去做一下了解。