目录
前言
在开发中,或多或少会使用唤醒锁(wake lock),有的是为了保持屏幕长亮,有的是为了保持 CPU 运行。
唤醒锁的本质,其实是对屏幕状态的控制,以及对 CPU 挂起的控制。
屏幕状态的控制,指的是保持屏幕处于点亮的状态,或者直接唤醒屏幕,或者延长亮屏时间。
CPU 挂起的控制,指的是否阻止 CPU 挂起,如果阻止了 CPU 挂起,其实就是保持 CPU 运行。
本文重点分析唤醒锁是如何实现对屏幕状态的控制,以及对 CPU 挂起的控制。
本文仍以前面的三篇文章为基础,重复的过程不会分析,只会简要概述,因此请读者务必仔细阅读如下三篇文章
使用唤醒锁
首先介绍下如何使用唤醒锁,如下
PowerManager pm = mContext.getSystemService(PowerManager.class); // 1. 创建唤醒锁 // 保持屏幕处于点亮状态,但是允许变暗 PowerManager.WakeLock wl = pm.newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG); // 2. 获取唤醒锁 wl.acquire(); // ... 执行任务 ... // 3. 释放唤醒锁 wl.release();
使用唤醒锁的步骤为
- 创建唤醒锁
- 获取唤醒锁
- 在不需要唤醒锁的时候,释放它。
注意,使用唤醒时,还需要在 AndroidManifest.xml 中声明权限 android.Manifest.permission.WAKE_LOCK。
创建唤醒锁
首先介绍下创建唤醒锁的API
// PowerManager.java public WakeLock newWakeLock(int levelAndFlags, String tag) { validateWakeLockParameters(levelAndFlags, tag); return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName(), Display.INVALID_DISPLAY); }
参数 levelAndFlags 是由 level 和 flag 以按位或的方式组成,其中必须指定一个 level,但是 flag 是可选的。
第三方 app 能使用的 level 有如下几个
level | 描述 |
---|---|
PARTIAL_WAKE_LOCK | 保证 CPU 运行,但是屏幕和键盘背光可以关闭 |
FULL_WAKE_LOCK | 保证屏幕和键盘背光处于最大亮度 |
SCREEN_DIM_WAKE_LOCK | 确保屏幕处于点亮状态,但是可以变暗,键盘背光允许关闭 |
SCREEN_BRIGHT_WAKE_LOCK | 确认屏幕处于最大亮度,但是键盘背光允许关闭 |
PROXIMITY_SCREEN_OFF_WAKE_LOCK | 当距离传感器检测到物体靠近时,灭屏,检测到物体远离时,点亮屏幕 |
注意,FULL_WAKE_LOCK 、SCREEN_DIM_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK 不仅会使屏幕处于点亮状态,同时也会保持 CPU 处于运行状态,我们将在后面的分析得到验证。
第三方 app 能使用的 flag 有如下几个
flag | 描述 |
---|---|
ACQUIRE_CAUSES_WAKEUP | 当唤醒锁被获取时,点亮屏幕 |
ON_AFTER_RELEASE | 当唤醒锁被释放时,如果屏幕处于点亮的状态,那么延长亮屏的时间 |
注意,ACQUIRE_CAUSES_WAKEUP 和 ON_AFTER_RELEASE 要配合屏幕唤醒锁 FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK, SCREEN_DIM_WAKE_LOCK一起使用。我们将在后面的分析得到验证。
这里介绍的 level 和 flag 只适用于第三方 app 使用,其实系统还定义了一些,用于完成特殊的功能。
参数 tag,名字其实可以随意,但是官方说,最好以 app:mytag
的方式命名,例如 gmail:mytag
。
参数介绍完了,我现在想提另外一个话题,与多屏相关。 不知从何时起,Android 把多屏进行了分组,内置的屏幕是在默认的分组中。PowerManager#newWakeLock(int levelAndFlags, String tag) 这个 API 会作用于所有的屏幕分组,但是如果我们想指定某组显示屏呢,那么需要使用下面的 API,但是它是系统 API
// PowerManager.java /** * @hide */ public WakeLock newWakeLock(int levelAndFlags, String tag, int displayId) { validateWakeLockParameters(levelAndFlags, tag); return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName(), displayId); }
参数 displayId 其实应该叫做 display group id,它表示唤醒锁作用于指定分组显示屏。
现在看下 WakeLock 的构造函数
// PowerManager.java WakeLock(int flags, String tag, String packageName, int displayId) { mFlags = flags; mTag = tag; mPackageName = packageName; mToken = new Binder(); mTraceName = "WakeLock (" + mTag + ")"; mDisplayId = displayId; }
构造函数就是简单保存几个参数,但是有一点需要注意,mToken 是一个 Binder,它会传给服务端 PowerManagerService,服务端会注册它的死亡事件。那么这个 Binder 对象其实就是为了监控服务端进程的生死。这个技术大家要学会,我曾经用这个技术优化过自己写的服务端代码。
获取唤醒锁
// PowerManager.java public void acquire() { synchronized (mToken) { acquireLocked(); } } public void acquire(long timeout) { synchronized (mToken) { acquireLocked(); // 发送一个延时消息,自动释放唤醒锁 mHandler.postDelayed(mReleaser, timeout); } } private void acquireLocked() { mInternalCount++; mExternalCount++; // mRefCounted 默认为 true,它表示对唤醒锁引用计数 if (!mRefCounted || mInternalCount == 1) { mHandler.removeCallbacks(mReleaser); Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0); try { mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource, mHistoryTag, mDisplayId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } mHeld = true; } }
获取唤醒锁时,可以指定一个超时时间,如果时间到了,唤醒锁还没有释放,那么会自动释放唤醒锁。
默认的情况下,唤醒锁是计数的。如果多次获取唤醒锁,需要进行相应次数的释放。
而如果通过 wakeLock.setReferenceCounted(false)
设置唤醒锁为不计数
// PowerManager.java public void setReferenceCounted(boolean value) { synchronized (mToken) { mRefCounted = value; } }
那么多次获取唤醒锁后,只需要释放一次。
现在让我们看下服务端 PowerManagerService 是如何获取唤醒锁的
// PowerManagerService.java public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, WorkSource ws, String historyTag, int displayId) { // ... 省略权限检测 try { acquireWakeLockInternal(lock, displayId, flags, tag, packageName, ws, historyTag, uid, pid); } finally { Binder.restoreCallingIdentity(ident); } } private void acquireWakeLockInternal(IBinder lock, int displayId, 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) { // 唤醒锁已经存在 // ... } else { // 唤醒锁不存在 // mUidState 由 ActivityManagerService 同步给 PowerManagerService // UidState 代表一个 app 进程的状态 UidState state = mUidState.get(uid); if (state == null) { state = new UidState(uid); state.mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT; mUidState.put(uid, state); } // 保存唤醒锁的数量 state.mNumWakeLocks++; // 1. 创建唤醒锁 wakeLock = new WakeLock(lock, displayId, flags, tag, packageName, ws, historyTag, uid, pid, state); try { // 2. 监听客户端进程的死亡 // 当客户端进程死亡时,释放它所申请的唤醒锁 lock.linkToDeath(wakeLock, 0); } catch (RemoteException ex) { throw new IllegalArgumentException("Wake lock is already dead."); } // 3. 保存唤醒锁 mWakeLocks.add(wakeLock); // 4. 更新唤醒锁 PowerManager.PARTIAL_WAKE_LOCK 的 disable 状态 setWakeLockDisabledStateLocked(wakeLock); notifyAcquire = true; } // 5. 处理 PowerManager.ACQUIRE_CAUSES_WAKEUP 唤醒锁亮屏的情况 applyWakeLockFlagsOnAcquireLocked(wakeLock, uid); // 6. 标记唤醒锁已经改变 mDirty |= DIRTY_WAKE_LOCKS; // 7. 更新电源状态 updatePowerStateLocked(); if (notifyAcquire) { // 记录唤醒锁 notifyWakeLockAcquiredLocked(wakeLock); } } }
先大致了解下,首次向 PowerManagerService 申请唤醒锁的过程
- 创建服务端的 WakeLock。
- 监听客户端传递过来的 Binder 的死亡事件,其实就是监听客户端进程的死亡。当客户端进程死亡时,释放它所申请的唤醒锁。
- PowerManagerService 使用 ArrayList< WakeLock > mWakeLocks 保存创建的唤醒锁。
更新唤醒锁 PowerManager.PARTIAL_WAKE_LOCK 的 disable 状态,因此有些情况下,是不允许获取这种唤醒锁的,这些特殊情况如下
private boolean setWakeLockDisabledStateLocked(WakeLock wakeLock) { if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) == PowerManager.PARTIAL_WAKE_LOCK) { boolean disabled = false; final int appid = UserHandle.getAppId(wakeLock.mOwnerUid); if (appid >= Process.FIRST_APPLICATION_UID) { // Cached inactive processes are never allowed to hold wake locks. // 1. 缓存的不活跃的进程的唤醒锁需要disable if (mConstants.NO_CACHED_WAKE_LOCKS) { disabled = mForceSuspendActive || (!wakeLock.mUidState.mActive && wakeLock.mUidState.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT && wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER); } if (mDeviceIdleMode) { // 2. idle 模式下,不处理白名单的进程的唤醒锁,也需要 disable final UidState state = wakeLock.mUidState; if (Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 && Arrays.binarySearch(mDeviceIdleTempWhitelist, appid) < 0 && state.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT && state.mProcState > ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { disabled = true; } } } // 3. 更新唤醒锁的 disable 状态 if (wakeLock.mDisabled != disabled) { wakeLock.mDisabled = disabled; return true; } } return false; }
处理 PowerManager.ACQUIRE_CAUSES_WAKEUP 唤醒锁亮屏的情况。
private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) { // 注意,PowerManager.ACQUIRE_CAUSES_WAKEUP 要与如下几个屏幕锁一起使用才有效 // PowerManager.FULL_WAKE_LOCK // PowerManager.SCREEN_BRIGHT_WAKE_LOCK // PowerManager.SCREEN_DIM_WAKE_LOCK if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0 && isScreenLock(wakeLock)) { String opPackageName; int opUid; if (wakeLock.mWorkSource != null && !wakeLock.mWorkSource.isEmpty()) { // ... } else { opPackageName = wakeLock.mPackageName; opUid = wakeLock.mOwnerUid; } for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { // 更新 wakefulness 为 WAKEFULNESS_AWAKE wakeDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, opUid, opPackageName, opUid); } } }
注意,PowerManager.ACQUIRE_CAUSES_WAKEUP 是要下屏幕锁一直使用,屏幕锁为 PowerManager.FULL_WAKE_LOCK、PowerManager.SCREEN_BRIGHT_WAKE_LOCK、PowerManager.SCREEN_DIM_WAKE_LOCK。很显然,这些屏幕锁,都是保持屏幕处于点亮的状态。
根据前面的文章,wakeDisplayGroupNoUpdateLocked() 其实就是更新 wakefulness 为 WAKEFULNESS_AWAKE。当后面更新电源状态时,会向 DisplayManagerService 发起屏幕请示,从而进行亮屏。这个过程,请读者参考前面的文章,自行分析。
- 标记唤醒锁已经改变。
- 更新电源状态,根据 mDirty 处理唤醒锁的改变 。
现在来看下最后一步,更新电源状态
// PowerManagerService.java private void updatePowerStateLocked() { if (!mSystemReady || mDirty == 0) { return; } // 注意这里的技术,线程可以判断是否获取了某个锁 if (!Thread.holdsLock(mLock)) { Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked"); } Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState"); try { // Phase 0: Basic state updates. updateIsPoweredLocked(mDirty); updateStayOnLocked(mDirty); updateScreenBrightnessBoostLocked(mDirty); // Phase 1: Update wakefulness. // Loop because the wake lock and user activity computations are influenced // by changes in wakefulness. final long now = mClock.uptimeMillis(); int dirtyPhase2 = 0; for (;;) { int dirtyPhase1 = mDirty; dirtyPhase2 |= dirtyPhase1; mDirty = 0; // 1. 归纳唤醒锁 updateWakeLockSummaryLocked(dirtyPhase1); // 更新用户行为 updateUserActivitySummaryLocked(now, dirtyPhase1); updateAttentiveStateLocked(now, dirtyPhase1); if (!updateWakefulnessLocked(dirtyPhase1)) { break; } } // Phase 2: Lock profiles that became inactive/not kept awake. updateProfilesLocked(now); // Phase 3: Update display power state. // 2. 更新显示屏的电源状态 final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2); // Phase 4: Update dream state (depends on display ready signal). updateDreamLocked(dirtyPhase2, displayBecameReady); // Phase 5: Send notifications, if needed. finishWakefulnessChangeIfNeededLocked(); // Phase 6: Update suspend blocker. // Because we might release the last suspend blocker here, we need to make sure // we finished everything else first! // 3. 唤醒锁保持 CPU 运行 updateSuspendBlockerLocked(); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } }
与唤醒锁相关的主要流程如下
- 归纳唤醒锁。这个过程会把所有的唤醒锁整合到一起,它会影响请求策略,也就是会影响屏幕最终状态。并用它也会决定是否阻止CPU挂起,也就是是否保持CPU运行。详见【归纳唤醒锁】
- 更新电源状态。根据前面的文章可知,屏幕的最终状态是由请求的策略所决定的,而唤醒锁可以响应策略。详见【更新请求策略】
- 如果有唤醒锁需要保证 CPU 运行,那么 PMS 会向底层获取锁,保证 CPU 运行。详见【唤醒锁保持 CPU 运行】
归纳唤醒锁
private void updateWakeLockSummaryLocked(int dirty) { if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS | DIRTY_DISPLAY_GROUP_WAKEFULNESS)) != 0) { //1. wake lock summary 清 0 mWakeLockSummary = 0; final int numProfiles = mProfilePowerState.size(); for (int i = 0; i < numProfiles; i++) { mProfilePowerState.valueAt(i).mWakeLockSummary = 0; } for (int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { mDisplayGroupPowerStateMapper.setWakeLockSummaryLocked(groupId, 0); } // 2. 获取 wake lock summary int invalidGroupWakeLockSummary = 0; final int numWakeLocks = mWakeLocks.size(); // 遍历所有的 WakeLock for (int i = 0; i < numWakeLocks; i++) { final WakeLock wakeLock = mWakeLocks.get(i); final Integer groupId = wakeLock.getDisplayGroupId(); if (groupId == null) { continue; } // 把 PowerManager 定义的 WakeLock flag 转化为 PowerManagerService 定义的 WakeLock flag final int wakeLockFlags = getWakeLockSummaryFlags(wakeLock); // 更新 PMS 的 mWakeLockSummary mWakeLockSummary |= wakeLockFlags; if (groupId != Display.INVALID_DISPLAY_GROUP) { int wakeLockSummary = mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked( groupId); wakeLockSummary |= wakeLockFlags; mDisplayGroupPowerStateMapper.setWakeLockSummaryLocked(groupId, wakeLockSummary); } else { // 没有指定 group id 的唤醒锁,保存到 invalidGroupWakeLockSummary invalidGroupWakeLockSummary |= wakeLockFlags; } for (int j = 0; j < numProfiles; j++) { // ... } } // 遍历所有 WakeLock 结束 // 3. 调整的 wake lock summary for (int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { // 从这里可以看出,invalidGroupWakeLockSummary 应用到了所有的 display group 中 // 因此,在获取 WakeLock 没有指定 group id 时,这个 WakeLock 是应用到所有的 display group 上 final int wakeLockSummary = adjustWakeLockSummaryLocked( mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId), invalidGroupWakeLockSummary | mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId)); mDisplayGroupPowerStateMapper.setWakeLockSummaryLocked(groupId, wakeLockSummary); } mWakeLockSummary = adjustWakeLockSummaryLocked(getWakefulnessLocked(), mWakeLockSummary); for (int i = 0; i < numProfiles; i++) { // ... } } }
这里的逻辑很清晰,其实就是遍历所有的唤醒锁,然后归纳保存到 mWakeLockSummary。当然这其中有几个重要的函数需要搞清楚
通过 getWakeLockSummaryFlags() 把 PowerManager 定义的唤醒锁转化为 PowerManagerService 定义的唤醒锁
private int getWakeLockSummaryFlags(WakeLock wakeLock) { switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { // 这个唤醒锁用于保持 CPU 运行 case PowerManager.PARTIAL_WAKE_LOCK: // disabled 状态的唤醒锁,是不能保证 CPU 运行的 if (!wakeLock.mDisabled) { return WAKE_LOCK_CPU; } break; // 以下三个唤醒锁用于保持屏幕处于点亮状态 case PowerManager.FULL_WAKE_LOCK: return WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT; case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: return WAKE_LOCK_SCREEN_BRIGHT; case PowerManager.SCREEN_DIM_WAKE_LOCK: return WAKE_LOCK_SCREEN_DIM; // 用距离传感器进行灭屏、亮屏 case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: return WAKE_LOCK_PROXIMITY_SCREEN_OFF; // PowerManager.DOZE_WAKE_LOCK 由 DreamManagerService 获取 // 它使设备真正进入打盹状态 case PowerManager.DOZE_WAKE_LOCK: return WAKE_LOCK_DOZE; // 由 window manager 获取,允许应用在系统doze状态下能够绘制 case PowerManager.DRAW_WAKE_LOCK: return WAKE_LOCK_DRAW; } return 0; }
通过 adjustWakeLockSummaryLocked() 调整归纳的唤醒锁
private static int adjustWakeLockSummaryLocked(int wakefulness, int wakeLockSummary) { // 系统处于 非doze 状态,PowerManager.DOZE_WAKE_LOCK 和 PowerManager.DRAW_WAKE_LOCK 无效 // 看来,这两个锁只有当系统处于 doze 状态,才有效果 if (wakefulness != WAKEFULNESS_DOZING) { wakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW); } // 系统处于休眠或者doze状态下,如下三个保持屏幕点亮状态的锁是无效的 // PowerManager.FULL_WAKE_LOCK // PowerManager.SCREEN_BRIGHT_WAKE_LOCK // PowerManager.SCREEN_DIM_WAKE_LOCK if (wakefulness == WAKEFULNESS_ASLEEP || (wakeLockSummary & WAKE_LOCK_DOZE) != 0) { wakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM | WAKE_LOCK_BUTTON_BRIGHT); // 甚至,当系统处于休眠状态,PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK 无法唤醒屏幕 if (wakefulness == WAKEFULNESS_ASLEEP) { wakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF; } } // 如下三个保持屏幕点亮状态的锁 // PowerManager.FULL_WAKE_LOCK // PowerManager.SCREEN_BRIGHT_WAKE_LOCK // PowerManager.SCREEN_DIM_WAKE_LOCK // 当系统处于唤醒状态或者屏保状态,这两个其实都是亮屏状态 // 需要保证 CPU 运行,也就是下面添加的 WAKE_LOCK_CPU // 并且系统处于唤醒状态时,还要屏幕长亮,这正好符合上面三个唤醒锁的定义 if ((wakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0) { // 并且如果系统处理唤醒状态,还要保持长亮 if (wakefulness == WAKEFULNESS_AWAKE) { wakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_STAY_AWAKE; } else if (wakefulness == WAKEFULNESS_DREAMING) { // 系统处于屏保状态 wakeLockSummary |= WAKE_LOCK_CPU; } } // 系统处于 doze 状态,PowerManager.DRAW_WAKE_LOCK 需要保持 CPU 运行 if ((wakeLockSummary & WAKE_LOCK_DRAW) != 0) { wakeLockSummary |= WAKE_LOCK_CPU; } return wakeLockSummary; }
调整归纳的唤醒锁,其实就是针对系统处于不同的状态,去掉一些不兼容的唤醒锁或者添加一些合适的锁。
例如,前面说过,PowerManager.FULL_WAKE_LOCK、PowerManager.SCREEN_BRIGHT_WAKE_LOCK、PowerManager.SCREEN_DIM_WAKE_LOCK 不仅仅要保持屏幕的亮度,而且还要保持 CPU 运行,这里就可以看出端倪。
更新请求策略
通过前面的文章可知,屏幕最终的状态是通过请求策略控制的,函数如下
int getDesiredScreenPolicyLocked(int groupId) { final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); final int wakeLockSummary = mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId); if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) { // 1. 系统处于休眠状态,任何唤醒锁都不起作用,屏幕会进入关闭状态 return DisplayPowerRequest.POLICY_OFF; } else if (wakefulness == WAKEFULNESS_DOZING) { // 2. 系统处于 doze 状态,PowerManager.DRAW_WAKE_LOCK 会让屏幕进入 doze 状态 // 当 dream manager 成功启动 doze dream,才会获取 PowerManager.DRAW_WAKE_LOCK,此时系统才真正进入 doze 状态 if ((wakeLockSummary & WAKE_LOCK_DOZE) != 0) { return DisplayPowerRequest.POLICY_DOZE; } if (mDozeAfterScreenOff) { return DisplayPowerRequest.POLICY_OFF; } } if (mIsVrModeEnabled) { return DisplayPowerRequest.POLICY_VR; } // 下面处理的是系统处于唤醒和屏保状态,都是亮屏的状态 // 3. PowerManager.FULL_WAKE_LOCK 和 PowerManager.SCREEN_BRIGHT_WAKE_LOCK 会 // 让屏幕处于亮屏状态 if ((wakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0 || !mBootCompleted || (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId) & USER_ACTIVITY_SCREEN_BRIGHT) != 0 || mScreenBrightnessBoostInProgress) { return DisplayPowerRequest.POLICY_BRIGHT; } // 4. PowerManager.SCREEN_DIM_WAKE_LOCK 允许屏幕变暗 // 当屏幕快要超时时,会进入变暗的状态,此时持有 PowerManager.SCREEN_DIM_WAKE_LOCK 会保持屏幕 // 一直处于 dim 状态 return DisplayPowerRequest.POLICY_DIM; }
从获取请求策略的过程,我们可以看到,当系统处于不同的状态,不同的唤醒锁,是如何影响屏幕状态的。
例如,PowerManager.FULL_WAKE_LOCK 和 PowerManager.SCREEN_BRIGHT_WAKE_LOCK 会保证屏幕一直处于亮屏状态,而 PowerManager.SCREEN_DIM_WAKE_LOCK 会保证屏幕也处于亮屏状态,但是允许变暗。
唤醒锁保持 CPU 运行
private void updateSuspendBlockerLocked() { // 1. 检测是否有唤醒锁需要保持 CPU 运行 final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0); // 2. 检测屏幕的某些状态是否需要保持 CPU 运行 final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked(); final boolean autoSuspend = !needDisplaySuspendBlocker; final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked(); boolean interactive = false; for (int id : groupIds) { interactive |= mDisplayGroupPowerStateMapper.getPowerRequestLocked(id).isBrightOrDim(); } // mDecoupleHalAutoSuspendModeFromDisplayConfig 默认为 false if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) { setHalAutoSuspendModeLocked(false); } // 3. 向底层获取锁,保证 CPU 运行 // First acquire suspend blockers if needed. if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) { mWakeLockSuspendBlocker.acquire(); mHoldingWakeLockSuspendBlocker = true; } if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) { mDisplaySuspendBlocker.acquire(); mHoldingDisplaySuspendBlocker = true; } // mDecoupleHalInteractiveModeFromDisplayConfig 默认为 false if (mDecoupleHalInteractiveModeFromDisplayConfig) { // ... } // 下面表示没有对应的唤醒锁,就需要向底层释放锁 if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) { mWakeLockSuspendBlocker.release(); mHoldingWakeLockSuspendBlocker = false; } if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) { mDisplaySuspendBlocker.release(); mHoldingDisplaySuspendBlocker = false; } // mDecoupleHalAutoSuspendModeFromDisplayConfig 默认为 flase if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) { setHalAutoSuspendModeLocked(true); } }
如下几个锁会保持 CPU 运行
- PowerManager.PARTIAL_WAKE_LOCK(缓存的后台进程 或 idle模式下,不处于白名单的进程,唤醒锁无效)
- PowerManager.FULL_WAKE_LOCK(系统处于唤醒或屏保状态)
- PowerManager.SCREEN_BRIGHT_WAKE_LOCK(系统处于唤醒或屏保状态)
- PowerManager.SCREEN_DIM_WAKE_LOCK(系统处于唤醒或屏保状态)
- PowerManager.DOZE_WAKE_LOCK(系统处于doze状态)
- PowerManager.DRAW_WAKE_LOCK(系统处于doze状态)
屏幕的几种状态也需要保持 CPU 运行,请看下面代码所展示的所有情况
private boolean needDisplaySuspendBlockerLocked() { // 1. DisplayManagerService 正在处理请求, 需要保持 CPU 运行 if (!mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { return true; } // 2. 屏幕亮度正在增强中,需要保持 CPU 运行 if (mScreenBrightnessBoostInProgress) { return true; } // When we transition to DOZING, we have to keep the display suspend blocker // up until the Doze service has a change to acquire the DOZE wakelock. // Here we wait for mWakefulnessChanging to become false since the wakefulness // transition to DOZING isn't considered "changed" until the doze wake lock is // acquired. // 3. doze状态的转换中,需要保持 CPU 运行 if (getWakefulnessLocked() == WAKEFULNESS_DOZING && mDozeStartInProgress) { return true; } final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked(); for (int id : groupIds) { final DisplayPowerRequest displayPowerRequest = mDisplayGroupPowerStateMapper.getPowerRequestLocked(id); // 3. 亮屏状态下,需要保持 CPU 运行 // 屏幕处于点亮或者变暗的状态,都是亮屏的状态 if (displayPowerRequest.isBrightOrDim()) { // If we asked for the screen to be on but it is off due to the proximity // sensor then we may suspend but only if the configuration allows it. // On some hardware it may not be safe to suspend because the proximity // sensor may not be correctly configured as a wake-up source. if (!displayPowerRequest.useProximitySensor || !mProximityPositive || !mSuspendWhenScreenOffDueToProximityConfig) { return true; } } // 4. 系统真正处于 doze 状态,也需要保持 CPU 运行 // 因此需要在屏幕绘制一些东西,例如时间 if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE && displayPowerRequest.dozeScreenState == Display.STATE_ON) { // Although we are in DOZE and would normally allow the device to suspend, // the doze service has explicitly requested the display to remain in the ON // state which means we should hold the display suspend blocker. return true; } } // Let the system suspend if the screen is off or dozing. return false; }
为何屏幕的状态有时候也需要 CPU 保持运行?举个最简单的例子,如果处于亮屏状态,CPU 允许挂起的话,app 进程就无法运行了。
释放锁
// PowerManager.java public void release() { release(0); } /** * Releases the wake lock with flags to modify the release behavior. * <p> * 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. * </p> * * @param flags Combination of flag values to modify the release behavior. * Currently only {@link #RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY} is supported. * Passing 0 is equivalent to calling {@link #release()}. */ public void release(int flags) { synchronized (mToken) { if (mInternalCount > 0) { // internal count must only be decreased if it is > 0 or state of // the WakeLock object is broken. mInternalCount--; } if ((flags & RELEASE_FLAG_TIMEOUT) == 0) { mExternalCount--; } if (!mRefCounted || mInternalCount == 0) { mHandler.removeCallbacks(mReleaser); if (mHeld) { Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0); try { mService.releaseWakeLock(mToken, flags); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } mHeld = false; } } if (mRefCounted && mExternalCount < 0) { throw new RuntimeException("WakeLock under-locked " + mTag); } } }
从这里我们可以验证前面说的一个结论,mRefCounted 默认为 true,表示唤醒锁是引用计数的,如果多少获取唤醒锁,需要释放相应次数的唤醒锁。如果不计数,那么只需要释放一次。
现在看下服务端 PowerManagerService 是如何释放锁的
public void releaseWakeLock(IBinder lock, int flags) { if (lock == null) { throw new IllegalArgumentException("lock must not be null"); } // 需要 android.Manifest.permission.WAKE_LOCK 权限 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); final long ident = Binder.clearCallingIdentity(); try { releaseWakeLockInternal(lock, flags); } finally { Binder.restoreCallingIdentity(ident); } } private void releaseWakeLockInternal(IBinder lock, int flags) { synchronized (mLock) { // 1. 找到服务端保存的唤醒锁 int index = findWakeLockIndexLocked(lock); if (index < 0) { return; } WakeLock wakeLock = mWakeLocks.get(index); // 延迟释放 PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK // 直到距离传感器检测到物体远离 if ((flags & PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY) != 0) { mRequestWaitForNegativeProximity = true; } // 不再监听客户端进程的死亡 wakeLock.mLock.unlinkToDeath(wakeLock, 0); // 移除唤醒锁 removeWakeLockLocked(wakeLock, index); } } private void removeWakeLockLocked(WakeLock wakeLock, int index) { // 2. 从数据结构中移除唤醒锁 mWakeLocks.remove(index); // 进程状态中减少唤醒锁的数量 UidState state = wakeLock.mUidState; state.mNumWakeLocks--; if (state.mNumWakeLocks <= 0 && state.mProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) { mUidState.remove(state.mUid); } // 记录唤醒锁被释放的数据 notifyWakeLockReleasedLocked(wakeLock); // 3. 处理 PowerManager.ON_AFTER_RELEASE // 带有这个 flag 的锁,在释放的时候,会更新用户行为时间,从而可以延长亮屏的时间 applyWakeLockFlagsOnReleaseLocked(wakeLock); // 4.标记唤醒已经改变,并更新电源状态 mDirty |= DIRTY_WAKE_LOCKS; updatePowerStateLocked(); }
PowerManagerService 移除唤醒锁的过程一般如下
- 从数据结构中移除。
- 处于带有 PowerManager.ON_AFTER_RELEASE 这个 flag 的唤醒锁。在释放带有这个 flag 的唤醒锁的时候,会更新用户行为时间,从而可以延长亮屏的时间。
- 标记唤醒锁已经改变,更新电源状态。
距离传感器锁的原理,在看完本文后,大家可以自行分析。
现在来看下,释放带有 PowerManager.ON_AFTER_RELEASE 的唤醒锁,是如何延长亮屏的时间的
private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) { // PowerManager.ON_AFTER_RELEASE 必须与如下的屏幕锁一起使用 // PowerManager.FULL_WAKE_LOCK // PowerManager.SCREEN_BRIGHT_WAKE_LOCK // PowerManager.SCREEN_DIM_WAKE_LOCK if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0 && isScreenLock(wakeLock)) { userActivityNoUpdateLocked(mClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER, PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS, wakeLock.mOwnerUid); } } private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) { boolean updatePowerState = false; // 注意,PowerManager.ON_AFTER_RELEASE 影响了所有的 display group 的 用户行为时间 // 那么也说明,它会导致所有的屏幕延长亮屏的时间 for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { if (userActivityNoUpdateLocked(id, eventTime, event, flags, uid)) { updatePowerState = true; } } return updatePowerState; } // PowerManagerService.java private boolean userActivityNoUpdateLocked(int groupId, long eventTime, int event, int flags, int uid) { // ... try { // ... if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) { // ... } else { if (eventTime > mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked( groupId)) { // 记录用户行为的时间 mDisplayGroupPowerStateMapper.setLastUserActivityTimeLocked(groupId, eventTime); // 标记用户活动有改变 mDirty |= DIRTY_USER_ACTIVITY; if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) { mDirty |= DIRTY_QUIESCENT; } return true; } } } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } return false; }
处理 PowerManager.ON_AFTER_RELEASE 的过程,我们要注意以下几点事情
- PowerManager.ON_AFTER_RELEASE 必须要与屏幕唤醒锁 PowerManager.FULL_WAKE_LOCK, PowerManager.SCREEN_BRIGHT_WAKE_LOCK, PowerManager.SCREEN_DIM_WAKE_LOCK 一起使用.
- PowerManager.ON_AFTER_RELEASE 更新了所有屏幕分组的用户行为时间,也就是说最终会导致所有屏幕都延长亮屏的时间。
- 更新用户行为时间,根据 PowerManagerService之自动灭屏 可知,用户行为时间的更新,最终会导致延长亮屏的时间
结束
通过本文的分析,我们可以看到唤醒锁是如何控制屏幕状态,以及如何保持CPU运行。但是本文写的比较简洁,是因为很多东西已经在前文分析过了,如果读者看本文的时候,有点压力,不妨再回头看看前面的文章。
PowerManagerService 系列的文章,就此结束。虽然还有一些功能我并未分析,但是我写的这些文章都是基础,只要掌握基础,其它功能的分析,岂不是信手拈来。
以上就是PowerManagerService之唤醒锁的使用获取创建示例解析的详细内容,更多关于PowerManagerService 唤醒锁的资料请关注其它相关文章!