WakeLock 锁机制

1、WakeLock锁简介

WakeLock是Android系统中一种锁的机制,当进程持有这个锁,系统则不会进入休眠状态。在申请WakeLock锁时,需要在AndroidManifest.xml文件中配置android.Manifest.permission.WAKE_LOCK 权限。

WakeLock锁分类:

  • 根据作用时间,WakeLock可以分为永久锁和超时锁。

    永久锁:在获取了WakeLock锁后必须显式的释放,否则系统会一直持有该锁。

    超时锁:在到达给定时间后会自动释放WakeLock锁。

  • 根据释放原则,WakeLock可以分为计数锁和非计数锁,默认为计数锁。

    计数锁:一次申请必须对应一次释放。

    非计数锁:不管申请多少次,一次就可以释放该WakeLock。

2、WakeLock的等级

WakeLock具有以下七种等级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//如果持有该类型的 wakelock 锁,则按 Power 键灭屏后,即使允许屏幕、按键灯灭,也不会释放该锁,CPU 不会进入休眠状态
public static final int PARTIAL_WAKE_LOCK;

//Deprecated,如果持有该类型的 wakelock 锁,即使屏幕保持亮/ Dim 的状态,键盘灯允许灭,按 Power 键灭屏后,会立即释放,CPU不会进入休眠状态
public static final int SCREEN_DIM_WAKE_LOCK;

//Deprecated,如果持有该类型的 wakelock 锁,保证屏幕处于常亮状态,键盘灯允许灭,按 Power 键灭屏后,会立即释放
public static final int SCREEN_BRIGHT_WAKE_LOCK;

//Deprecated,如果持有该类型的 wakelock 锁,则使屏幕、键盘灯都保持常亮状态,按 Power 键灭屏后,会立即释放
public static final int FULL_WAKE_LOCK;

//如果持有该锁,则当 PSensor 检测到有物体靠近时关闭屏幕,远离时又亮屏,该类型锁不会阻止系统进入睡眠状态,比如当到达休眠时间后会进入睡眠状态,但是如果当前屏幕由该 wakelock 关闭,则不会进入睡眠状态。
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK;

//如果持有该锁,则会使屏幕处于 DOZE 状态,同时允许 CPU 挂起,该锁用于 DreamManager 实现 Doze 模式,如 SystemUI 的 DozeService
public static final int DOZE_WAKE_LOCK;

//如果持有该锁,则会使设备保持唤醒状态,以进行屏幕绘制,该锁常用于 WindowManager 中,允许应用在系统处于 Doze 状态下时进行绘制
public static final int DRAW_WAKE_LOCK;

除等级外,还有以下标记:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//该值为 0x0000FFFF,用于根据 flag 判断 Wakelock 的级别,如:
//if((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) == PowerManager.PARTIAL_WAKE_LOCK){}
public static final int WAKE_LOCK_LEVEL_MASK;

//用于在申请锁时唤醒设备,一般情况下,申请 wakelock 锁时不会唤醒设备,它只会使屏幕保持打开状态,如果带有这个 flag,则会在申请 wakelock 时就点亮屏幕,
//如:通知来时屏幕亮,该 flag 不能和 PowerManager.PARTIAL_WAKE_LOCE 一起使用。
public static final int ACQUIRE_CAUSES_WAKEUP;

//在释放锁时,如果 wakelock 带有该标志,则会小亮一会儿再灭屏,该 flag 不能和 PowerManager.PARTIAL_WAKE_LOCE 一起使用。
public static final int ON_AFTER_RELEASE;

//该标记是作为 release() 方法的参数,且仅仅用于释放 PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK 类型的锁,
//如果带有该参数,则会延迟释放锁,直到传感器不再感到对象接近
public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY;

3、WakeLock锁申请

WakeLock锁申请:

1
2
3
4
5
6
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
//获取 WakeLock 对象
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
wl.acquire();//申请锁
Wl.acquire(int timeout);//申请超时锁
wl.release();//释放锁

newWakeLock()方法:

应用中获取WakeLock对象,获取的是位于PowerManager中的内部类(WakeLock的实例)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//frameworks/base/core/java/android/os/PowerManager.java

public WakeLock newWakeLock(int levelAndFlags, String tag) {
validateWakeLockParameters(levelAndFlags, tag); //参数校验
return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName());//调用WakeLock构造方法获取实例
}

