Android7.0 PowerManagerService WakeLock的使用及流程

吾生也有涯,而知也无涯。这篇文章主要讲述Android7.0 PowerManagerService WakeLock的使用及流程相关的知识,希望能为你提供帮助。
作为移动终端, 电量是一种稀缺资源, 需要尽可能的节省。于是, android系统在空闲时, 会主动进入到休眠状态。
我们知道整个Android系统中运行着很多个进程, 因此必须有一种机制能够知道每个进程是否正在进行重要的工作, 只有这样Android系统才能对整个终端当前的状态做出判断。
显然我们不能启动一个进程, 去主动监管其它所有进程的工作状态, 这样CPU开销太大, 反而加剧了电量的消耗。为此Android引入了基于WakeLock的电量管理机制, 而PMS就是专门负责管理WakeLock的进程。
个人觉得WakeLock机制的思想, 有点类似于早期通信领域局域网中的令牌环机制。当局域网中有设备需要发送数据时, 需要申请令牌(Token), 申请到令牌才能发送数据; 设备发送完数据后, 再释放掉令牌。
与此相似, Android设备中运行的进程需要使用电量资源时, 也需要向PMS申请一个WakeLock; 当工作完成后, 就释放掉申请的WakeLock。PMS通过判断当前是否还有进程持有WakeLock, 就能得出系统是否空闲的结论。
从这里也可以看出, 当我们写一个永不停止工作的线程, 但不申请WakeLock时, 系统仍然可以休眠。在休眠时, CPU不会再耗费资源去调度该线程, 于是“永不停止工作”被打上了引号。
接下来, 我们就来看看PMS中的WakeLock。
一、创建WakeLock
我们以RIL.java为例, 看看一般情况下, PMS以外的其它进程如何使用WakeLock。
在RIL.java的构造函数中:

public RIL(Context context, int preferredNetworkType, int cdmaSubscription, Integer instanceId) { ............. PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); //获取WakeLock, 第一个参数决定了WakeLock的等级和flag mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_LOG_TAG); //默认WakeLocked会ReferenceCounted, 即一次申请对应一次释放 //设为false后, 一次释放就可以对应所有的申请 mWakeLock.setReferenceCounted(false); ........... //RIL.java中自己维护了WakeLockCount mWakeLockCount = 0; ........... }

