本文基于Android S源码梳理
系列回顾
学习目标
通过AOSP源码,对Power键灭屏流程进行梳理,比较亮屏之间的差异。
Power键灭屏简介
上一篇Power键亮屏流程中,已经通过源码简单了解了整个流程,包括事件处理、PowerManager侧处理,DPC侧亮度处理。本篇将继续进行Power键灭屏流程的梳理。由于亮屏和灭屏有部分逻辑重叠,如设置屏幕亮度动画等,这里重叠的部分会一带而过,重点去梳理一些差异部分。
按键亮灭屏框架: Power键灭屏流程图:
简要概括一下Power键的灭屏流程,当用户在亮屏状态下按下Power键,InputReader从EventHub中读取到事件,交由InputDispatcher进行分发,通知PhoneWindowManager对此事件进行处理,PhoneWindowManager处理后,通知PowerManagerService进行wakeUp处理,随后PowerManagerService在内部更新wakefulness,再通知DisplayPowerController进行亮屏状态更新,此间DisplayPowerController同样也存在阻塞灭屏,等待系统callback通知后,进行屏幕状态动画与亮度设置动画,完成后通知锁屏灭屏,并发送灭屏广播。
Power键灭屏时序图: 同样到此处,如果只是对Power键灭屏流程了解一下的同学,可以不用往下看了,下面就是具体业务中重要环节的详细介绍。
业务梳理
相关类
- frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
- frameworks/base/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
- frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
- frameworks/base/services/core/java/com/android/server/power/Notifier.java
- frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
- frameworks/base/services/core/java/com/android/server/display/RampAnimator.java
- frameworks/base/services/core/java/com/android/server/display/ColorFade.java
- frameworks/base/services/core/java/com/android/server/display/DisplayPowerState.java
- frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
- frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
- frameworks/base/services/core/java/com/android/server/lights/LightsService.java
部分类名解释和缩写
类名 | 作用 | 缩写 |
---|---|---|
SingleKeyGestureDetector | Android 12新增处理多屏按键响应 | |
PowerManagerService | Power状态管理 | PMS |
DisplayPowerController | 管理设备Display状态,主要处理近距sensor,亮灭屏 | DPC |
Notifier | Power侧用于通知其他系统模块 |
1.Power键事件处理差异
随着多屏设备的出现,google也在Android S上体现出他们对此类设备的适配理解,原先在Android S之前,处理Power键灭屏的流程还是放在对其Key Event处理中实现,而在Android S上,这个逻辑被移到SingleKeyGestureDetector中进行处理:
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
...
// This could prevent some wrong state in multi-displays environment,
// the default display may turned off but interactive is true.
// 这里注释是为了解决多屏显示的场景下出现错误的状态
final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
final boolean interactiveAndOn = interactive && isDefaultDisplayOn;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
handleKeyGesture(event, interactiveAndOn);
}
...
}
private void handleKeyGesture(KeyEvent event, boolean interactive) {
...
mSingleKeyGestureDetector.interceptKey(event, interactive);
}
复制代码
从PhoneWindowManager的处理可以看出,调用到了SingleKeyGestureDetector:
frameworks/base/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
void interceptKey(KeyEvent event, boolean interactive) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
// Store the non interactive state when first down.
if (mDownKeyCode == KeyEvent.KEYCODE_UNKNOWN || mDownKeyCode != event.getKeyCode()) {
mBeganFromNonInteractive = !interactive;
}
interceptKeyDown(event);
} else {
interceptKeyUp(event);
}
}
private boolean interceptKeyUp(KeyEvent event) {
...
// This could be a multi-press. Wait a little bit longer to confirm.
mKeyPressCounter++;
Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
mKeyPressCounter, downTime);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT);
...
}
复制代码
在消息MSG_KEY_DELAYED_PRESS中,调用接口onMultiPress,此接口同样也被PhoneWindowManager的内部类PowerKeyRule实现,并在其方法中调用了powerPress方法:
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {
...
switch (mShortPressOnPowerBehavior) {
...
case SHORT_PRESS_POWER_GO_TO_SLEEP:
sleepDefaultDisplayFromPowerButton(eventTime, 0);
break;
...
}
private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) {
...
sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
...
}
private void sleepDefaultDisplay(long eventTime, int reason, int flags) {
mRequestedOrSleepingDefaultDisplay = true;
mPowerManager.goToSleep(eventTime, reason, flags);
}
复制代码
最终调用了PMS的goToSleep接口,将业务传递到PMS侧进一步处理。 总结一下,在Android S中,google对Power键灭屏的流程基本思路还是和原来一样的,只不过为了去处理多屏事件处理时可能产生的状态异常,增加了一个SingleKeyGestureDetector来辅助PhoneWindowManager进行处理。最终还是通过PMS接口goToSleep完成对Power KeyEvent的处理。
2.PMS侧处理差异
开篇的时候提到的Power键灭屏的流程可以看到,和亮屏基本上没有区别,流程都是PMS先更新相关状态,如wakefulness,然后通知DPC进行屏幕状态更新,并设置亮度动画。所以这一块的流程就不再去梳理细节,需要的可以参考亮屏流程中的梳理。 但是也有不相同的地方,灭屏广播的触发时机,在亮屏流程中,亮屏广播发生在PMS通知DPC亮屏之前,而灭屏流程中,同样也存在相应的灭屏广播,灭屏广播的发送时间是在通知DPC处理之后。
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updatePowerStateLocked() {
...
// Phase 3: Update display power state.
// 通知DPC更新屏幕状态
final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
...
// Phase 5: Send notifications, if needed.
// 向系统模块更新Power状态,其中也包括触发灭屏广播
finishWakefulnessChangeIfNeededLocked();
...
}
private void finishWakefulnessChangeIfNeededLocked() {
...
mNotifier.onWakefulnessChangeFinished();
}
复制代码
Notifier是PMS框架中专门用于通知其他系统模块PMS状态发生变化的模块,同时也包括发送亮灭屏广播。
frameworks/base/services/core/java/com/android/server/power/Notifier.java
public void onWakefulnessChangeFinished() {
if (DEBUG) {
Slog.d(TAG, "onWakefulnessChangeFinished");
}
if (mInteractiveChanging) {
mInteractiveChanging = false;
handleLateInteractiveChange();
}
}
private void handleLateInteractiveChange() {
// Finished going to sleep...
// This is a good time to make transitions that we don't want the user to see,
// such as bringing the key guard to focus. There's no guarantee for this
// however because the user could turn the device on again at any time.
// Some things may need to be protected by other mechanisms that defer screen on.
...
updatePendingBroadcastLocked();
}
}
}
复制代码
之所以需要将灭屏广播移到DPC处理之后,在handleLateInteractiveChange中注释也说明了这一点,因为此时通知系统中的其他模块,可以进行一些不希望用户看到的业务逻辑处理。
3.DPC侧处理差异
DPC主要作用就是更新屏幕状态,执行亮度动画,本节中主要梳理的差异是阻塞灭屏,在亮屏流程中,当DPC准备好屏幕状态后,需要通过阻塞亮屏来等待WMS和锁屏的绘制,以确保屏幕点亮后能够正常显示。而在灭屏的过程中DPC也同样存在阻塞灭屏的流程:
frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private boolean setScreenState(int state, boolean reportOnly) {
final boolean isOff = (state == Display.STATE_OFF);
if (mPowerState.getScreenState() != state
|| mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
// If we are trying to turn screen off, give policy a chance to do something before we
// actually turn the screen off.
if (isOff && !mScreenOffBecauseOfProximity) {
if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON
|| mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
// 阻塞灭屏
blockScreenOff();
// 通知PhoneWindowManager开始灭屏
mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker);
// 解除阻塞亮屏
unblockScreenOff();
} else if (mPendingScreenOffUnblocker != null) {
// Abort doing the state change until screen off is unblocked.
return false;
}
}
...
}
private void blockScreenOff() {
if (mPendingScreenOffUnblocker == null) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
mPendingScreenOffUnblocker = new ScreenOffUnblocker();
mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime();
Slog.i(TAG, "Blocking screen off");
}
}
private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener {
// 通过源码查询,调用此callback的地方只有TaskSnapshotController,用于在灭屏的时候保存当前task状态。
@Override
public void onScreenOff() {
Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this);
mHandler.sendMessage(msg);
}
}
复制代码
当完成后,会再次调用updatePowerState来继续更新屏幕状态,并完成亮度动画设置。 其余的部分基本流程都和亮屏的流程相似,这里也不重复整理了。