DreamManagerService(下面就简称DreamMS)是继承于SystemService的系统服务,运行于system_server进程内,负责Dream和Doze功能的进入、退出等,Dream是指手机屏保,现在这个功能已经几乎不使用了,被AOD取而代之,Doze则是表示一种屏幕状态,在Doze状态时,屏幕会处于一种低耗电状态,支持系统提供显示内容,但也允许CPU挂起,AOD/FOD等就是在Doze下实现的。从代码来看DreamMS只有短短600行,应该是碰到过最小的SystemService了,当然,整个Dream或Doze的实现,并非仅仅由它一个类完成,这篇文章分析总结下DreamMS模块的整体框架,以及Dream/Doze的实现流程,由于屏保功能已几乎不使用,所以以Doze为主,这里统称为Dreamland。
1.实现原理
当系统灭屏后,PMS会通过DreamMS去启动一个指定的DreamService服务,当该服务启动成功后,DreamMS中会获取一个DOZE_WAKE_LOCK类型的wakelock锁,此时屏幕将处于Doze状态,而DreamService所属组件,将自由添加显示内容。
2.模块架构
整个Dreamland相关类图如下:
- IDreamManager: 通过IDreamManager.aidl生成的Binder接口,用来和Client进程交互;
- DreamManagerInternal: DreamManagerService用来和system_server内部线程的交互;
- DreamManagerService:继承于SystemService,由SystemServer启动,内部实现了IDreamService.Stub和DisplayManagerInternal用于和其他进程/线程进行交互;
- DreamController: Dreamland功能的控制层,DreamManagerService不会直接和Client交互,而是通过该类和Client交互;
- DreamRecord: 代表一个Dreamland(梦境),实现了ServiceConnection接口,用来绑定Client端的DreamService;
- DreamService: FW层定义的一个Service,Client端必须是它的子类。
- IDreamService:通过IDreamService.aidl生成的Binder接口,bindService时作为客户端返回给DreamMS的IBinder对象;
- AmbientDisplayConfiguration: 该类中会读取配置相关信息;
3.启动流程
DreamMS的启动在SystemServer中开始:
mSystemServiceManager.startService(DreamManagerService.class);
由于是SystemService,因此之后将依次调用其构造方法、onStart()、onPhase(),直接看这三个方法:
public DreamManagerService(Context context) {
super(context);
mContext = context;
// 所以Dream相关的操作是在android.fg线程执行的
mHandler = new DreamHandler(FgThread.get().getLooper());
// 实例化DreamController实例,并接受其callback
mController = new DreamController(context, mHandler, mControllerListener);
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
// 实例化一个Doze WakeLock
mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG);
// 该对象用来获取doze相关配置
mDozeConfig = new AmbientDisplayConfiguration(mContext);
}
构造方法中进行了一些初始化操作,先是使用android.fg线程的Looper对象创建了一个DreamHandler,所以此Handler中的逻辑,运行在android.fg线程中,然后实例化了DreamController对象,顾名思义,这个对象就是用来控制Dream/Doze的。最后实例化了AmbientDisplayConfiguration对象,可以通过该对象获取Doze的一些配置属性,内部就是通过这个传入的mContext操作的。 这里也顺便看看DreamController的构造方法:
public DreamController(Context context, Handler handler, Listener listener) {
mContext = context;
mHandler = handler;
mListener = listener;
// 获取WMS对象
mIWindowManager = WindowManagerGlobal.getWindowManagerService();
// 创建一个Intent,在startDream的时候会发送该广播
mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mCloseNotificationShadeIntent.putExtra("reason", "dream");
}
构造方法执行完毕后,执行onStart():
@Override
public void onStart() {
publishBinderService(DreamService.DREAM_SERVICE, new BinderService());
publishLocalService(DreamManagerInternal.class, new LocalService());
}
该方法中,将BinderService对象发布给了ServiceManager,供其他模块进行IPC操作,BinderService继承于IDreamManager.Stub,当其他进程需要调用DreamMS中的方法时,可以通过这种方式进行:
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.getService(DreamService.DREAM_SERVICE));
然后将LocalService对象注册给了LocalServices,供system_server进程内的其他模块调用,LocalService继承于DreamManagerInternal,比如在PMS中:
mDreamManager = getLocalService(DreamManagerInternal.class);
接下来调用的方法是onPhase():
@Override
public void onBootPhase(int phase) {
// 此阶段表示可以启动或bind三方app了
if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// 注册用户切换广播
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
writePulseGestureEnabled();
synchronized (mLock) {
stopDreamLocked(false /*immediate*/);
}
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
// 注册
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP), false,
mDozeEnabledObserver, UserHandle.USER_ALL);
writePulseGestureEnabled();
}
}
在SystemServer中该方法会被调用多次,以表示不同的启动阶段,很显然DreamMS中只处理一个阶段。在这个阶段,注册了Intent.ACTION_USER_SWITCHED广播以及Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP监听。然后调用了writePulseGestureEnabled()
方法:
private void writePulseGestureEnabled() {
ComponentName name = getDozeComponent();
boolean dozeEnabled = validateDream(name);
LocalServices.getService(InputManagerInternal.class).setPulseGestureEnabled(dozeEnabled);
}
从代码中看,这个方法就是用来通知InputMangerService,是否应该在Doze/Dream时,传递pulse gesture事件的(从代码来看,这个功能并没有使用)。
到这里,SystemServer对DreamMS的启动完成。以上这个过程时序图如下:
4.进入Dreamland过程
Doze的启动,还需要从PMS的流程流程开始说起,我们知道,在灭屏时,PMS中首先会将mWakefulness设置为WAKEFULNESS_DOZING,如果不支持Dream/Doze,才会设置为WAKEFULNESS_ASLEEP,表示设备进入休眠状态。在将WAKEFULNESS_DOZING后,将开始进入Dream/Doze的流程。这里关于灭屏的流程不详细分析,只调进入Dream/Doze的handleSandman()方法中的两个重要调用:
private void handleSandman() { // runs on handler thread
......
final boolean isDreaming;
if (mDreamManager != null) {
// 是否需要Dream/Doze
if (startDreaming) {
// 通过DreamMS启动Dream/Doze
mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
}
}
......
// 停止Dream/Doze了
if (isDreaming) {
mDreamManager.stopDream(false /*immediate*/);
}
}
由于Dream已几乎不使用了,所以关于Dream的流程在此都略过。
可以看到,通过DreamManagerInternal.startDream()将进入Doze状态:
@Override
public void startDream(boolean doze) {
startDreamInternal(doze);
}
private void startDreamInternal(boolean doze) {
final int userId = ActivityManager.getCurrentUser();
// 选择客户端Dream/Doze组件
final ComponentName dream = chooseDreamForUser(doze, userId);
if (dream != null) {
synchronized (mLock) {
startDreamLocked(dream, false /*isTest*/, doze, userId);
}
}
}
在LocalService中收到PMS调用后,立即调用了startDreamInternal(),在这个方法中,首先是选择一个客户端Doze组件,然后进一步执行启动流程。
所谓的客户端Doze组件,就是一个继承于DozeService的服务,而进入Doze,实际也就是DreamMS中通过bind方式启动这个服务,之后的事情将由这个服务来处理,比如AOD的显示等。当选择好客户端Doze组件后,将通过bind的方式启动该DreamService。 选择DreamService组件:
private ComponentName chooseDreamForUser(boolean doze, int userId) {
if (doze) {
ComponentName dozeComponent = getDozeComponent(userId);
return validateDream(dozeComponent) ? dozeComponent : null;
}
........
}
private ComponentName getDozeComponent(int userId) {
if (mDozeConfig.enabled(userId)) {
// 从AmbientDisplayConfiguration中获取
return ComponentName.unflattenFromString(mDozeConfig.ambientDisplayComponent());
} else {
return null;
}
}
上面可以看到,最终是通过AmbientDisplayConfiguration获取了一个ComponentName对象,而AmbientDisplayConfiguration中的这个方法也很简单:
public String ambientDisplayComponent() {
return mContext.getResources().getString(R.string.config_dozeComponent);
}
因此,进Doze后启动哪个DreamService,是在config.xml中配置的。 得到客户端DreamService的ComponentName对象后,来看接下来的流程将会进入到DreamController中:
private void startDreamLocked(final ComponentName name,
final boolean isTest, final boolean canDoze, final int userId) {
// 做一个退出Dreamland操作
stopDreamLocked(true /*immediate*/);
final Binder newToken = new Binder();
mCurrentDreamToken = newToken;
mCurrentDreamName = name;
mCurrentDreamIsTest = isTest;
mCurrentDreamCanDoze = canDoze;
mCurrentDreamUserId = userId;
PowerManager.WakeLock wakeLock = mPowerManager
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream");
// 进入到android.fgi线程,该wakelock锁在执行完毕后自动释放
mHandler.post(wakeLock.wrap(
() -> mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock)));
}
在这里,执行流程进入到了android.fg线程,并调用进入DreamController中:
public void startDream(Binder token, ComponentName name,
boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
// start前先做一次stop
stopDream(true /*immediate*/);
try {
// 发送Intent.ACTION_CLOSE_SYSTEM_DIALOGS广播,关闭系统Dialog
mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
// 参数封装给了DreamRecode.
mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock);
mDreamStartTime = SystemClock.elapsedRealtime();
try {
// 添加TYPE_DREAM类型的WindowToken
mIWindowManager.addWindowToken(token, TYPE_DREAM, DEFAULT_DISPLAY);
} catch (RemoteException ex) {
stopDream(true /*immediate*/);
return;
}
Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
intent.setComponent(name);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
try {
// bind service
if (!mContext.bindServiceAsUser(intent, mCurrentDream,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
new UserHandle(userId))) {
stopDream(true /*immediate*/);
return;
}
} catch (SecurityException ex) {
stopDream(true /*immediate*/);
return;
}
mCurrentDream.mBound = true; // 表示bind Doze组件成功
// 如果5s没有bind service成功,则stop dream恢复到原始状态
mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);
} finally {
}
}
在进入startDream()后,首先将创建一个DreamRecord对象,startDream()方法中的参数作为属性封装在了DreamRecord中,因此,每进入一次Dreamland都会创建一个新的DreamRecord对象:
public DreamRecord(Binder token, ComponentName name,
boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
mToken = token; // Binder类型Token
mName = name; // doze组件
mIsTest = isTest; // 是否用于测试
mCanDoze = canDoze; // 是否是Doze
mUserId = userId; // 当前user id
mWakeLock = wakeLock; // WakeLock对象
mWakeLock.acquire();
mHandler.postDelayed(mReleaseWakeLockIfNeeded, 10000);
}
DreamRecord还有一个重要的任务,它实现了ServiceConnection对象,因此在接下来的流程中,将以bind的方式去启动客户端DreamService,启动成功后,DreamRecord中收到客户端回调:
@Override
public void onServiceConnected(ComponentName name, final IBinder service) {
mHandler.post(new Runnable() {
@Override
public void run() {
mConnected = true; // 表示和客户端连接成功(收到callback了)
if (mCurrentDream == DreamRecord.this && mService == null) {
// 开始attach
attach(IDreamService.Stub.asInterface(service));
} else {
releaseWakeLockIfNeeded();
}
}
});
}
当绑定成功后,接下来将和客户端返回的IDreamService进行attach,attach成功后,将客户端返回的IDreamService赋给DreamRecord对象的属性,然后发送Intent.ACTION_DREAMING_STARTED广播:
private void attach(IDreamService service) {
try {
service.asBinder().linkToDeath(mCurrentDream, 0);
// 调用客户端attach()
service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze,
mCurrentDream.mDreamingStartedCallback);
} catch (RemoteException ex) {
stopDream(true /*immediate*/);
return;
}
mCurrentDream.mService = service;
if (!mCurrentDream.mIsTest) {
// attach成功后,发送DreamStart广播
mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL);
mCurrentDream.mSentStartBroadcast = true;
}
}
IDreamService是一个Binder接口,其实现类为DreamService.DreamServiceWrapper,紧接着开始DreamService.attach()的执行:
// frameworks/base/core/java/android/service/dreams/DreamService.java
private final void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started) {
// .......
if (!mWindowless) {
// 创建并添加一个Window
try {
getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes());
} catch (WindowManager.BadTokenException ex) {
mWindow = null;
return;
}
}
mHandler.post(new Runnable() {
@Override
public void run() {
if (mWindow != null || mWindowless) {
mStarted = true;
try {
// 子类实现此方法
onDreamingStarted();
} finally {
try {
// callback 给DreamController
started.sendResult(null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
}
});
}
在DreamService#attach()中,创建了一个Window,然后将调用onDreamingStarted(),可以发现这个方法在DreamService中并没有任何逻辑,它是为子类提供的一个hook方法,也是自绑定客户端DreamService后,收到的第一个方法。
最终,通过调用DreamMS#startDozing()
方法,完成屏幕DOZE状态的设置:
private void startDozingInternal(IBinder token, int screenState,
int screenBrightness) {
synchronized (mLock) {
if (mCurrentDreamToken == token && mCurrentDreamCanDoze) {
// 更新Doze时的Display状态和亮度
mCurrentDreamDozeScreenState = screenState;
mCurrentDreamDozeScreenBrightness = screenBrightness;
// 设置给PMS
mPowerManagerInternal.setDozeOverrideFromDreamManager(
screenState, screenBrightness);
if (!mCurrentDreamIsDozing) {
mCurrentDreamIsDozing = true;
// 申请DozeWakeLock
mDozeWakeLock.acquire();
}
}
}
}
在这里,更新了Doze状态时的Display状态以及亮度,然后传递给PMS,由PMS完成设置,最后申请了Doze WakeLock。通过Debug可以发现,每当客户端要更新Doze状态的Display状态或亮度时,都会调用startingDoze()方法进行设置。
到这里,Framework层的Dreamland切换流程完成了,至于客户端的逻辑和具体的业务,如AOD/FOD等,是由客户端进行实现。这个过程时序图如下:
5.退出Dreamland
亮屏时,PMS中调用DreamManagerInternal#stopDream()
方法,在这个方法内部调用stopDreamLocked()方法开始执行退出Dreamland流程:
private void stopDreamLocked(final boolean immediate) {
// startDreamLocked中创建的一个Binder Token
if (mCurrentDreamToken != null) {
if (immediate) { // 是否立即退出dreamland
Slog.i(TAG, "Leaving dreamland.");
cleanupDreamLocked();
} else if (mCurrentDreamIsWaking) {
return; // already waking
} else { // 缓缓退出dreamland
Slog.i(TAG, "Gently waking up from dream.");
mCurrentDreamIsWaking = true;
}
// 进入android.fg线程执行
mHandler.post(new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Performing gentle wake from dream.");
// 调用进入DreamController
mController.stopDream(immediate);
}
});
}
}
stopDream(boolean immediate)方法有一个参数,表示是否立即退出dreamland,还是给点时间执行一个退出过渡过程。 就先以参数为false分析,看看两个参数的流程有何区别:
public void stopDream(boolean immediate) {
try {
if (!immediate) {
// 表示已经开始做退出dreamland过渡了
if (mCurrentDream.mWakingGently) {
return; // already waking gently
}
if (mCurrentDream.mService != null) {
mCurrentDream.mWakingGently = true;
try {
// 调用客户端DozeService的wakeUp()
mCurrentDream.mService.wakeUp();
// 设置一个限制时长,5s后再次执行此方法
mHandler.postDelayed(mStopStubbornDreamRunnable, DREAM_FINISH_TIMEOUT);
return; //直接返回
} catch (RemoteException ex) {
}
}
}
// ......
}
如果immediate=false,则先是调用mService#wakeUp(),然后设置一个5s的时间,到达5s后会再次进入此方法,然后直接返回,不执行后面操作。在DreamService#wakeUp()方法中,调用finish()方法:
// frameworks/base/core/java/android/service/dreams/DreamService.java
public final void finish() {
if (!mFinished) {
mFinished = true;
if (mWindowToken == null) {
} else {
try {
// 调用DreamMS.finishSelf()
mSandman.finishSelf(mWindowToken, true /*immediate*/);
} catch (RemoteException ex) {
// system server died
}
}
}
}
finish()方法中,又通过Binder调用到了DreamMS#finishSelf(),将再一次调用stopDreamLocked()方法,不过参数将变为true:
private void finishSelfInternal(IBinder token, boolean immediate) {
......
synchronized (mLock) {
if (mCurrentDreamToken == token) {
stopDreamLocked(immediate);
}
}
}
再次进入DreamMS#stopDreamLocked()
后,将先调用cleanupDreamLocked()方法清除当前DreamLand的所有变量信息和释放Doze wakelock:
private void cleanupDreamLocked() {
......
if (mCurrentDreamIsDozing) {
mCurrentDreamIsDozing = false;
mDozeWakeLock.release(); //释放Doze wakelock
}
......
mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
}
然后再次进入DreamController#stopDream()方法,这次进入后,immediate为true:
public void stopDream(boolean immediate) {
try {
if (!immediate) {
// 不满足
}
final DreamRecord oldDream = mCurrentDream;
mCurrentDream = null;// 置空mCurrentDream对象
// 发送Intent.ACTION_DREAMING_STOPPED广播
if (oldDream.mSentStartBroadcast) {
mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL);
}
if (oldDream.mService != null) {
try {
// 拆卸关联的DreamService
oldDream.mService.detach();
} catch (RemoteException ex) {
// we don't care; this thing is on the way out
}
try {
oldDream.mService.asBinder().unlinkToDeath(oldDream, 0);
} catch (NoSuchElementException ex) {
// don't care
}
oldDream.mService = null; //置空绑定的DreamService
}
if (oldDream.mBound) {
// 解除和客户端DreamService的绑定
mContext.unbindService(oldDream);
}
oldDream.releaseWakeLockIfNeeded(); // 释放锁
try {
// 移除WindowToken
mIWindowManager.removeWindowToken(oldDream.mToken, DEFAULT_DISPLAY);
} catch (RemoteException ex) {
}
// 发起回调
mHandler.post(new Runnable() {
@Override
public void run() {
mListener.onDreamStopped(oldDream.mToken);
}
});
}
}
这次执行时,将依次进行以下操作:
- 发送Intent.ACTION_DREAMING_STOPPED广播;
- DreamMS和客户端DreamService进行detach操作;
- 调用
unbindService()
方法解除和客户端DreamService的绑定; - 移除WindowToken;
- 发起mListener.onDreamStopped()回调。
和客户端DreamService进行detach操作如下:
// frameworks/base/core/java/android/service/dreams/DreamService.java
private final void detach() {
if (mStarted) {
mStarted = false;
onDreamingStopped();
}
// ....
}
该方法中调用onDreamingStopped()方法,进行退出操作,和onDreamingStarted()一样,onDreamingStopped()也是一个hook方法,其具体实现交给了客户端DreamService。
detach完毕之后,通过mContext.unbindService(oldDream)
方法,DreamMS和客户端DreamService进行解绑,解除绑定成功后回调onServiceDisconnected()方法:
@Override
public void onServiceDisconnected(ComponentName name) {
mHandler.post(new Runnable() {
@Override
public void run() {
mService = null;
if (mCurrentDream == DreamRecord.this) {
stopDream(true /*immediate*/);
}
}
});
}
解绑后又会调用一次stopDream()方法,然而正常情况下,这次调用由于mCurrentDream已经被置为null了,因此不会执行。
于是,DreamMS中退出Dreamland的流程就结束了。现在回到PMS模块流程中,前面说过了,cleanupDreamLocked()的逻辑中会释放Doze WakeLock,这个操作会触发PMS中的状态更新,从而由于已经不满足Dreamland条件,因此PMS中唤醒状态将由DOZING进入ASLEEP状态,即真正意义上的灭屏:
// Doze has ended or will be stopped. Update the power state.
reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
就这样,退出了Dreamland......
在分析过程中,还有一个方法没有分析:DreamMS#stopDozing(),这个方法应该是提供给客户端去主动退出Dreamland的。
退出Dreamland过程时序图如下:
6.总结
1.经过对DreamMS模块退出和进入Dreamland的流程分析,可以发现,在交互方面,DreamMS没有直接和客户端DreamService进行交互,而是通过DreamController以Binder的方式进行交互,而客户端则可以直接通过Binder调用DreamMS。可以用下图表示这三者之间的调用关系:
2.进入Doze后,客户端则可以自己决定Doze的亮度和Display状态,并通过startDozing()设置到FW层。如果开启DreamMSdebug,可以看到设置信息:
01-17 16:49:50.737 1721 6857 D DreamManagerService: Dream requested to start dozing: android.os.Binder@f9282a4, screenState=4, screenBrightness=4095
01-17 16:49:59.500 1721 23117 D DreamManagerService: Dream requested to start dozing: android.os.Binder@f9282a4, screenState=1, screenBrightness=4095
01-17 16:50:04.271 1721 6857 D DreamManagerService: Dream requested to start dozing: android.os.Binder@f9282a4, screenState=4, screenBrightness=4095
所以,Doze状态下客户端每次改变Doze状态下的Display状态和亮度,都会调用startDozing(IBinder token, int screenState, int screenBrightness)
方法。
3.在退出Dreamland时,可以有多种方式,Powerkey亮屏时可以通过DreamManagerInternal#stopDream()退出,也可以通过客户端发起Binder调用IDreamManager#finishSelf()和awaken()退出。
4.在进入Doze后,查看LocalDisplayAdapter log时,会发现有如下DOZE和DOZE_SUSPEND不断交替的场景:
01-17 21:41:29.398 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.417 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
01-17 21:41:29.454 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.493 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
01-17 21:41:29.520 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.559 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
01-17 21:41:29.587 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.627 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
01-17 21:41:29.654 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.693 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
01-17 21:41:29.721 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.760 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
这并非是客户端不断下发的Display状态,而是由于客户端不断申请释放PowerManager.DRAW_WAKE_LOCK类型的锁导致的,通过DEBUG可以看到:
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 -> DOZE
01-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_SUSPEND
01-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=29499
01-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=0x10000
01-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=29499
01-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=29873
01-17 21:47:22.749 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
对应代码如下:
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0
&& !mDrawWakeLockOverrideFromSidekick) {
if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE;
}
if (mDisplayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) {
mDisplayPowerRequest.dozeScreenState = Display.STATE_ON;
}
}
mDisplayPowerRequest.dozeScreenBrightness =
mDozeScreenBrightnessOverrideFromDreamManager;
}
这很好地诠释了PowerManager.DRAW_WAKE_LOCK
WakeLock锁的意义,它用来让系统保持足够的awake以进行draw操作,当Display状态为Display.STATE_DOZE_SUSPEND时,表示CPU已经挂起不再更新。所以,只要有PowerManager.DRAW_WAKE_LOCK锁,Display的状态就会由Display.STATE_DOZE_SUSPEND退到Display.STATE_DOZE状态,进行轻微的绘制工作。
我们可以通过dump power 信息来查看客户端DreamService下发的状态:
$ adb shell dumpsys power | grep -Ei "mDozeScreen"
mDozeScreenStateOverrideFromDreamManager=4
mDozeScreenBrightnessOverrideFromDreamManager=2047
可以看到,客户端DreamService下发的Doze状态时,Display状态为Display.STATE_DOZE_SUSPEND,亮度为2047。