//WakeLock构造方法
WakeLock(int flags, String tag, String packageName) {
mFlags = flags;//表示 wakelock 类型或等级
mTag = tag;//一个 tag,一般为当前类名
mPackageName = packageName;//获取 wakelock 的包名
mToken = new Binder();//一个 Binder 标记
mTraceName = "WakeLock (" + mTag + ")";
}

acquire()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//frameworks/base/core/java/android/os/PowerManager.java

public void acquire() {
synchronized (mToken) {
acquireLocked();
}
}

//申请超时锁
public void acquire(long timeout) {
synchronized (mToken) {
acquireLocked();
//申请锁之后,内部会维护一个 Handler 去完成自动释放锁
mHandler.postDelayed(mReleaser, timeout);
}
}

acquireLocked()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//frameworks/base/core/java/android/os/PowerManager.java

private void acquireLocked() {
//应用每次申请 wakelock,内部计数和外部计数加 1
mInternalCount++;
mExternalCount++;
//如果是非计数锁或者内部计数值为 1,即第一次申请该锁,才会真正去申请
if (!mRefCounted || mInternalCount == 1) {
mHandler.removeCallbacks(mReleaser);
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
try {
//向 PowerManagerService 申请锁
mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
mHistoryTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
//表示此时持有该锁
mHeld = true;
}
}

//是否是计数锁可以通过setReferenceCount()来设计,默认为计数锁。
public void setReferenceCounted(boolean value) {
synchronized (mToken) {
mRefCounted = value;
}
}

acquireLocked()方法中可以发现,对于计数锁,只会在第一次申请时向PowerManagerService去申请锁,当该wakelock实例后续再去申请时,如果锁未释放,则只会对计数引用加1。而如果是非计数锁,则每次申请都会调到PowerManagerService中去。

acquireWakeLock()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

@Override // Binder call
public void acquireWakeLock(IBinder lock, int flags, String tag,
String packageName,WorkSource ws, String historyTag) {
//...
//检查 wakelock 级别
PowerManager.validateWakeLockParameters(flags, tag);
//检查 WAKE_LOCK 权限
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
//如果是 DOZE_WAKE_LOCK 级别的 wakelock,还要检查 DEVICE_POWER 权限
if ((flags & PowerManager.DOZE_WAKE_LOCK) != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
} else {
ws = null
}
//...
//重置当前线程上传入的IPC标志
final long ident = Binder.clearCallingIdentity();
try {
acquireWakeLockInternal(lock, flags, tag, packageName, ws, historyTag, uid, pid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}

acquireWakeLockInternal()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

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;
//查找是否已存在该 WakeLock 实例
int index = findWakeLockIndexLocked(lock);
boolean notifyAcquire;
//是否存在 wakelock
if (index >= 0) {
wakeLock = mWakeLocks.get(index);
if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
//更新 wakelock
notifyWakeLockChangingLocked(wakeLock, flags, tag, packageName,
uid, pid, ws, historyTag);
wakeLock.updateProperties(flags, tag, packageName,
ws, historyTag, uid, pid);
}
notifyAcquire = false;
} else {
//从 SpareArray<UidState> 中查找是否存在该 uid
UidState state = mUidState.get(uid);
if (state == null) {
state = new UidState(uid);
//设置该 Uid 的进程状态
state.mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
mUidState.put(uid, state);
}
//将该 uid 申请的 WakeLock 计数加 1
//创建新的 PMS.WakeLock 实例
wakeLock = new WakeLock(lock, flags, tag, packageName, ws,
historyTag, uid, pid);
try {
lock.linkToDeath(wakeLock, 0);
} catch (RemoteException ex) {
throw new IllegalArgumentException("Wake lock is already dead.");
}
//添加到 wakelock 集合中
mWakeLocks.add(wakeLock);
//用于设置 PowerManger.PARTIAL_WAKE_LOCK 能否可用
//1.缓存的不活动进程不能持有 wakelock 锁
//2.如果处于 idle 模式,则会忽略掉所有未处于白名单中的应用申请的锁
setWakeLockDisabledStateLocked(wakeLock);
//表示有新的wakelock申请了
notifyAcquire = true;
}
//判断是否直接点亮屏幕,如果带有点亮屏幕标志值,并且 wakelock 类型为FULL_WAKE_LOCK,SCREEN_BRIGHT_WAKE_LOCK,SCREEN_DIM_WAKE_LOCK,则进行下一步处理
applyWakeLockFlagsOnAcquireLocked(wakeLock, uid);//更新电源状态
//更新标志位
mDirty |= DIRTY_WAKE_LOCKS;
updatePowerStateLocked();//更新电源状态
if (notifyAcquire) {//如果有新的 WakeLock 实例创建,则 notifyAcquire 值为 true
//通知 BatteryStatsService进行统计持锁时间等
notifyWakeLockAcquiredLocked(wakeLock);
}
}
}

