AOD相关机制

admin 2025-11-05 06:31:12 竞猜大厅

AOD相关机制

AOD的概念:

AOD,即A(lways) O(n) D(isplay)是android一种低功耗的显示模式的一种应用,他能保证屏幕某块区域一直亮,该应用开启时绘制的频率会低于正常的频率。由于AOD现实的不是和正常的亮屏之后显示的一样,只 会显示非黑色的部分,而且频率低于正常的频率 ,所以即使一直在显示功耗也比较低。

AOD进程的流程:

AOD是一个应用,主要是通过DreamManagerService来切换DOZE、DOZE_SUSPEND实现AOD的进入、退出和显示。 如图一 transitionTo开始进入到setDozeScreenState结束,而DreamService也就是DreamServiceManager的 代理,后面就开始到了DreamServiceManager里面来做了。

DreamManager和DreamManagerService

在DreamManager里面是通过updateDoze 从而调用到PowerManagerService里面的mSandman.startDozing方法来和DreamManagerService通讯的,此处调用逻辑简单看下源码就知道。

在DreamManagerService中,我们以进入DOZE状态为例,分析下调用流程。在进入DOZE的时候最重要的方法是

private void startDozingInternal(IBinder token, int screenState,

225 int screenBrightness) {

226 if (DEBUG) {

227 Slog.d(TAG, "Dream requested to start dozing: " + token

228 + ", screenState=" + screenState

229 + ", screenBrightness=" + screenBrightness);

230 }

231

232 synchronized (mLock) {

233 if (mCurrentDreamToken == token && mCurrentDreamCanDoze) {

234 mCurrentDreamDozeScreenState = screenState;

235 mCurrentDreamDozeScreenBrightness = screenBrightness;

236 mPowerManagerInternal.setDozeOverrideFromDreamManager(

237 screenState, screenBrightness);//设置亮度

238 if (!mCurrentDreamIsDozing) {

239 mCurrentDreamIsDozing = true;

240 mDozeWakeLock.acquire();//申请一个DozeWakeLock的锁

241 }

242 }

243 }

244 }

这个方法中其实最重要的就是设置一个亮度,申请一个锁。

先说亮度:这个亮度screenBrightness最终将通过PowerManagerService的mDozeScreenBrightnessOverrideFromDreamManager封装成一个request,给PowerManagerService和DisplayManagerServices交互的类DisplayPowerController

然后在recordBrightnessChange方法中通过binder直接给surface底层,从而把这个亮度给设置进去。

前面说过状态的切换最重要的是申请一个DozeWakeLock锁,这个锁定义如下:

mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG);

这个锁对应的类型未 public static final int DOZE_WAKE_LOCK = OsProtoEnums.DOZE_WAKE_LOCK; // 0x00000040

也就是说申请了0x00000040的锁,熟悉PowerManagerServices的同学都知道,这个锁最终会和用户行为一起合成一个drity,在getDesiredScreenPolicyLocked方法里计算出一个最终的policy给DisplayPowerController,

最终在DisplayPowerState里面将状态设置成为对应的状态(在这里要注意的是brightness原生流程中也是在DisplayPowerState里面去设置的,但是因为我们将传统的亮度和doze的亮度两个节点在底层分开了,所以doze的亮度是通过

recordBrightnessChange,利用binder直接和底层通信,将亮度设置进去的,具体的J1/J2 AOD节点移植方案)。

我们看可以看下getDesiredScreenPolicyLocked里面DOZE在是如何影响影响policy的

2 int getDesiredScreenPolicyLocked() {

2703 。。。。。。

2707 if (mWakefulness == WAKEFULNESS_DOZING) {

2708 if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {

2709 return DisplayPowerRequest.POLICY_DOZE;//policy的状态为doze

2710 }

2711 。。。。

2737

2738 return DisplayPowerRequest.POLICY_DIM;

2739 }

如果当前状态为WAKEFULNESS_DOZING且mWakeLockSummary里面有WAKE_LOCK_DOZE时当前policy就是DOZE

private static final int WAKE_LOCK_DOZE = 1 << 6;

前面DOZE_WAKE_LOCK是十进制的64,WAKE_LOCK_DOZE换成10进制也是64,换句话说进入DOZE状态的途径之一就是申请DOZE_WAKE_LOCK锁。

既然是途径之一还有什么方法可以进入DOZE状态能,通过源码我们发现,如果当前是本来就是POLICY_DOZE且有WAKE_LOCK_DRAW锁,这个时候回将dozeScreenState变成DOZE状态

private boolean updateDisplayPowerStateLocked(int dirty) {

2592 。。。。。。

2626 if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {

2627 mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;

2628 if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0

2629 && !mDrawWakeLockOverrideFromSidekick) {

2630 if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {

2631 mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE;//display为doze状态

2632 }

2633 if (mDisplayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) {

2634 mDisplayPowerRequest.dozeScreenState = Display.STATE_ON;

2635 }

2636 }

2637 。。。。。。。。。。。。

2665 return mDisplayReady && !oldDisplayReady;

2666 }

要通过dozeScreenState来进入DOZE,在源码中还有一种情况就是dozeScreenState为STATE_UNKNOWN状态,这个时候会用dozeScreenState的状态,但这种情况进入DOZE的会比较少。

private void updatePowerState() {

780 。。。。。

831 case DisplayPowerRequest.POLICY_DOZE:

832 if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {

833 state = mPowerRequest.dozeScreenState;

834 } else {

835 state = Display.STATE_DOZE;

836 }

837 if (!mAllowAutoBrightnessWhileDozingConfig) {

838 brightness = mPowerRequest.dozeScreenBrightness;

839 mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);

840 }

841 break;

。。。。。。

842 }

