前言
在开发中,或多或少会使用唤醒锁(wake lock),有的是为了保持屏幕长亮,有的是为了保持 CPU 运行。
唤醒锁的本质,其实是对屏幕状态的控制,以及对 CPU 挂起的控制。
屏幕状态的控制,指的是保持屏幕处于点亮的状态,或者直接唤醒屏幕,或者延长亮屏时间。
CPU 挂起的控制,指的是否阻止 CPU 挂起,如果阻止了 CPU 挂起,其实就是保持 CPU 运行。
本文重点分析唤醒锁是如何实现对屏幕状态的控制,以及对 CPU 挂起的控制。
本文仍以前面的三篇文章为基础,重复的过程不会分析,只会简要概述,因此请读者务必仔细阅读如下三篇文章
PowerManagerService之亮屏流程分析
PowerManagerService之手动灭屏
PowerManagerService之自动灭屏
使用唤醒锁
首先介绍下如何使用唤醒锁,如下
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
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);
}
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 唤醒锁的资料请关注编程网其它相关文章!