分析acquireWakeLockInternal()方法可知,首先通过传入的第一个参数IBinder查找WakeLock是否已经存在,若存在,则不再进行实例化,只在原有的WakeLock上更新其属性值,若不存在,则创建一个WakeLock对象,同时将该WakeLock保存到List中。需要注意的是,此处的WakeLock对象和PowerManager中获取的不是同一个WakeLock。

获取到 WakeLock 实例后,通过 setWakeLockDisabledStateLocked(wakeLock) 进行了判断该 WakeLock 是否可用,主要有两种情况:

  • 缓存的不活动进程不能持有 WakeLock 锁;
  • 如果处于 idle 模式,则会忽略掉所有未处于白名单中的应用申请的锁。

根据情况会设置 WakeLock 实例的 disable 属性值表示该 WakeLock 是否不可用。下一步进行判断是否直接点亮屏幕。

applyWakeLockFlagsOnAcquireLocked()

1
2
3
4
5
6
7
8
9
10
11
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) {
if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0
&& isScreenLock(wakeLock)) {
//...
//唤醒设备。更新上次唤醒设备的时间,在系统超时休眠时用这个值判断。
wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), wakeLock.mTag, opUid,
opPackageName, opUid);
}
}

notifyWakeLockAcquiredLocked()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
//通过该方法通知 Notifier,Notifier 中则会根据该锁申请的时间开始计时,并以此来判断是否是一个长时间持有的锁。
private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) {
if (mSystemReady && !wakeLock.mDisabled) {
wakeLock.mNotifiedAcquired = true;
wakeLock.mStartTimeStamp = SystemClock.elapsedRealtime();
//Called when a wake lock is acquired.
mNotifier.onWakeLockAcquired(wakeLock.mFlags,
wakeLock.mTag, wakeLock.mPackageName,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
wakeLock.mHistoryTag);
//...
//重新开始检查持锁时间
restartNofifyLongTimerLocked(wakeLock);
}
}

restartNofifyLongTimerLocked()方法:

1
2
3
4
5
6
7
8
9
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void restartNofifyLongTimerLocked(WakeLock wakeLock) {
wakeLock.mAcquireTime = SystemClock.uptimeMillis();
if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
== PowerManager.PARTIAL_WAKE_LOCK && mNotifyLongScheduled == 0) {
enqueueNotifyLongMsgLocked(wakeLock.mAcquireTime + MIN_LONG_WAKE_CHECK_INTERVAL);
}
}

enqueueNotifyLongMsgLocked()方法:

1
2
3
4
5
6
7
8
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void enqueueNotifyLongMsgLocked(long time) {
mNotifyLongScheduled = time;
Message msg = mHandler.obtainMessage(MSG_CHECK_FOR_LONG_WAKELOCKS);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, time);
}

至此,WakeLock锁的申请便已结束,完整流程如下:

4、WakeLock锁释放

对于使用acquire()方法申请的永久锁,必须显式的释放。否则如果系统一直持有wakelock锁,将无法进入休眠状态,从而导致耗电过快等功耗问题。

而通过acquire(long timeout)方法申请的超时锁则会在时间到达后自动释放(通过Handler.post(Runnable)的方式释放)。

release(int flags)方法:

不管是哪种锁的释放,其实都是在 release(int) 中进行的,只不过参数不同。如果flags为RELEASE_FLAG_TIMEOUT,表示释放的为超时锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

public void release(int flags) {
synchronized (mToken) {
//内部计数 -1
mInternalCount--;
//如果释放超时锁,外部计数 -1
if ((flags & RELEASE_FLAG_TIMEOUT) == 0) {
mExternalCount--;
}
//如果释放非计数锁或内部计数为 0,并且该锁还在持有,则通过 PowerManagerService 去释放
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;
}
}
//如果是计数锁,并且外部计数小于 0,则抛出异常
if (mRefCounted && mExternalCount < 0) {
throw new RuntimeException("WakeLock under-locked " + mTag);
}
}
}