那如何切成DOZE_SUSPEND的呢,对应用(如指纹)会使用AOD暴露给他们的接口fireAodState,调到AOD的流程里,而AOD那边也是通过setDozeScreenState方法切成DOZE_SUSPEND同时应用(如指纹)释放DOZE_WAKE_LOCK锁的时候,CPU会

挂起,再通过DreamManagerService将mDozeScreenBrightnessOverrideFromDreamManager然后进入DOZE_SUSPEND状态,这里就不贴代码了,对应的log如下,

01-17 21:47:17.348 29499 29514 D PowerManagerService: acquireWakeLockInternal: lock=158204803, flags=0x80, tag="gxzw", ws=null, uid=1000, pid=29873 // 申请01-17 21:47:17.356 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE) // DOZE_SUSPEND -> DOZE01-17 21:47:17.665 29499 30650 D PowerManagerService: releaseWakeLockInternal: lock=158204803 [gxzw], flags=0x10000 // 释放01-17 21:47:17.672 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND) // DOZE -> DOZE_SUSPEND01-17 21:47:18.882 29499 29514 D PowerManagerService: acquireWakeLockInternal: lock=206718903, flags=0x80, tag="Window:AOD", ws=WorkSource{1000 com.miui.aod}, uid=1000, pid=2949901-17 21:47:18.899 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)01-17 21:47:19.019 29499 29499 D PowerManagerService: releaseWakeLockInternal: lock=206718903 [Window:AOD], flags=0x1000001-17 21:47:19.023 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)01-17 21:47:22.412 29499 30650 D PowerManagerService: acquireWakeLockInternal: lock=258767652, flags=0x80, tag="Window:gxzw_anim", ws=WorkSource{1000 com.android.systemui}, uid=1000, pid=2949901-17 21:47:22.416 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)01-17 21:47:22.423 29499 30103 D PowerManagerService: acquireWakeLockInternal: lock=158204803, flags=0x80, tag="gxzw", ws=null, uid=1000, pid=2987301-17 21:47:22.749 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)

关于DOZE,DOZE_SUSPEND的切换还有另外一种情况就是在LocalDisplayAdapter.java中,如果两次发下来的都是DOZE_SUSPEND,这个时候会在requestDisplayStateLocked方法里面切成DOZE,这也是很多时候把指纹等一些应用关掉之后,没有任何进程在上层切换,也会来回切的原因,这是Google原生的逻辑,最好还是别在这里动刀,这样设计的目的估计也是因为下两次联系的DOZE_SUSPEND的可能性非常小,下了两次就肯定是要做其他的事情(如修改亮度),这个时候就不能在DOZE_SUSPEND下修改了。

分析方法

在AOD的分析过程中,更多的是底层发现问题,fw层定位问题,给出解决方案,上层修改问题。

总的来说就是找到根本原因,上层能改就尽量上层改。

对于问题首先要确定上层的状态指令已经下到fw层,也就是先确定有类似的08-15 23:54:38.412 3482 3482 W DozeScreenState: applyScreenState: setDozeScreenState 4,如果没有则可以先让AOD排查下逻辑是否正常;

如果有指令下发,则需要排查是否底层和上层应用的一些状态没有对上。

还有一种情况就是有一些机制Google和底层并没有打成一致的协议,如底层要求DOZE_SUSPEND下不去绘制,但Google并没有任何机制去保证,对于某些屏也没有做限制.

当然更多的还是具体问题具体分析,这里也只能按照往常分析fw问题的方式,先确定下到底是哪里的问题(即有没有对应的log),不同的问题还要根据不同团队的能力给出不同的解决方案。