当前位置:   article > 正文

功耗异常管控中WakeLock机制的埋点和需求调研_acquire_causes_wakeup

acquire_causes_wakeup

1. 前言

作为移动终端,电量是一种稀缺资源,需要尽可能的节省。于是,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时长
在这里插入图片描述

2. PowerManagerService SDK

2.1 DEMO

使用 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
        }
    }
}

  • 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

2.2 levelAndFlags

WakeLock主要用于控制CPU、屏幕、键盘三部分

  • PARTIAL_WAKE_LOCK = 0x00000001
  • SCREEN_DIM_WAKE_LOCK = 0x00000006
  • SCREEN_BRIGHT_WAKE_LOCK = 0x0000000a
  • FULL_WAKE_LOCK = 0x0000001a
  • PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020
  • DOZE_WAKE_LOCK = 0x00000040
  • DRAW_WAKE_LOCK = 0x00000080
  • ACQUIRE_CAUSES_WAKEUP = 0x10000000
  • ON_AFTER_RELEASE = 0x20000000

2.2.1 四大天王-level

对于PARTIAL_WAKE_LOCK、SCREEN_DIM_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK和FULL_WAKE_LOCK而言,不考虑Power键的话,随着等级的提高,权限也相应增大,即持有高等级的锁,能够激活的部分越多;如果考虑Power键的话,PARTIAL_WAKE_LOCK可以保证CPU不休眠,反而是权限最大的。

level 值CPUScreenKeyboard备注
PARTIAL_WAKE_LOCKOnOffOff不受Power键影响
SCREEN_DIM_WAKE_LOCKOnDimOff按下电源键,仍然可进入休眠
SCREEN_BRIGHT_WAKE_LOCKOnBrightoff按下电源键,仍然可进入休眠
FULL_WAKE_LOCKOnBrightOn按下电源键,仍然可进入休眠

上述看,如果滥用下,很容易导致耗电异常。

2.2.1 levelAndFlags

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释放后,如果没有该标志位,那么系统会立即息屏。如果有该标志位,系统可以延长一段时间再息屏。

3. 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;
  ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3.1 PowerManager.WakeLock.acquire()

在这里插入图片描述

我们知道一个进程创建的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);
        ...
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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);
    }
  • 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

3.2 PowerManager.WakeLock.release()

frameworks/base/core/java/android/os/PowerManager.java
在这里插入图片描述

    public void release() {
        release(0);
    }

    public void release(int flags) {
        ...
        // 工作流程将通过Binder通信进入到PMS中
        mService.releaseWakeLock(mToken, flags);
        ...
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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();
    }
  • 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

3. 华为的wakeLock功耗异常埋点调研

Hw的WakeLock埋点

PowerManagerService Notifier Utils LogPower notifyWakeLockAcquiredLocked notifyWakeLockReleasedLocked notifyWakeLockChangingLocked onWakeLockAcquired(160) onWakeLockReleased(161) onWakeLockChanging noteWakelock push(160/161) PowerManagerService Notifier Utils LogPower

Hw的埋点数据处理

DeviceMonitor WakelockStats createAllStats handleScrState handleStatsEvent(160/161) 1. getWkTimeByUidPid 2. getWkTimeByUid 3. getWkHoldingTime 4. getWkTimeByUidPidTAG 5. getWkTagByUidPid 6. getWkTimeByTag 7. getWkUidsByTag 8. getWkPidsByTag 9. getHoldingWkPidsByUid 10. isHoldWkByTag 11. getLastReleaseAudioMixUid 12. getLastReleaseAudioInUid 13. getHoldingJobWkTime DeviceMonitor WakelockStats

即HW也是根据电量服务的notifyWakeLockAcquiredLocked/notifyWakeLockReleasedLocked/notifyWakeLockChangingLocked 进行wakeLock的埋点,从而建立wakeLock状态的最小模型,方便获取各种定制化接口。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/301745
推荐阅读
相关标签
  

闽ICP备14008679号