对于计数锁(一次申请一次释放)的释放,每次都会对内部计数值减一,只有当你内部计数值减为 0 时,才会去调用 PowerManagerService 去真正的释放锁;如果释放非计数锁(无论申请多少次,一次可释放),则每次都会调用 PowerManagerService 进行释放。

releaseWakeLock()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

@Override // Binder call
public void releaseWakeLock(IBinder lock, int flags) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
//检查权限
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
//重置当前线程的 IPC 标志
final long ident = Binder.clearCallingIdentity();
try {
//去释放锁
releaseWakeLockInternal(lock, flags);
} finally {
//设置新的 IPC 标志
Binder.restoreCallingIdentity(ident);
}
}

releaseWakeLockInternal()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void releaseWakeLockInternal(IBinder lock, int flags) {
synchronized (mLock) {
//查找 WakeLock 是否存在
int index = findWakeLockIndexLocked(lock);
if (index < 0) {
return;
}
WakeLock wakeLock = mWakeLocks.get(index);
//该 flag 用来推迟释放 PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK 类型的锁,
//它会在传感器感觉不再靠近的时候才释放该锁
if ((flags & PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY) != 0) {
//表示在点亮屏幕前需要等待 PSensor 返回负值
mRequestWaitForNegativeProximity = true;
}
if ((flags & PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY) != 0) {
mRequestWaitForNegativeProximity = true;
}
//取消 Binder 的死亡代理
wakeLock.mLock.unlinkToDeath(wakeLock, 0);
//释放锁,详见下面分析
removeWakeLockLocked(wakeLock, index);
}
}

releaseWakeLockInternal() 中处理时,首先查找 WakeLock 是否存在,若不存在,直接返回;然后检查是否带有影响释放行为的标志值,上面已经提到过,目前只有一个值,之后取消了 Binder 的死亡代理,最后调用了 removeWakeLockLocked() 方法。

removeWakeLockLocked()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void removeWakeLockLocked(WakeLock wakeLock, int index) {
//从 List中 移除
mWakeLocks.remove(index);
//得到该 wakelock 中的 UidState 属性
UidState state = wakeLock.mUidState;
state.mNumWakeLocks--;
if (state.mNumWakeLocks <= 0 &&
state.mProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
//从 SpareArray<UidState> 中移除该 wakelock 的 UidState
//注意,下面的 mUidState 是 SpareArray<UidState>,而上面的 mUidState 是 wakeLock.mUidState
mUidState.remove(state.mUid);
}
//使用 Notifier 通知其他应用
notifyWakeLockReleasedLocked(wakeLock);
//对带有 ON_AFTER_RELEASE 标志的 wakelock 进行处理
applyWakeLockFlagsOnReleaseLocked(wakeLock);
mDirty |= DIRTY_WAKE_LOCKS;
//更新电源状态信息,详见
updatePowerStateLocked();
}

在 removeWakeLockLocked() 中,对带有 ON_AFTER_RELEASE 标志的 wakelock 进行处理时,释放锁后会亮一段时间后灭屏,这里来看看 applyWakeLockFlagsOnReleaseLocked(wakeLock) 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

/**
*如果当前释放的 wakelock 带有 PowerManager.ON_AFTER_RELEASE 标志,则会屏幕在灭屏时小亮一会儿才会熄灭
*/
private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0
&& isScreenLock(wakeLock)) {
//更新用户活动时间,并带有 PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS 标志,用于延缓灭屏时间
userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER,
PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
wakeLock.mOwnerUid);
}
}

最后,又将调用updatePowerStateLocked()方法,然后调用updateSuspendBlockerLocked()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void updateSuspendBlockerLocked() {
//...
//如果不再持有 PARTIAL_WAKELOCK 类型的 WakeLock 锁,释放 mWakeLockSuspendBlocker 锁
if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
mWakeLockSuspendBlocker.release();
mHoldingWakeLockSuspendBlocker = false;
}
//如果不再需要屏幕保持亮屏,释放 mDisplaySuspendBlocker 锁
if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
mDisplaySuspendBlocker.release();
mHoldingDisplaySuspendBlocker = false;
}
//...
}

在释放锁流程中,调用release()方法,在该方法中继续调用JNI层方法nativeReleaseSuspendBlocker(),在JNI层方法中调用HAL层方法,通过向/sys/power/wake_unlock中写值完成释放。

完整流程图如下: