Android 亮屏流程分析

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

五、总结

亮屏流程核心要点

  1. PMS 负责电源状态管理

    • 管理唤醒状态机 (WAKEFULNESS_AWAKE)
    • 控制屏幕硬件点亮
    • 通过回调机制通知 WMS
  2. WMS 负责内容准备

    • 接收 PMS 的 screenTurningOn 通知
    • 移除 DISPLAY_OFF_SLEEP_TOKEN
    • 等待 Keyguard 和窗口绘制完成
    • 通过 screenOnListener.onScreenOn() 通知 PMS
  3. 关键交互时刻

    • screenTurningOn(): 屏幕点亮前,WMS 开始准备内容
    • finishKeyguardDrawn() / finishWindowsDrawn(): 内容绘制完成
    • onScreenOn(): WMS 通知 PMS 内容已准备好,可以显示
    • screenTurnedOn(): 屏幕硬件已完全点亮
  4. SleepToken 核心价值

    • 将睡眠从"设备级别"降维到"屏幕级别",支持折叠屏、车机多屏等场景
    • 解耦 Display 状态与 Activity 可见性,每个 Display 独立管理自己的睡眠令牌
    • 确保 Keyguard 显示时应用不可见,控制内容显示时机,避免黑屏闪烁
    • 以解耦、优雅的方式精准控制 Activity 在息屏/锁屏下的生命周期,保障功耗和安全

PMS 与 WMS 交互的三层通知

层次 PMS → WMS WMS → PMS 目的
1 startedWakingUp() / finishedWakingUp() - 通知唤醒状态变化
2 screenTurningOn() screenOnListener.onScreenOn() 协调内容显示与屏幕点亮
3 screenTurnedOn() - 通知屏幕硬件已点亮

这个设计确保了:

  • 屏幕点亮前,内容已经准备好
  • 避免用户看到黑屏或未完成的绘制
  • Keyguard 的安全遮挡得到保证
相关推荐
Amos_Web2 小时前
Rspack 源码解析(十四):Resolver 与 NormalModuleFactory
前端·rust·源码
哈基米南北2 小时前
Activity Embedding 架构和应用启动流程
架构·源码
哈基米南北2 小时前
Android 输入法框架浅析
源码
哈基米南北2 小时前
Shell Transition 机制详解
源码
哈基米南北2 小时前
Android 启动窗口(Starting Window)技术文档
源码
哈基米南北2 小时前
PhoneWindow、DecorView 和 ViewRootImpl 的关系与工作原理
源码
哈基米南北5 天前
ViewRootImpl 事件分发责任链分析
源码
Amos_Web6 天前
Rspack 源码解析(十二):JavaScript Chunk 是如何被渲染出来的
前端·rust·源码
淡海水6 天前
13-04-面试-源码级深度追问链
数据结构·unity·面试·c#·游戏引擎·源码·il2cpp