赞
踩
作为移动终端,电量是一种稀缺资源,需要尽可能的节省。于是,Android系统在空闲时,会主动进入到休眠状态。
Android设备中运行的进程需要使用电量资源时,也需要向PMS申请一个WakeLock;当工作完成后,就释放掉申请的WakeLock。PMS通过判断当前是否还有进程持有WakeLock,就能得出系统是否空闲。
经过调研PMS机制和HW的逆向源码,我们得到如下埋点函数。
即HW也是根据电量服务的notifyWakeLockAcquiredLocked/notifyWakeLockReleasedLocked/notifyWakeLockChangingLocked 进行wakeLock的埋点,从而建立wakeLock状态的最小模型,方便获取各种定制化接口。
埋点函数 | 作用 |
---|---|
PowerManagerService.notifyWakeLockAcquiredLocked | 应用持锁埋点 |
PowerManagerService.notifyWakeLockReleasedLocked | 应用释放锁埋点 |
PowerManagerService.notifyWakeLockChangingLocked | 锁配置更新埋点 |
通过上述函数我们需要得到的函数
API接口 | 作用 |
---|---|
getWkTimeByUidPid | 根据UID\PID获取持锁时间 |
getWkTimeByUid | 根据UID获取持锁时间 |
getWkHoldingTime | 获取阻止休眠时长 |
getWkTimeByUidPidTAG | 根据UID\PID\TAG获取持锁TAG |
getWkTagByUidPid | 根据UID\PID获取持锁TAG |
getWkTimeByTag | 根据TAG获取持锁时长 |
getWkUidsByTag | 根据TAG获取持锁UID |
getWkPidsByTag | 根据TAG获取持锁PID |
getHoldingWkPidsByUid | 根据UID获取阻止休眠PID |
isHoldWkByTag | 根据TAG获取持锁TAG |
getLastReleaseAudioMixUid | 获取最近音频输出的释放锁 |
getLastReleaseAudioInUid | 获取最近音频输入的释放锁 |
getHoldingJobWkTime | 获取持锁Job时长 |
使用 PowerManager.newWakeLock API 进行持锁管理,WakeLock Flag一般与WakeLock Level组合使用
/* Flag Value CPU Screen Keyboard PARTIAL_WAKE_LOCK On Off Off 0x00000001 1 SCREEN_DIM_WAKE_LOCK On Dim Off 0x00000006 6 SCREEN_BRIGHT_WAKE_LOCK On Bright Off 0x0000000a 10 FULL_WAKE_LOCK On Bright Bright 0x0000001a 26 */ object AlertWakeLock { private val TAG = "AlertWakeLock" private var sCpuWakeLock: PowerManager.WakeLock? = null @SuppressLint("InvalidWakeLockTag") internal fun createPartialWakeLock(context: Context): PowerManager.WakeLock? { // 第一步:获取PowerManager的实例 val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager ?: return null // 第二步:调用PowerManager中的newWakeLock方法创建一个WakeLock对象 return pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, TAG) //return pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, TAG); } fun acquireCpuWakeLock(context: Context) { if (sCpuWakeLock != null) { return } sCpuWakeLock = createPartialWakeLock(context) // 第三步:acquire()获取相应的锁 sCpuWakeLock!!.acquire() } fun releaseCpuLock() { if (sCpuWakeLock != null) { // 最后:release释放 sCpuWakeLock!!.release() sCpuWakeLock = null } } }
WakeLock主要用于控制CPU、屏幕、键盘三部分
对于PARTIAL_WAKE_LOCK、SCREEN_DIM_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK和FULL_WAKE_LOCK而言,不考虑Power键的话,随着等级的提高,权限也相应增大,即持有高等级的锁,能够激活的部分越多;如果考虑Power键的话,PARTIAL_WAKE_LOCK可以保证CPU不休眠,反而是权限最大的。
level 值 | CPU | Screen | Keyboard | 备注 |
---|---|---|---|---|
PARTIAL_WAKE_LOCK | On | Off | Off | 不受Power键影响 |
SCREEN_DIM_WAKE_LOCK | On | Dim | Off | 按下电源键,仍然可进入休眠 |
SCREEN_BRIGHT_WAKE_LOCK | On | Bright | off | 按下电源键,仍然可进入休眠 |
FULL_WAKE_LOCK | On | Bright | On | 按下电源键,仍然可进入休眠 |
上述看,如果滥用下,很容易导致耗电异常。
levelAndFlags | 作用 |
---|---|
PROXIMITY_SCREEN_OFF_WAKE_LOCK | 无法阻止系统休眠。当系统处于唤醒态时,传感器发觉终端某个物体比较近时,关闭屏幕。重新拉个某个物体距离后,点亮屏幕。例如通话时,贴耳通话灭屏,远离耳朵亮屏 |
DOZE_WAKE_LOCK | 终端处于Dozing state时,使能CPU挂起,屏幕处于低电量模式。 |
DRAW_WAKE_LOCK | 终端处于Dozeing state 时,使应用获取足够的时间完成绘制。 |
ACQUIRE_CAUSES_WAKEUP | 正常情况下,获取WakeLock并不会点亮屏幕(即acquire之前机器处于息屏状态,无法点亮屏幕)。加上这个Flag后,acquire Wakelock同时能够点亮屏幕 |
ON_AFTER_RELEASE | 和用户体验有关。正常情况下当wakelock释放后,如果没有该标志位,那么系统会立即息屏。如果有该标志位,系统可以延长一段时间再息屏。 |
frameworks/base/core/java/android/os/PowerManager.java
WakeLock是PowerManager中的内部类
public final class WakeLock {
...
@UnsupportedAppUsage
private int mFlags;
@UnsupportedAppUsage
private String mTag;
private final String mPackageName;
private final IBinder mToken;
private int mInternalCount;
...
}
我们知道一个进程创建的WakeLock,实际上表明了该进程执行某个工作时对电量的需求,例如声明该工作需要保持屏幕处于点亮状态,或该工作需要CPU处于唤醒态等。
因此,进程创建了WakeLock后,需要将WakeLock发送到PMS中,让PMS明白该进程的需求。
这种将WakeLock通知到PMS的过程,就被称为acquire WakeLock。
frameworks/base/core/java/android/os/PowerManager.java
public void acquire() {
synchronized (mToken) {
acquireLocked();
}
}
private void acquireLocked() {
...
// 工作流程将通过Binder通信进入到PMS中
mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,mHistoryTag, mDisplayId);
...
}
PowerManager.WakeLock.acquire() 通过Binder调用到PowerManagerService
@Override // Binder call public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, ... acquireWakeLockInternal(lock, displayId, flags, tag, packageName, ws, historyTag,uid, pid); ... } private void acquireWakeLockInternal(IBinder lock, int displayId, int flags, String tag, String packageName, WorkSource ws, String historyTag, int uid, int pid) { ... //PMS中维持了一个ArrayList,记录当前已申请的WakeLock int index = findWakeLockIndexLocked(lock); ... //如果index大于0,说明此时Acquire的是一个旧的WakeLock if (index >= 0) { wakeLock = mWakeLocks.get(index); //这是判断WakeLock对应的成员变量是否发生改变 if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) { // 改变则更新 wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid); } } else { ... // 监控申请WakeLock的进程是否死亡 lock.linkToDeath(wakeLock, 0); ... //创建一个新的WakeLock,例如RIL第一次调用send就会进入该分支 wakeLock = new WakeLock(lock, displayId, flags, tag, packageName, ws, historyTag, uid, pid, state); //添加到wakelock列表 mWakeLocks.add(wakeLock); // 特殊处理PARTIAL_WAKE_LOCK,根据Doze模式的白名单更新wakelock的disabled变量,可以定制:即使该应用申请了PARTIAL_WAKE_LOCK,也不能阻止系统进入休眠状态。 setWakeLockDisabledStateLocked(wakeLock); } // 处理WakeLock对应的Flag // 判断WakeLock是否有ACQUIRE_CAUSES_WAKEUP,在必要时唤醒屏幕 applyWakeLockFlagsOnAcquireLocked(wakeLock, uid); mDirty |= DIRTY_WAKE_LOCKS; //更新电源状态 updatePowerStateLocked(); // 通知wakeLock发生变化,电量统计服务做相关统计 // 同时适合功耗异常埋点 notifyWakeLockAcquiredLocked(wakeLock); }
frameworks/base/core/java/android/os/PowerManager.java
public void release() {
release(0);
}
public void release(int flags) {
...
// 工作流程将通过Binder通信进入到PMS中
mService.releaseWakeLock(mToken, flags);
...
}
PowerManager.WakeLock.release() 通过Binder调用到PowerManagerService
private void releaseWakeLockInternal(IBinder lock, int flags) { ... releaseWakeLockInternal(lock, flags); ... } private void releaseWakeLockInternal(IBinder lock, int flags) { ... //根据Binder代理,从存储的ArrayList中找到对应WakeLock的序号 int index = findWakeLockIndexLocked(lock); //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(); }
Hw的WakeLock埋点
Hw的埋点数据处理
即HW也是根据电量服务的notifyWakeLockAcquiredLocked/notifyWakeLockReleasedLocked/notifyWakeLockChangingLocked 进行wakeLock的埋点,从而建立wakeLock状态的最小模型,方便获取各种定制化接口。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。