【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();
}
相关推荐
selt7913 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
Yao_YongChao4 小时前
Android MVI处理副作用(Side Effect)
android·mvi·mvi副作用
非凡ghost5 小时前
JRiver Media Center(媒体管理软件)
android·学习·智能手机·媒体·软件需求
席卷全城5 小时前
Android 推箱子实现(引流文章)
android
齊家治國平天下5 小时前
Android 14 系统中 Tombstone 深度分析与解决指南
android·crash·系统服务·tombstone·android 14
maycho1237 小时前
MATLAB环境下基于双向长短时记忆网络的时间序列预测探索
android
思成不止于此8 小时前
【MySQL 零基础入门】MySQL 函数精讲(二):日期函数与流程控制函数篇
android·数据库·笔记·sql·学习·mysql
brave_zhao8 小时前
达梦数据库(DM8)支持全文索引功能,但并不直接兼容 MySQL 的 FULLTEXT 索引语法
android·adb
sheji34168 小时前
【开题答辩全过程】以 基于Android的网上订餐系统为例,包含答辩的问题和答案
android
easyboot9 小时前
C#使用SqlSugar操作mysql数据库
android·sqlsugar