【SystemUI】锁屏来通知默认亮屏Wake模式

一、问题描述

基于 Android 14平台,锁屏状态下来通知时默认是进入Doze模式,此时屏幕不能点击只能查看通知信息且很快灭屏,用户体验不是很好,要求修改为通知直接亮屏。

二、问题分析

梳理锁屏状态下(特指设备息屏或处于Doze/AOD状态)接收到新通知的完整流程,可以帮助您更清晰地理解代码的脉络。这个流程横跨了Android Framework的核心服务和SystemUI。

流程概览

一个App发送通知 -> NotificationManagerService (系统服务) -> SystemUI (UI进程) -> 通知中断判断 -> Doze/亮屏决策 -> 执行动作 (Pulse或WakeUp)

阶段一:通知的发送与接收
  1. 应用层 -> 系统服务层

    • App : 调用 NotificationManager.notify() 方法发送一个通知。
    • Framework (NotificationManagerService.java) : 这是Android系统中负责管理所有通知的核心服务。
      • 方法 : enqueueNotificationInternal()
      • 作用: 接收来自App的通知请求,进行处理、权限检查、分组、排序(Ranking)等一系列操作,然后将通知"排入队列"准备分发。
  2. 系统服务层 -> SystemUI进程

    • SystemUI (NotificationListener.java) : SystemUI中有一个服务会继承 NotificationListenerService,用于监听系统中所有的通知事件。
      • 方法 : onNotificationPosted(StatusBarNotification sbn)
      • 作用 : 当NotificationManagerService处理完一个新通知后,会回调这个方法。这是通知进入SystemUI的第一个入口
階段二:SystemUI的内部处理与中断判断
  1. 通知信息收集与分发

    • SystemUI (NotifCollection.java) & (NotificationEntryManager.java) : NotificationListener 在收到通知后,会将其传递给这些管理器。
      • 作用 : 它们负责将StatusBarNotification对象包装成NotificationEntry对象,这个新对象包含了通知的所有信息和其在UI中的状态。然后,它们会将"有新通知"这个事件分发给各个监听者。
  2. 决定是否"打扰"用户(关键决策点)

    • SystemUI (NotificationInterruptStateProvider.java) : 这个类是决定一个新通知是否应该发出声音、振动或**点亮屏幕(Pulse)**的核心。
      • 方法 : shouldHeadsUp() / shouldPulse()
      • 作用: 它会根据通知的渠道、重要性(Importance)、用户设置(是否开启勿扰模式DND)、设备当前状态等一系列复杂的规则,来判断这个通知是否应该以"抬头通知"(Heads-Up Notification)的形式出现,或者是否应该触发一次Doze Pulse。
阶段三:从"打扰"到"亮屏"的决策传递
  1. 触发Doze Host回调

    • SystemUI (StatusBarNotificationPresenter.java) & (DozeService.java) : 当NotificationInterruptStateProvider判断一个通知应该触发Pulse时,它会通过一系列调用,最终通知到DozeHost
    • 接口 : DozeHost.Callback
      • 方法 : onNotificationAlerted(Runnable onPulseSuppressedListener)
      • 作用: 这是一个回调方法,字面意思就是"被通知提醒了"。它标志着"有一个通知需要唤醒屏幕"这个信号正式传递到了Doze管理体系中。
  2. DozeTriggers接收信号(您正在修改的地方)

    • SystemUI (DozeTriggers.java) : 这个类实现了DozeHost.Callback接口,是各种Doze触发器(传感器、通知、充电等)的集中处理地。
      • 成员 : mHostCallback (一个DozeHost.Callback的实例)
      • 方法 : onNotification(Runnable onPulseSuppressedListener)
      • 作用 : mHostCallback在被调用时,会直接执行这个onNotification方法。这就是整个流程的汇合点,也是您进行修改的最佳位置。之前所有的判断和流程,最终都会走到这里,请求一次屏幕状态的改变。
