Android 亮屏流程分析
本文档分析 Android 系统中的亮屏流程,重点阐述 PMS (PowerManagerService) 与 WMS (WindowManagerService) 的交互机制,从 WMS 视角解读亮屏过程。
一、整体架构:PMS 唤醒 WMS 的完整流程
css
┌──────────────────────────────────────────────────────────────────────────────┐
│ Phase 1: 电源键事件拦截与唤醒触发 │
└──────────────────────────────────────────────────────────────────────────────┘
电源键按下 → PhoneWindowManager.interceptKeyBeforeQueueing()
→ WindowWakeUpPolicy.wakeUpFromKey()
→ PowerManagerService.wakeUp(WAKE_REASON_POWER_BUTTON)
→ PowerGroup.wakeUpLocked() → setWakefulnessLocked(WAKEFULNESS_AWAKE)
→ mWakefulnessListener.onWakefulnessChangedLocked() ───────┐
│
(触发全局唤醒状态变化)
┌────────────────────────────────────────────────◀─────────────────────────────┐
│ Phase 2: PMS 通知 WMS 唤醒开始 │
└──────────────────────────────────────────────────────────────────────────────┘
onWakefulnessChangedLocked()
│
├── mNotifier.onGroupWakefulnessChangeStarted()
│ │
│ └── mPolicy.startedWakingUp() ───────────────────────►
│ (PhoneWindowManager.startedWakingUp)
│ • 设置 mDefaultDisplayPolicy.setAwake(true)
│ 更新锁屏超时、唤醒手势监听器
│ • 通知 Keyguard onStartedWakingUp()
│
└── updatePowerStateLocked()
│
└── updatePowerGroupsLocked()
│
└── PowerGroup.updateLocked()
│
└── mDisplayManagerInternal.requestPowerState()
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ Phase 3: 显示电源控制与屏幕点亮 │
└──────────────────────────────────────────────────────────────────────────────┘
DisplayPowerController.requestPowerState()
│
├── updatePowerStateInternal()
│ │
│ └── animateScreenStateChange()
│ │
│ └── setScreenState(Display.STATE_ON)
│ │
│ └── mPowerState.setScreenState()
│ │
│ └── PhotonicModulator.setState()
│ │
│ └── 硬件驱动点亮屏幕
│
┌──────────────────────────────────────────────────────────────────────────────┐
│ Phase 4: PMS 通知 WMS 屏幕开始点亮 (screenTurningOn) │
└──────────────────────────────────────────────────────────────────────────────┘
DisplayPowerController.setScreenState()
│
├── blockScreenOn() ← 创建 ScreenOnUnblocker
│ │
│ └── mPendingScreenOnUnblocker = new ScreenOnUnblocker()
│
└── mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker)
│ │
└─────────────────────────────────────────────────────►
PhoneWindowManager.screenTurningOn()
│
├── reportScreenTurningOnToWallpaper()
│
├── mDefaultDisplayPolicy.screenTurningOn(screenOnListener)
│ │
│ • removeSleepToken(DISPLAY_OFF_SLEEP_TOKEN_TAG) ★
│ • mScreenOnEarly = true
│ • mScreenOnFully = false
│ • 保存 screenOnListener
│ ★ WMS 移除睡眠令牌,准备显示内容
│
├── mKeyguardDelegate.onScreenTurningOn(reason)
│ │
│ └── Keyguard 开始绘制
│
└── 设置 MSG_KEYGUARD_DRAWN_TIMEOUT (超时保护)
┌──────────────────────────────────────────────────────────────────────────────┐
│ Phase 5: WMS 等待内容绘制完成 │
└──────────────────────────────────────────────────────────────────────────────┘
WMS 并行处理两个绘制完成事件:
│
┌───────────────────────┴───────────────────────┐
│ │
▼ ▼
Keyguard 绘制完成 WMS 窗口绘制完成
│ │
│ KeyguardDelegate.onDrawn() │
│ │ │
│ └── MSG_KEYGUARD_DRAWN_COMPLETE │
│ │ │
│ ▼ │
│ finishKeyguardDrawn() │
│ │ │
│ • mDefaultDisplayPolicy │
│ .finishKeyguardDrawn() │
│ → mKeyguardDrawComplete = true │
│ │
│ 设置窗口绘制等待 ──────────────────┤
│ mWindowManagerInternal │
│ .waitForAllWindowsDrawn( │
│ MSG_WINDOW_MANAGER_DRAWN_ │
│ COMPLETE) │
│ │
│ │
│ MSG_WINDOW_MANAGER_DRAWN_COMPLETE ◄──┘
│ │
│ ▼
│ finishWindowsDrawn(displayId)
│ │
│ • mDefaultDisplayPolicy
│ .finishWindowsDrawn()
│ → mWindowManagerDrawComplete = true
│
│ ▼
│ finishScreenTurningOn()
│ │
│ • 检查 mScreenOnEarly && mWindowManagerDrawComplete
│ • 如果 awake 还需检查 mKeyguardDrawComplete
│ • mScreenOnFully = true
│
│ ▼
│ enableScreen(listener)
│ │
│ • listener.onScreenOn() ★★★★★★★★★★★★★★★
│ ↳ PMS 解除屏幕点亮阻塞
│
└───────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ Phase 6: PMS 解除阻塞并通知 WMS 屏幕已点亮 (screenTurnedOn) │
└──────────────────────────────────────────────────────────────────────────────┘
DisplayPowerController
│
▼
MSG_SCREEN_ON_UNBLOCKED 接收
│
├── unblockScreenOn() ← 清除 mPendingScreenOnUnblocker
│ │
│ └── mWindowManagerPolicy.screenTurnedOn(mDisplayId)
│ │ │
│ └─────────────────────►
│ PhoneWindowManager.screenTurnedOn()
│ │
│ ├── reportScreenTurnedOnToWallpaper()
│ │
│ ├── mDefaultDisplayPolicy.screenTurnedOn()
│ │
│ └── mKeyguardDelegate.onScreenTurnedOn()
│
└── updatePowerState() ← PowerManagerService
│
└── PMS 完成唤醒流程
二、关键交互机制详解
2.1 screenTurningOn - 屏幕开始点亮
调用时机: DisplayPowerController 准备点亮屏幕硬件之前
目的: 通知 WMS 屏幕即将点亮,WMS 需要准备显示内容
PMS 侧实现 (DisplayPowerController.java):
java
// 在 animateScreenStateChange() 中
if (target == Display.STATE_ON && mPendingScreenOnUnblocker == null) {
// ★★★ 阻塞屏幕点亮,创建 Unblocker ★★★
if (mPowerState.getColorFadeLevel() == 0.0f) {
blockScreenOn();
} else {
unblockScreenOn();
}
// ★★★ 通知 WMS 屏幕开始点亮,传入 Unblocker ★★★
mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker);
}
private void blockScreenOn() {
if (mPendingScreenOnUnblocker == null) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
// ★★★ 创建 ScreenOnUnblocker,等待 WMS 绘制完成回调 ★★★
mPendingScreenOnUnblocker = new ScreenOnUnblocker();
mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
Slog.i(TAG, "Blocking screen on until initial contents have been drawn.");
}
}
private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {
@Override
public void onScreenOn() {
// ★★★ WMS 调用此方法通知绘制完成 ★★★
Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this);
mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
}
WMS 侧实现 (DisplayPolicy.java):
java
public void screenTurningOn(ScreenOnListener screenOnListener) {
WindowProcessController visibleDozeUiProcess = null;
synchronized (mLock) {
// ★★★ 释放屏幕关闭的 SleepToken("Display-off"),允许 Activity 显示 ★★★
// 标准实现为 mDisplayContent.removeSleepToken(DISPLAY_OFF_SLEEP_TOKEN_TAG),
// 部分版本经 SleepTokenAcquirer.release 等价释放
mDisplayContent.removeSleepToken(DisplayContent.DISPLAY_OFF_SLEEP_TOKEN_TAG);
mScreenOnEarly = true; // 屏幕点亮早期阶段
mScreenOnFully = false; // 尚未完全点亮
mKeyguardDrawComplete = false; // Keyguard 尚未绘制完成
mWindowManagerDrawComplete = false; // WMS 窗口尚未绘制完成
// ★★★ 保存 ScreenOnListener,绘制完成后调用 onScreenOn() ★★★
mScreenOnListener = screenOnListener;
// ... 处理 Doze 进程优先级 ...
}
}
2.2 等待内容绘制完成
Keyguard 绘制完成 (PhoneWindowManager.java):
java
// KeyguardDelegate 回调
@Override
public void onDrawn() {
if (DEBUG_WAKEUP) Slog.d(TAG, "mKeyguardDelegate.onDrawn.");
// ★★★ Keyguard 绘制完成,发送消息 ★★★
mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
}
// 处理 Keyguard 绘制完成
case MSG_KEYGUARD_DRAWN_COMPLETE:
if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mKeyguardDrawComplete");
finishKeyguardDrawn();
break;
// 完成 Keyguard 绘制
private void finishKeyguardDrawn() {
if (!mDefaultDisplayPolicy.finishKeyguardDrawn()) {
return;
}
synchronized (mLock) {
mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
}
// ★★★ Keyguard 绘制完成后,等待所有窗口绘制完成 ★★★
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, INVALID_DISPLAY);
mWindowManagerInternal.waitForAllWindowsDrawn(
mHandler.obtainMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE,
INVALID_DISPLAY, 0),
WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
}
// DisplayPolicy.java
public boolean finishKeyguardDrawn() {
synchronized (mLock) {
if (!mScreenOnEarly || mKeyguardDrawComplete) {
return false;
}
// ★★★ 标记 Keyguard 绘制完成 ★★★
mKeyguardDrawComplete = true;
mWindowManagerDrawComplete = false;
}
return true;
}
WMS 窗口绘制完成 (PhoneWindowManager.java):
java
// 处理窗口绘制完成
case MSG_WINDOW_MANAGER_DRAWN_COMPLETE:
final int displayId = msg.arg1;
if (DEBUG_WAKEUP) Slog.w(TAG, "All windows drawn on display " + displayId);
Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, displayId);
finishWindowsDrawn(displayId);
break;
// 完成窗口绘制
private void finishWindowsDrawn(int displayId) {
if (displayId != DEFAULT_DISPLAY && displayId != INVALID_DISPLAY) {
final ScreenOnListener screenOnListener = mScreenOnListeners.removeReturnOld(displayId);
if (screenOnListener != null) {
// ★★★ 非默认显示,直接通知 PMS ★★★
screenOnListener.onScreenOn();
}
return;
}
if (!mDefaultDisplayPolicy.finishWindowsDrawn()) {
return;
}
// ★★★ 窗口绘制完成,尝试完成屏幕点亮 ★★★
// 注:标准流程直接调用 finishScreenTurningOn();部分厂商实现会在此处
// 插入额外的 Keyguard 透明化门控,待该门控满足后再触发 finishScreenTurningOn()。
finishScreenTurningOn();
}
// DisplayPolicy.java
public boolean finishWindowsDrawn() {
synchronized (mLock) {
if (!mScreenOnEarly || mWindowManagerDrawComplete) {
return false;
}
// ★★★ 标记窗口绘制完成 ★★★
mWindowManagerDrawComplete = true;
}
return true;
}
2.3 finishScreenTurningOn - 屏幕点亮完成
条件检查 (DisplayPolicy.java):
java
public boolean finishScreenTurningOn() {
synchronized (mLock) {
ProtoLog.d(WM_DEBUG_SCREEN_ON,
"finishScreenTurningOn: mAwake=%b, mScreenOnEarly=%b, "
+ "mScreenOnFully=%b, mKeyguardDrawComplete=%b, "
+ "mWindowManagerDrawComplete=%b",
mAwake, mScreenOnEarly, mScreenOnFully, mKeyguardDrawComplete,
mWindowManagerDrawComplete);
// ★★★ 检查是否可以完成屏幕点亮 ★★★
// 1. 尚未完全点亮
// 2. 屏幕已经进入点亮早期阶段
// 3. 窗口已经绘制完成
// 4. 如果 awake,Keyguard 也必须绘制完成
if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete
|| (mAwake && !mKeyguardDrawComplete)) {
return false;
}
ProtoLog.i(WM_DEBUG_SCREEN_ON, "Finished screen turning on...");
mScreenOnListener = null;
mScreenOnFully = true; // ★★★ 完全点亮 ★★★
}
return true;
}
PhoneWindowManager 处理完成 (PhoneWindowManager.java):
java
private void finishScreenTurningOn() {
// 所有窗口绘制完成,重新启用方向监听器
mDefaultDisplayRotation.updateOrientationListener();
final ScreenOnListener listener = mDefaultDisplayPolicy.getScreenOnListener();
if (!mDefaultDisplayPolicy.finishScreenTurningOn()) {
return; // 尚未准备好
}
Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0);
// ★★★ 通知 PMS 屏幕可以显示了 ★★★
enableScreen(listener, true /* report */);
}
private void enableScreen(ScreenOnListener listener, boolean report) {
final boolean enableScreen;
final boolean awake = mDefaultDisplayPolicy.isAwake();
synchronized (mLock) {
// 记录第一次绘制 Keyguard,完成后启用屏幕
if (!mKeyguardDrawnOnce && awake) {
mKeyguardDrawnOnce = true;
enableScreen = true;
if (mBootMessageNeedsHiding) {
mBootMessageNeedsHiding = false;
hideBootMessages();
}
} else {
enableScreen = false;
}
}
// ★★★ 调用 PMS 提供的 screenOnListener.onScreenOn() ★★★
if (report) {
if (listener != null) {
listener.onScreenOn();
}
}
// 启用屏幕(首次)
if (enableScreen) {
mWindowManagerFuncs.enableScreenIfNeeded();
}
}
2.4 screenTurnedOn - 屏幕硬件已点亮
PMS 侧实现 (DisplayPowerController.java):
java
// 在 updatePowerStateInternal() 中
final boolean ready = mPendingScreenOnUnblocker == null
&& mPendingScreenOnUnblockerByDisplayOffload == null
&& (!mColorFadeEnabled || !mColorFadeOffAnimator.isStarted())
&& mPowerState.waitUntilClean(mCleanListener);
final boolean finished = ready && !mScreenBrightnessRampAnimator.isAnimating();
// ★★★ 通知 Policy 屏幕已点亮(硬件层面)★★★
if (ready && state != Display.STATE_OFF
&& mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON);
mWindowManagerPolicy.screenTurnedOn(mDisplayId);
}
WMS 侧实现 (PhoneWindowManager.java):
java
@Override
public void screenTurnedOn(int displayId) {
if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turned on...");
reportScreenTurnedOnToWallpaper(displayId);
if (displayId != DEFAULT_DISPLAY) {
return;
}
synchronized (mLock) {
// ★★★ 通知 Keyguard 屏幕已完全点亮 ★★★
mKeyguardDelegate.onScreenTurnedOn();
}
// ★★★ DisplayPolicy 标记屏幕点亮完成 ★★★
mDefaultDisplayPolicy.screenTurnedOn();
reportScreenStateToVrManager(true);
}
// DisplayPolicy.java
public void screenTurnedOn() {
onDisplaySwitchFinished();
}
2.5 MSG_SCREEN_ON_UNBLOCKED 处理
PMS 侧实现 (DisplayPowerController.java):
java
case MSG_SCREEN_ON_UNBLOCKED:
// ★★★ WMS 调用 onScreenOn() 后收到此消息 ★★★
if (mPendingScreenOnUnblocker == msg.obj) {
unblockScreenOn(); // 清除 Unblocker
updatePowerState(); // 更新电源状态
}
break;
三、SleepToken 机制
SleepToken 是 Display 级别的休眠控制机制。早期 Android 用全局 mSleeping 布尔值表示设备是否休眠,但折叠屏、车机多屏出现后,一个全局变量不够用了------系统需要为不同的 Display 独立管理休眠状态。
DisplayContent 维护一个 mAllSleepTokens 列表,存储所有未释放的 SleepToken。只要列表不为空,Display 就判定为应该睡眠,其上所有 Activity 进入休眠(onPause/onStop);列表变空后,Display 才会唤醒。
系统中存在两类 SleepToken:
| Token Tag | 获取时机 | 释放时机 |
|---|---|---|
"Display-off" |
screenTurnedOff() 或非 Default Display 关闭 |
screenTurningOn() 或非 Default Display 打开 |
"keyguard" |
Keyguard 正在显示 | Keyguard 消失 |
以电源键灭屏再亮屏为例:
- 灭屏 :
screenTurnedOff()→addSleepToken("Display-off")→mAllSleepTokens非空 →shouldSleep()=true→sleepIfNeeded()→ Activity onPause/onStop - 亮屏 :
screenTurningOn()→removeSleepToken("Display-off")→mAllSleepTokens为空 →wakeIfNeeded()→mSleeping=false→ Activity 恢复可见
如果亮屏时 Keyguard 还在显示,"keyguard" token 仍持有,Display 继续休眠直到用户解锁 Keyguard 释放该 token。这保证了 Activity 不会在屏幕还没真正亮起时就 resume,避免黑屏闪烁。
四、应用触发亮屏 (FLAG_TURN_SCREEN_ON)
4.1 使用方式
xml
<activity
android:name=".IncomingCallActivity"
android:turnScreenOn="true"
android:showWhenLocked="true" />
4.2 触发流程
Task.java:
java
if (next != null && next.canTurnScreenOn()
&& !mWmService.mPowerManager.isInteractive()) {
mTaskSupervisor.wakeUp(getDisplayId(), "resumeTop-turnScreenOnFlag");
next.setCurrentLaunchCanTurnScreenOn(false);
}
ActivityTaskSupervisor.java:
java
void wakeUp(int displayId, String reason) {
// ★★★ 使用 WAKE_REASON_APPLICATION 触发亮屏 ★★★
mPowerManager.wakeUp(SystemClock.uptimeMillis(),
PowerManager.WAKE_REASON_APPLICATION,
"android.server.wm:TURN_ON:" + reason, displayId);
}
ActivityRecord.java:
java
boolean canTurnScreenOn() {
if (!getTurnScreenOnFlag()) {
return false;
}
// ★★★ 检查 Keyguard 可见性 ★★★
return mCurrentLaunchCanTurnScreenOn
&& mTaskSupervisor.getKeyguardController().checkKeyguardVisibility(this);
}
五、总结
亮屏流程核心要点
-
PMS 负责电源状态管理
- 管理唤醒状态机 (WAKEFULNESS_AWAKE)
- 控制屏幕硬件点亮
- 通过回调机制通知 WMS
-
WMS 负责内容准备
- 接收 PMS 的
screenTurningOn通知 - 移除
DISPLAY_OFF_SLEEP_TOKEN - 等待 Keyguard 和窗口绘制完成
- 通过
screenOnListener.onScreenOn()通知 PMS
- 接收 PMS 的
-
关键交互时刻
screenTurningOn(): 屏幕点亮前,WMS 开始准备内容finishKeyguardDrawn() / finishWindowsDrawn(): 内容绘制完成onScreenOn(): WMS 通知 PMS 内容已准备好,可以显示screenTurnedOn(): 屏幕硬件已完全点亮
-
SleepToken 核心价值
- 将睡眠从"设备级别"降维到"屏幕级别",支持折叠屏、车机多屏等场景
- 解耦 Display 状态与 Activity 可见性,每个 Display 独立管理自己的睡眠令牌
- 确保 Keyguard 显示时应用不可见,控制内容显示时机,避免黑屏闪烁
- 以解耦、优雅的方式精准控制 Activity 在息屏/锁屏下的生命周期,保障功耗和安全
PMS 与 WMS 交互的三层通知
| 层次 | PMS → WMS | WMS → PMS | 目的 |
|---|---|---|---|
| 1 | startedWakingUp() / finishedWakingUp() |
- | 通知唤醒状态变化 |
| 2 | screenTurningOn() |
screenOnListener.onScreenOn() |
协调内容显示与屏幕点亮 |
| 3 | screenTurnedOn() |
- | 通知屏幕硬件已点亮 |
这个设计确保了:
- 屏幕点亮前,内容已经准备好
- 避免用户看到黑屏或未完成的绘制
- Keyguard 的安全遮挡得到保证