【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();
}
相关推荐
PenguinLetsGo16 小时前
你的App是否有出现过幽灵调用?
android
没有了遇见17 小时前
Android ViewPager2 嵌套 RecyclerView 滑动冲突解决方案
android
咖啡の猫17 小时前
Android开发-选择按钮
android·gitee
火柴就是我18 小时前
android 以maven的方式 引入本地的aar
android
过-眼-云-烟18 小时前
新版Android Studio能打包但无法run ‘app‘,编译通过后手机中没有安装,顶部一直转圈
android·ide·android studio
hedalei19 小时前
android14 硬键盘ESC改BACK按键返回无效问题
android·android14·esc·back按键
hcgeng19 小时前
android 如何判定底部导航栏显示时 不是键盘显示
android·底部导航·导航高度
和煦的春风19 小时前
性能案例分析 | Waiting for GPU completion
android·linux
用户20187928316719 小时前
ConcurrentHashMap:用 “社区超市” 故事讲透并发的设计哲学
android
4Forsee19 小时前
【Android】View 交互的事件处理机制
android·交互