阶段四:执行最终动作(Pulse或WakeUp)
  1. DozeTriggers.onNotification() 内部

    • 原始逻辑 :
      • 方法 : requestPulse(...)
      • 作用 : 这个方法会请求Doze状态机执行一次"脉冲"。它内部会进一步调用proximityCheckThenCall来检查近距离传感器,如果检查通过,最终会调用mMachine.requestPulse(reason)
    • 您的修改逻辑 :
      • 方法 : mMachine.wakeUp(reason)
      • 作用: 这个方法会请求Doze状态机直接"唤醒"。
  2. 状态机执行命令

    • SystemUI (DozeMachine.java) : 这是一个状态机,负责管理设备在INITIALIZED, DOZE, DOZE_AOD, DOZE_PULSING, FINISH等各种Doze状态之间的转换。
      • 方法 : requestPulse(int reason)
        • 效果 : 将状态切换到 DOZE_PULSING。这会触发DozeService在屏幕上绘制AOD通知界面,短暂亮起后熄灭。
      • 方法 : wakeUp(int reason)
        • 效果 : 将状态切换到 FINISH。这会退出整个Doze流程,并最终通过PowerManager完全点亮屏幕,显示锁屏界面(Keyguard)。

总结

所以,整个流程可以精简为:

NotificationManagerService.enqueueNotificationInternal()
->
NotificationListener.onNotificationPosted()
->
NotificationInterruptStateProvider.shouldPulse()
->
DozeHost.Callback.onNotificationAlerted()
->
DozeTriggers.onNotification()

三、解决方案

DozeTriggers.requestPulse() -> DozeMachine.requestPulse() -> 屏幕进入Pulse状态
DozeTriggers.mMachine.wakeUp() -> DozeMachine.wakeUp() -> 屏幕被完全唤醒

src/com/android/systemui/doze/DozeTriggers.java

java 复制代码
private void onNotification(Runnable onPulseSuppressedListener) {
    // Bug #2190530 [Android14][AR.599.001744.006101.016207][SystemUI] Dimension Log Configuration for SystemUI
    if (UniSystemuiComponentFactory.getInstance().getDebugConfigs("DozeMachine")) {
        Log.d(TAG, "requestNotificationPulse");
    }
    if (!sWakeDisplaySensorState) {
        Log.d(TAG, "Wake display false. Pulse denied.");
        runIfNotNull(onPulseSuppressedListener);
        mDozeLog.tracePulseDropped("wakeDisplaySensor");
        return;
    }
    mNotificationPulseTime = SystemClock.elapsedRealtime();
    if (!mConfig.pulseOnNotificationEnabled(mUserTracker.getUserId())) {
        runIfNotNull(onPulseSuppressedListener);
        mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
        return;
    }
    if (mDozeHost.isAlwaysOnSuppressed()) {
        runIfNotNull(onPulseSuppressedListener);
        mDozeLog.tracePulseDropped("dozeSuppressed");
        return;
    }
    - requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */,
    -         onPulseSuppressedListener);
    + mMachine.wakeUp(DozeLog.PULSE_REASON_NOTIFICATION);
    mDozeLog.traceNotificationPulse();
}
相关推荐
Andy_GF2 小时前
鸿蒙Next在蒲公英平台分发测试包
android·ios·harmonyos
恋猫de小郭3 小时前
iOS 26 正式版即将发布,Flutter 完成全新 devicectl + lldb 的 Debug JIT 运行支持
android·前端·flutter
幻雨様4 小时前
UE5多人MOBA+GAS 54、用户登录和会话创建请求
android·ue5
没有了遇见4 小时前
Android +,++,+= 的区别
android·kotlin
_无_妄_5 小时前
Android 使用 WebView 直接加载 PDF 文件,通过 JS 实现
android
VomPom5 小时前
手写一个精简版Koin:深入理解依赖注入核心原理
android
IT乐手6 小时前
Java 编写查看调用栈信息
android
Digitally7 小时前
如何轻松永久删除 Android 手机上的短信
android·智能手机