从上面的代码, 可以看出调用PowerManager的newWakeLock函数, 可以创建出WakeLock。
我们看看newWakeLock函数定义:
public WakeLock newWakeLock(int levelAndFlags, String tag) { //检查参数有效性, 即levelAndFlags必须对应于PowerManager中定义的WakeLock级别和flag, tag不能为空 validateWakeLockParameters(levelAndFlags, tag); //此WakeLock为PowerManager定义的内部类 return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName()); }

1、WakeLock Level
newWakeLock中的第一个参数对应于WakeLock的级别和标志位构成的位图。
目前, 在PowerManger中一共为WakeLock定义了7种level。
/** * Wake lock level: Ensures that the CPU is running; the screen and keyboard * backlight will be allowed to go off. * * If the user presses the power button, then the screen will be turned off * but the CPU will be kept on until all partial wake locks have been released. * / public static final int PARTIAL_WAKE_LOCK = 0x00000001; /** * Wake lock level: Ensures that the screen is on (but may be dimmed); * the keyboard backlight will be allowed to go off. * * If the user presses the power button, then the SCREEN_DIM_WAKE_LOCK will be * implicitly released by the system, causing both the screen and the CPU to be turned off. */ @ Deprecated public static final int SCREEN_DIM_WAKE_LOCK = 0x00000006; /** * Wake lock level: Ensures that the screen is on at full brightness; * the keyboard backlight will be allowed to go off. * *If the user presses the power button, then the SCREEN_BRIGHT_WAKE_LOCK will be * implicitly released by the system, causing both the screen and the CPU to be turned off. */ @ Deprecated public static final int SCREEN_BRIGHT_WAKE_LOCK = 0x0000000a; /** * Wake lock level: Ensures that the screen and keyboard backlight are on at * full brightness. * *If the user presses the power button, then the FULL_WAKE_LOCK will be * implicitly released by the system, causing both the screen and the CPU to be turned off. */ @ Deprecated public static final int FULL_WAKE_LOCK = 0x0000001a; /** * Wake lock level: Turns the screen off when the proximity sensor activates. * If the proximity sensor detects that an object is nearby, the screen turns off * immediately.Shortly after the object moves away, the screen turns on again. * * A proximity wake lock does not prevent the device from falling asleep * unlike link FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK and SCREEN_DIM_WAKE_LOCK. * If there is no user activity and no other wake locks are held, then the device will fall asleep (and lock) as usual. * However, the device will not fall asleep while the screen has been turned off * by the proximity sensor because it effectively counts as ongoing user activity. * * Cannot be used with ACQUIRE_CAUSES_WAKEUP (WakeLock的flag). */ //例如拨号, 打通后接听电话, 屏幕变黑 public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020; /** * Wake lock level: Put the screen in a low power state and allow the CPU to suspend * if no other wake locks are held. * * This is used by the dream manager to implement doze mode.It currently * has no effect unless the power manager is in the dozing state. * / public static final int DOZE_WAKE_LOCK = 0x00000040; /** * Wake lock level: Keep the device awake enough to allow drawing to occur. * * This is used by the window manager to allow applications to draw while the * system is dozing.It currently has no effect unless the power manager is in * the dozing state. * / public static final int DRAW_WAKE_LOCK = 0x00000080;

从上面的代码注释可以看出, WakeLock主要用于控制CPU、屏幕和键盘三大部分( 当然, 现在的Anroid中基本没有键盘了) 。
对于PARTIAL_WAKE_LOCK、SCREEN_DIM_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK和FULL_WAKE_LOCK而言, 不考虑Power键的话, 随着等级的提高, 权限也相应增大, 即持有高等级的锁, 能够激活的部分越多; 如果考虑Power键的话, PARTIAL_WAKE_LOCK可以保证CPU不休眠, 反而是权限最大的。
PROXIMITY_SCREEN_OFF_WAKE_LOCK、DOZE_WAKE_LOCK和DRAW_WAKE_LOCK都是和具体场景相关的锁。
2、WakeLock Flag
PowerManager定义的WakeLock Flag很多, 无法一一列举, 就看一下比较常用的:
/** * Wake lock flag: Turn the screen on when the wake lock is acquired. * * Normally wake locks don' t actually wake the device, they just cause * the screen to remain on once it' s already on.Think of the video player * application as the normal behavior.Notifications that pop up and want * the device to be on are the exception; use this flag to be like them. * * Cannot be used with PARTIAL_WAKE_LOCK. * / public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000; /** * Wake lock flag: When this wake lock is released, poke the user activity timer * so the screen stays on for a little longer. * * Will not turn the screen on if it is not already on. * * Cannot be used with PARTIAL_WAKE_LOCK. * / public static final int ON_AFTER_RELEASE = 0x20000000; ..................

总结一下上面的内容, 如下所示:
Android7.0 PowerManagerService WakeLock的使用及流程

文章图片

WakeLock Flag一般与WakeLock Level组合使用, 使用的时候参照一下注释即可。
3、WakeLock的构造函数
最后, 我们来看看WakeLock的构造函数:
WakeLock(int flags, String tag, String packageName) { //level and flag mFlags = flags; //创建类对应的打印Tag mTag = tag; //创建类的类名 mPackageName = packageName; //创建一个Binder对象 //PMS将作为该Binder的客户端监听对应进程是否死亡 mToken = new Binder(); mTraceName = " WakeLock (" + mTag + " )" ; }

WakeLock的构造函数中需要注意的地方是, 创建了一个Binder对象。
回忆一下RIL.java中创建WakeLock的过程, 我们就知道这个Binder对象应该是创建在RIL.java所在的Phone进程中。
二、Acquire WakeLock
从上面的分析, 我们知道一个进程创建的WakeLock, 实际上表明了该进程执行某个工作时对电量的需求, 例如声明该工作需要保持屏幕处于点亮状态, 或该工作需要CPU处于唤醒态等。
因此, 进程创建了WakeLock后, 需要将WakeLock发送到PMS中, 让PMS明白该进程的需求。
这种将WakeLock通知到PMS的过程, 就被称为acquire WakeLock。
同样, 我们还是以RIL.java中的使用过程举例:
private void send(RILRequest rr) { Message msg; if (mSocket = = null) { rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); return; }msg = mSender.obtainMessage(EVENT_SEND, rr); //重点在这里 acquireWakeLock(rr, FOR_WAKELOCK); msg.sendToTarget(); }

当AP侧向modem发送请求时, 将要调用RIL.java的send函数。send函数将会发送消息给RILSender, 后者利用socket将消息发送给rild进程。
从上面的代码可以看出, 在发送消息给RILSender之前, 调用了acquireWakeLock函数:
private void acquireWakeLock(RILRequest rr, int wakeLockType) { synchronized(rr) { ............. switch(wakeLockType) { case FOR_WAKELOCK: synchronized (mWakeLock) { //调用acquire函数 mWakeLock.acquire(); mWakeLockCount+ + ; mWlSequenceNum+ + ; ............ } break; ......... } rr.mWakeLockType = wakeLockType; } }

我们跟进一下PowerManager中WakeLock的acquire函数:
public void acquire() { synchronized (mToken) { acquireLocked(); } }private void acquireLocked() { //前面已经提过, RIL.java中已经将mRefCounted置为false //如果不将mRefCounted置为false, 意味着acquire和release必须一一对应 //那么每个WakeLock只能acquire一次 if (!mRefCounted || mCount+ + = = 0) { ........ try { mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource, mHistoryTag); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } mHeld = true; } }

容易看出实际的工作流程将通过Binder通信进入到PMS中:
public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, WorkSource ws, String historyTag) { //参数和权限检查 ............ final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); final long ident = Binder.clearCallingIdentity(); try { acquireWakeLockInternal(lock, flags, tag, packageName, ws, historyTag, uid, pid); } finally { Binder.restoreCallingIdentity(ident); } }private void acquireWakeLockInternal(IBinder lock, int flags, String tag, String packageName, WorkSource ws, String historyTag, int uid, int pid) { synchronized (mLock) { ........... //PMS中也定义了WakeLock内部类 WakeLock wakeLock; //PMS中维持了一个ArrayList, 记录当前已申请的WakeLock //findWakeLockIndexLocked查找ArrayList, 判断参数对应的WakeLock, 是否在之前被申请过 int index = findWakeLockIndexLocked(lock); boolean notifyAcquire; if (index > = 0) { //如果index大于0, 说明此时Acquire的是一个旧的WakeLock //例如RIL会多次调用send函数, 于是除第一次外, 都会进入这个分支 wakeLock = mWakeLocks.get(index); //这是判断WakeLock对应的成员变量是否发生改变 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 { //创建一个新的WakeLock, 例如RIL第一次调用send就会进入该分支 wakeLock = new WakeLock(lock, flags, tag, packageName, ws, historyTag, uid, pid); try { //1、监控申请WakeLock的进程是否死亡 lock.linkToDeath(wakeLock, 0); } catch (RemoteException ex) { throw new IllegalArgumentException(" Wake lock is already dead." ); } //添加到wakelock列表 mWakeLocks.add(wakeLock); //2、特殊处理PARTIAL_WAKE_LOCK //实际上, 根据Doze模式的白名单更新wakelock的disabled变量 setWakeLockDisabledStateLocked(wakeLock); notifyAcquire = true; }//3、处理WakeLock对应的Flag //实际上判断WakeLock是否有ACQUIRE_CAUSES_WAKEUP, 在必要时唤醒屏幕 applyWakeLockFlagsOnAcquireLocked(wakeLock, uid); mDirty |= DIRTY_WAKE_LOCKS; //更新电源状态, 以后单独分析 updatePowerStateLocked(); if (notifyAcquire) { // This needs to be done last so we are sure we have acquired the // kernel wake lock.Otherwise we have a race where the system may // go to sleep between the time we start the accounting in battery // stats and when we actually get around to telling the kernel to // stay awake. //通知wakeLock发生变化 //电量统计服务做相关统计 notifyWakeLockAcquiredLocked(wakeLock); } } }

如上代码中标注的注释, acquireWakeLockInternal中有几处比较重要的地方, 我们一起来分析一下。
1、监听客户端进程死亡
上面的代码中, 第一次创建WakeLock后, 调用了:
......... lock.linkToDeath(wakeLock, 0); .........

我们将acquire WakeLock的进程定义为PMS的客户端进程, 那么上面代码的lock, 就是客户端进程中创建的Binder对象的代理。对于RIL而言, 就是存在于Phone进程中的Binder的代理。
PMS调用Binder代理的linkToDeath, 实际上使得PMS成为了对应进程Binder的客户端。于是, 当对应进程死亡后, 将通知PMS。
linkToDeath传入的必须是继承IBinder.DeathRecipient的对象, 作为进程死亡的”讣告”接收者。
我们看看PMS中WakeLock与此相关的定义:
private final class WakeLock implements IBinder.DeathRecipient { ........... @ Override public void binderDied() { //发现客户端进程死亡后, 调用PMS的handleWakeLockDeath进行处理, 传入的参数为WakeLock自己 PowerManagerService.this.handleWakeLockDeath(this); } ....... }

我们看看PMS的handleWakeLockDeath函数:
private void handleWakeLockDeath(WakeLock wakeLock) { synchronized (mLock) { .......... int index = mWakeLocks.indexOf(wakeLock); if (index < 0) { return; }removeWakeLockLocked(wakeLock, index); } }

跟进removeWakeLockLocked函数:
private void removeWakeLockLocked(WakeLock wakeLock, int index) { mWakeLocks.remove(index); //通知到BatteryStatsService notifyWakeLockReleasedLocked(wakeLock); //处理WakeLock对应的flag, 与后文applyWakeLockFlagsOnAcquireLocked一起分析 //实际上是判断是否需要立即息屏 applyWakeLockFlagsOnReleaseLocked(wakeLock); mDirty |= DIRTY_WAKE_LOCKS; //锁移除后, 还是利用updatePowerStateLocked更新电源状态 updatePowerStateLocked(); }

2、特殊处理PARTIAL_WAKE_LOCK
PMS处理第一次创建的WakeLock时, 还会调用setWakeLockDisabledStateLocked函数进行处理:
private boolean setWakeLockDisabledStateLocked(WakeLock wakeLock) { //仅会特殊处理PARTIAL_WAKE_LOCK, 毕竟PARTIAL_WAKE_LOCK要求按Power键后CPU依然可以工作 if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) = = PowerManager.PARTIAL_WAKE_LOCK) { boolean disabled = false; //设备处于Doze定义的device idle模式时 if (mDeviceIdleMode) { final int appid = UserHandle.getAppId(wakeLock.mOwnerUid); // If we are in idle mode, we will ignore all partial wake locks that are // for application uids that are not whitelisted.//判断是否为非系统应用 if (appid > = Process.FIRST_APPLICATION_UID & & //白名单search Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 & & Arrays.binarySearch(mDeviceIdleTempWhitelist, appid) < 0 & & //判断进程的类型 //ActivityManager中定义的数字最小的为: 常驻的操作UI的系统进程 //因此大概可理解为: 数字越大, 对处理事件的时效性要求越低 mUidState.get(wakeLock.mOwnerUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY) > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { disabled = true; } } if (wakeLock.mDisabled != disabled) { wakeLock.mDisabled = disabled; return true; } } return false; }

上面代码大致的含义就是:
在Android Doze模式下, 当终端处于device Idle Mode时,
对于一个非系统应用而言, 如果该应用不在系统定义的白名单中,
并且该应用所在进程的类型表明, 该进程对事件处理的时效性要求不高,
那么即使该应用申请了PARTIAL_WAKE_LOCK, 也不能阻止系统进入休眠状态。
有些设备商, 为了优化系统的功耗, 就修改了这个地方。
例如, 有些系统应用其实也很耗电, 因此可以去掉该函数中对非系统应用的限制, 对系统应用也进行管控。
3、处理WakeLock对应的Flag
前面的代码已经提到, 当acquire WakeLock时, 将调用applyWakeLockFlagsOnAcquireLocked处理WakeLock对应的flag;
当由于进程死亡, 释放WakeLock时, 会调用applyWakeLockFlagsOnReleaseLocked处理WakeLock对应的flag。
从函数命名来看, 这两个函数应该有相似的地方。
3.1 applyWakeLockFlagsOnAcquireLocked
我们先看看applyWakeLockFlagsOnAcquireLocked:
private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) { //仅处理ACQUIRE_CAUSES_WAKEUP flag, 同时要求WakeLock的level是与screen有关的, //即FULL_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK和SCREEN_DIM_WAKE_LOCK if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0 & & isScreenLock(wakeLock)) { .............. wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), wakeLock.mTag, opUid, opPackageName, opUid); } }private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid, String opPackageName, int opUid) { ............ //不满足以下条件, 没有唤醒屏幕的必要 if (eventTime < mLastSleepTime || mWakefulness = = WAKEFULNESS_AWAKE || !mBootCompleted || !mSystemReady) { return false; }try { mLastWakeTime = eventTime; //修改PMS的一些成员变量, 并进行通知 //其中主要的是将mDirty变量的DIRTY_WAKEFULNESS位置为了1 //PMS根据mDirty的位信息管理电源状态, 同时唤醒屏幕 setWakefulnessLocked(WAKEFULNESS_AWAKE, 0); //通知给电源统计服务 mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid); //调用userActivityNoUpdateLocked函数 userActivityNoUpdateLocked( eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid); } ..... return true; }

3.1.1 setWakefulnessLocked
我们看看唤醒屏幕相关的操作:
private void setWakefulnessLocked(int wakefulness, int reason) { if (mWakefulness != wakefulness) { mWakefulness = wakefulness; mWakefulnessChanging = true; mDirty |= DIRTY_WAKEFULNESS; //定义于frameworks/base/services/core/java/com/android/server/power/Notifier.java中 mNotifier.onWakefulnessChangeStarted(wakefulness, reason); } }

public void onWakefulnessChangeStarted(final int wakefulness, int reason) { final boolean interactive = PowerManagerInternal.isInteractive(wakefulness); ....... // Tell the activity manager about changes in wakefulness, not just interactivity. mHandler.post(new Runnable() { @ Override public void run() { mActivityManagerInternal.onWakefulnessChanged(wakefulness); } }); // Handle any early interactive state changes. // Finish pending incomplete ones from a previous cycle. if (mInteractive != interactive) { // Finish up late behaviors if needed. if (mInteractiveChanging) { handleLateInteractiveChange(); }// Start input as soon as we start waking up or going to sleep. mInputManagerInternal.setInteractive(interactive); mInputMethodManagerInternal.setInteractive(interactive); // Notify battery stats. try { mBatteryStats.noteInteractive(interactive); } catch (RemoteException ex) { }// Handle early behaviors. mInteractive = interactive; mInteractiveChangeReason = reason; mInteractiveChanging = true; //重点在这个位置 handleEarlyInteractiveChange(); } }/** * Handle early interactive state changes such as getting applications or the lock * screen running and ready for the user to see (such as when turning on the screen). */ private void handleEarlyInteractiveChange() { synchronized (mLock) { if (mInteractive) { // Waking up... mHandler.post(new Runnable() { @ Override public void run() { EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0); //mPolicy对应于PhoneWindowManager mPolicy.startedWakingUp(); } }); // Send interactive broadcast. mPendingInteractiveState = INTERACTIVE_STATE_AWAKE; mPendingWakeUpBroadcast = true; updatePendingBroadcastLocked(); } else { // Going to sleep... // Tell the policy that we started going to sleep. final int why = translateOffReason(mInteractiveChangeReason); mHandler.post(new Runnable() { @ Override public void run() { mPolicy.startedGoingToSleep(why); } }); } } }

从上面的代码来看, 应该是PhoneWindowManager完成亮屏前的初始化工作, 然后回调到PowerManager的wakeUp函数。
整个过程还是比较复杂的, 需要单独进行分析, 此处不做进一步说明。
3.2 applyWakeLockFlagsOnReleaseLocked
现在我们再看看applyWakeLockFlagsOnReleaseLocked函数:
private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) { //仅处理ON_AFTER_RELEASE, 同样要求WakeLock的level是与screen有关的 //ON_AFTER_RELEASE并不会立即息屏 if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0 & & isScreenLock(wakeLock)) { userActivityNoUpdateLocked(SystemClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER, PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS, wakeLock.mOwnerUid); } }

可以看出applyWakeLockFlagsOnAcquireLocked和applyWakeLockFlagsOnReleaseLocked最后均会调用userActivityNoUpdateLocked函数, 只是参数不同。
3.3 userActivityNoUpdateLocked
我们一起来看一下userActivityNoUpdateLocked:
private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) { ............. //过时的事件不需要处理 if (eventTime < mLastSleepTime || eventTime < mLastWakeTime || !mBootCompleted || !mSystemReady) { return false; }........... try { if (eventTime > mLastInteractivePowerHintTime) { //调用native加载的动态库的powerHint函数, 具体意义不是很清楚 powerHintInternal(POWER_HINT_INTERACTION, 0); mLastInteractivePowerHintTime = eventTime; }//调用BatteryStatsService的noteUserActivity函数, 看代码好像是做一些记录 mNotifier.onUserActivity(event, uid); //根据参数信息修改mDirty的一些变量 ............. } finally { ........ } }

从以上代码来看, acquire WakeLock将申请信息递交给PMS统一进行处理。
PMS根据WakeLock的level和flag, 完成修改一些变量、通知BatteryStatsService等工作后,
最终还是依赖于updatePowerStateLocked函数来进行实际的电源状态更新操作。
PMS类中有很多***NoUpdateLocked()方法, 这些方法都有一些共性, 就是仅更新状态, 不负责具体的执行。因为PMS中具体的执行逻辑都是在updatePowerStateLocked方法中。
上述acquire WakeLock主要的工作大致可以总结为下图:
Android7.0 PowerManagerService WakeLock的使用及流程

文章图片

三、释放WakeLock
当进程完成工作后, 需要释放之前申请的WakeLock。我们同样以RIL.java中的操作为例:
private void processResponse (Parcel p) { int type; type = p.readInt(); if (type = = RESPONSE_UNSOLICITED || type = = RESPONSE_UNSOLICITED_ACK_EXP) { ........... } else if (type = = RESPONSE_SOLICITED || type = = RESPONSE_SOLICITED_ACK_EXP) { //处理请求对应的回复信息 RILRequest rr = processSolicited (p, type); if (rr != null) { if (type = = RESPONSE_SOLICITED) { //重点在这里 decrementWakeLock(rr); } rr.release(); return; } } else if (type = = RESPONSE_SOLICITED_ACK) { ........... } }

我们跟进decrementWakeLock函数:
private void decrementWakeLock(RILRequest rr) { synchronized(rr) { switch(rr.mWakeLockType) { case FOR_WAKELOCK: synchronized (mWakeLock) { //前面已经提到过, RIL.java多个请求复用同一个WakeLock //并且利用mWakeLockCount记录复用的次数 //这么设计的目的是: RIL发送请求的数量非常多, 复用WakeLock可以避免多次构造释放 //同时减少与PMS之间Binder通信的次数 if (mWakeLockCount > 1) { mWakeLockCount--; } else { mWakeLockCount = 0; //所有请求均得到了处理, 调用PowerManager中WakeLock的release函数 mWakeLock.release(); } } break; ........ } } ........ }

现在我们跟进PowerManager中WakeLock定义的release函数:
/** * Releases the wake lock with flags to modify the release behavior. * * This method releases your claim to the CPU or screen being on. * The screen may turn off shortly after you release the wake lock, or it may * not if there are other wake locks still held. * */ public void release(int flags) { synchronized (mToken) { if (!mRefCounted || --mCount = = 0) { mHandler.removeCallbacks(mReleaser); if (mHeld) { ....... try { //还是会调用到PMS中的函数 mService.releaseWakeLock(mToken, flags); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } mHeld = false; }} .... } }

最后一起来看看PMS中释放WakeLock的函数:
public void releaseWakeLock(IBinder lock, int flags) { //参数和权限检查 ............. final long ident = Binder.clearCallingIdentity(); try { releaseWakeLockInternal(lock, flags); } finally { Binder.restoreCallingIdentity(ident); } }private void releaseWakeLockInternal(IBinder lock, int flags) { synchronized (mLock) { //根据Binder代理, 从存储的ArrayList中找到对应WakeLock的序号 int index = findWakeLockIndexLocked(lock); ........... WakeLock wakeLock = mWakeLocks.get(index); ........... //RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY, 表示当sensor判断终端离物体较远时, //才真正释放PROXIMITY_SCREEN_OFF_WAKE_LOCK等级的WakeLock if ((flags & PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY) != 0) { mRequestWaitForNegativeProximity = true; }//PMS不再关注客户端进程是否死亡 wakeLock.mLock.unlinkToDeath(wakeLock, 0); removeWakeLockLocked(wakeLock, index); } }private void removeWakeLockLocked(WakeLock wakeLock, int index) { mWakeLocks.remove(index); //通知BatteryStatsService notifyWakeLockReleasedLocked(wakeLock); //之前分析过, 会做一些记录信息等 applyWakeLockFlagsOnReleaseLocked(wakeLock); mDirty |= DIRTY_WAKE_LOCKS; //依然靠updatePowerStateLocked函数更新终端的电源状态 updatePowerStateLocked(); }

整个release的过程大致可以总结为下图:
Android7.0 PowerManagerService WakeLock的使用及流程

文章图片

四、总结
通过前面的分析, 我们知道了向PMS申请电量的基本用法类似于:
........ //1、创建 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_LOG_TAG); ...... //2、acquire mWakeLock.acquire(); ......... //3、release mWakeLock.release(); ...........

【Android7.0 PowerManagerService WakeLock的使用及流程】当申请发送到PMS后, PMS将针对WakeLock的level和flag信息进行一些处理。
无论是acquire还是release WakeLock, PMS最终将利用updatePowerStateLocked函数对终端的电源状态进行调整。
我们将单独分析一下PMS核心的updatePowerStateLocked函数。

    推荐阅读