<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: BeWaterMF</title>
    <description>The latest articles on DEV Community by BeWaterMF (@_e417c606aaf074cdd973d8).</description>
    <link>https://dev.to/_e417c606aaf074cdd973d8</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4215394%2F2490d074-9ac1-4b70-a884-7538d1558779.jpg</url>
      <title>DEV Community: BeWaterMF</title>
      <link>https://dev.to/_e417c606aaf074cdd973d8</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_e417c606aaf074cdd973d8"/>
    <language>en</language>
    <item>
      <title>AOSP 17 Toast 锁屏可见问题分析：WindowManagerPolicy layer 变更引入的 BUG</title>
      <dc:creator>BeWaterMF</dc:creator>
      <pubDate>Thu, 29 Oct 2026 15:19:56 +0000</pubDate>
      <link>https://dev.to/_e417c606aaf074cdd973d8/aosp-17-toast-suo-ping-ke-jian-wen-ti-fen-xi-windowmanagerpolicy-layer-bian-geng-yin-ru-de-bug-5h4</link>
      <guid>https://dev.to/_e417c606aaf074cdd973d8/aosp-17-toast-suo-ping-ke-jian-wen-ti-fen-xi-windowmanagerpolicy-layer-bian-geng-yin-ru-de-bug-5h4</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;1. 问题描述&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Android 17 中存在一个窗口层级相关的回归问题：默认文本 Toast 和持有 &lt;code&gt;INTERNAL_SYSTEM_WINDOW&lt;/code&gt; 权限的系统应用发出的自定义 Toast 在锁屏后仍然可见，而 Android 16 及更早版本中，锁屏时所有 Toast 均会被 Keyguard 正确覆盖。&lt;/p&gt;

&lt;p&gt;复现步骤：在任意应用中触发标准文本 Toast 后立即按电源键锁屏，可观察到 Toast 内容持续显示在锁屏界面上方。使用系统签名应用发出自定义 Toast 同样可复现。&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. 根因定位&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2.1 相关代码位置&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;问题位于 &lt;code&gt;frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java&lt;/code&gt;。&lt;code&gt;WindowManagerPolicy&lt;/code&gt; 是一个 Java &lt;code&gt;interface&lt;/code&gt;，继承自 &lt;code&gt;WindowManagerPolicyConstants&lt;/code&gt;。其中的 &lt;code&gt;default&lt;/code&gt; 方法 &lt;code&gt;getWindowLayerFromTypeLw&lt;/code&gt; 负责将窗口类型映射到 Z 轴层级，决定各窗口在屏幕上的显示顺序。&lt;/p&gt;

&lt;p&gt;该方法的完整签名为：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0uirzto8l3l3cddhr590.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0uirzto8l3l3cddhr590.png" alt="img-1.png" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;其中 &lt;code&gt;canAddInternalSystemWindow&lt;/code&gt; 参数反映窗口所属进程是否持有 &lt;code&gt;android.Manifest.permission.INTERNAL_SYSTEM_WINDOW&lt;/code&gt; 权限。该参数由 &lt;code&gt;WindowState.canAddInternalSystemWindow()&lt;/code&gt; 提供：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp3zarsmfukb2z55dz6jd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp3zarsmfukb2z55dz6jd.png" alt="img-2.png" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2.2 Android 16 及之前的实现&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;在 AOSP 16（&lt;code&gt;android-16.0.0_r1&lt;/code&gt;）中，&lt;code&gt;TYPE_TOAST&lt;/code&gt; 分支直接返回固定值 &lt;code&gt;7&lt;/code&gt;，不使用 &lt;code&gt;canAddInternalSystemWindow&lt;/code&gt; 参数：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjgt6ooo73djpqmv410w2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjgt6ooo73djpqmv410w2.png" alt="img-3.png" width="799" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;TYPE_TOAST&lt;/code&gt; 返回的 layer 值为 &lt;code&gt;7&lt;/code&gt;。而锁屏及通知栏对应的 &lt;code&gt;TYPE_NOTIFICATION_SHADE&lt;/code&gt; 返回 &lt;code&gt;17&lt;/code&gt;。由于 &lt;code&gt;7 &amp;lt; 17&lt;/code&gt;，Toast 窗口在 Z 轴上位于锁屏窗口之下，锁屏时会被正确覆盖。&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2.3 Android 17 的变更&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;在 AOSP 17（&lt;code&gt;android-17.0.0_r1&lt;/code&gt;）中，&lt;code&gt;TYPE_TOAST&lt;/code&gt; 分支被修改为使用 &lt;code&gt;canAddInternalSystemWindow&lt;/code&gt; 参数进行条件判断。实际代码如下：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flq05o837u2h7uipnkd62.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flq05o837u2h7uipnkd62.png" alt="img-4.png" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;变更逻辑为：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;若窗口所属进程持有 &lt;code&gt;INTERNAL_SYSTEM_WINDOW&lt;/code&gt; 权限 → 返回 &lt;code&gt;27&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;否则 → 保持原有的 &lt;code&gt;7&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;从注释可以看出，该改动的设计意图是区分「旧式 Toast」（legacy toast，可能携带任意 View，存在触控劫持风险）和「系统托管的 Toast」（system-managed toast，安全可控）。旧式 Toast 保持低层级，系统托管的 Toast 获得更高可见性。&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2.4 Layer 层级对比&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;变更前后的关键窗口层级如下：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Layer&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;窗口类型&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;说明&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;TYPE_TOAST（无权限）&lt;/td&gt;
&lt;td&gt;Toast（旧行为及新行为中无权限情形）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;TYPE_NOTIFICATION_SHADE&lt;/td&gt;
&lt;td&gt;通知面板 / 锁屏&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;TYPE_TOAST（有权限）&lt;/td&gt;
&lt;td&gt;Toast（新行为中有权限情形）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;27 &amp;gt; 17&lt;/strong&gt;，因此被分配 layer 27 的 Toast 窗口在 Z 序上高于锁屏窗口。&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2.5 问题的直接原因&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;layer 27 的分配并非无条件对所有 Toast 生效，而是按窗口的实际创建进程是否满足 &lt;code&gt;canAddInternalSystemWindow&lt;/code&gt; 条件来决定。在 Android 17 的架构下，以下两类 Toast 会命中 &lt;code&gt;canAddInternalSystemWindow ? 27 : 7&lt;/code&gt; 的 true 分支：&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2.5.1 默认文本 Toast：SystemUI 代理渲染（Android 12+）&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Android 系统从 Android 12 开始强制应用使用系统的 Toast 渲染管道：当任意应用调用 &lt;code&gt;Toast.makeText().show()&lt;/code&gt; 时，Toast 的实际渲染由 SystemUI 进程（&lt;code&gt;com.android.systemui&lt;/code&gt;）执行。&lt;/p&gt;

&lt;p&gt;因此在 &lt;code&gt;WindowManagerPolicy.getWindowLayerFromTypeLw&lt;/code&gt; 中：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Toast 窗口由 SystemUI 进程创建。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WindowState.canAddInternalSystemWindow()&lt;/code&gt; 对 SystemUI 返回 &lt;code&gt;true&lt;/code&gt;（SystemUI 持有 &lt;code&gt;INTERNAL_SYSTEM_WINDOW&lt;/code&gt; 权限）。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TYPE_TOAST&lt;/code&gt; 分支命中 &lt;code&gt;canAddInternalSystemWindow ? 27 : 7&lt;/code&gt; 的 true 分支。&lt;/li&gt;
&lt;li&gt;返回 layer 27，高于 &lt;code&gt;TYPE_NOTIFICATION_SHADE&lt;/code&gt; 的 layer 17。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;关键问题在于：&lt;strong&gt;权限检查的对象是 Toast 窗口的实际渲染进程（SystemUI），而非发起 Toast 请求的原始应用&lt;/strong&gt;。无论第三方应用是否有系统权限，其文本 Toast 都会经由 SystemUI 取得 layer 27 的层级提升。&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2.5.2 有 &lt;code&gt;INTERNAL_SYSTEM_WINDOW&lt;/code&gt; 权限的应用的自定义 Toast&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;对于通过 &lt;code&gt;setView()&lt;/code&gt; 设置自定义视图的 Toast，窗口仍由应用自身进程创建（不走 SystemUI 代理）。此时 &lt;code&gt;canAddInternalSystemWindow&lt;/code&gt; 直接反映调用进程的权限：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;持有 &lt;code&gt;INTERNAL_SYSTEM_WINDOW&lt;/code&gt; 权限的系统签名应用 / privileged 应用调用 &lt;code&gt;Toast.setView().show()&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WindowState.canAddInternalSystemWindow()&lt;/code&gt; 对调用进程返回 &lt;code&gt;true&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TYPE_TOAST&lt;/code&gt; 分支命中 &lt;code&gt;canAddInternalSystemWindow ? 27 : 7&lt;/code&gt; 的 true 分支。&lt;/li&gt;
&lt;li&gt;返回 layer 27，高于 &lt;code&gt;TYPE_NOTIFICATION_SHADE&lt;/code&gt; 的 layer 17。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;而对于&lt;strong&gt;普通第三方应用&lt;/strong&gt;发出的自定义 Toast，&lt;code&gt;canAddInternalSystemWindow&lt;/code&gt; 返回 &lt;code&gt;false&lt;/code&gt;，layer 保持 7，行为与 Android 16 一致——锁屏时仍被遮挡。&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2.6 第二块拼图：DisplayPolicy.applyKeyguardPolicy 如何将 layer 变更兑现为可见性&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;2.3 节回答了「为什么 Toast 的 layer 变成了 27」，本节回答「layer 27 为什么导致锁屏可见」——后者是 &lt;code&gt;DisplayPolicy.applyKeyguardPolicy()&lt;/code&gt; 的职责。&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2.6.1 applyKeyguardPolicy —— 隐藏决策入口&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;DisplayPolicy.applyKeyguardPolicy()&lt;/code&gt; 在锁屏状态变化时遍历所有窗口，调用 &lt;code&gt;canBeHiddenByKeyguard()&lt;/code&gt; 逐窗口判断是否应被 Keyguard 遮挡：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6rqe0quuenwqps7ca7k2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6rqe0quuenwqps7ca7k2.png" alt="img-5.png" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2.6.2 canBeHiddenByKeyguard —— 16 和 17 代码一致，未变更&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;本节关键结论：&lt;code&gt;canBeHiddenByKeyguard()&lt;/code&gt; 在 Android 16 和 17 中的代码完全相同&lt;/strong&gt;，并未发生任何修改。两版源码一致为：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frkja9s8kk7muv9n1rcsd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frkja9s8kk7muv9n1rcsd.png" alt="img-6.png" width="799" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;TYPE_TOAST&lt;/code&gt; 不在 &lt;code&gt;switch&lt;/code&gt; 的任何 &lt;code&gt;case&lt;/code&gt; 分支中，落入 &lt;code&gt;default&lt;/code&gt;。&lt;code&gt;default&lt;/code&gt; 的语义为：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;窗口的实际 layer 低于锁屏窗口（TYPE_NOTIFICATION_SHADE）的 layer 时，才被隐藏。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这是 Android 16 就已经存在的逻辑。&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2.6.3 为什么 16 上没问题、17 上出问题了？&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;答案很简单：&lt;strong&gt;layer 变了。&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;版本&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Toast Layer&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;比较 &lt;code&gt;layer &amp;lt; 17&lt;/code&gt;&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;canBeHiddenByKeyguard 返回值&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;锁屏行为&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Android 16&lt;/td&gt;
&lt;td&gt;7（固定）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;7 &amp;lt; 17&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;= &lt;code&gt;true&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Toast 被隐藏 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android 17（无权限）&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;7 &amp;lt; 17&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;= &lt;code&gt;true&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Toast 被隐藏 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android 17（SystemUI / 系统签名应用）&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;&lt;code&gt;27 &amp;lt; 17&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;= &lt;code&gt;false&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Toast 不被隐藏 ❌&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;layer 从 7 升至 27 是唯一的变更点。&lt;/strong&gt;&lt;code&gt;canBeHiddenByKeyguard()&lt;/code&gt; 自身未变——它始终做 layer 比较。在 16 上比较结果是 &lt;code&gt;true&lt;/code&gt;，在 17 上当 &lt;code&gt;canAddInternalSystemWindow&lt;/code&gt; = &lt;code&gt;true&lt;/code&gt; 时比较结果变为 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;canAddInternalSystemWindow()&lt;/code&gt; 的实现只是成员变量 getter，在窗口创建时由进程权限决定：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftj1worzujegij7jq4onk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftj1worzujegij7jq4onk.png" alt="img-7.png" width="800" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SystemUI 进程持有 &lt;code&gt;INTERNAL_SYSTEM_WINDOW&lt;/code&gt; 权限 → &lt;code&gt;mOwnerCanAddInternalSystemWindow&lt;/code&gt; = &lt;code&gt;true&lt;/code&gt; → &lt;code&gt;getWindowLayerFromTypeLw&lt;/code&gt; 返回 27 → &lt;code&gt;27 &amp;lt; 17&lt;/code&gt; = &lt;code&gt;false&lt;/code&gt; → &lt;code&gt;canBeHiddenByKeyguard&lt;/code&gt; 返回 &lt;code&gt;false&lt;/code&gt; → Toast 穿透锁屏。&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2.6.4 完整因果链&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.&amp;nbsp;App&amp;nbsp;调用&amp;nbsp;Toast.makeText().show()&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓2.&amp;nbsp;SystemUI&amp;nbsp;代理渲染&amp;nbsp;Toast&amp;nbsp;窗口&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓3.&amp;nbsp;WindowManagerPolicy.getWindowLayerFromTypeLw(TYPE_TOAST,&amp;nbsp;canAddInternalSystemWindow=true)&amp;nbsp;&amp;nbsp;&amp;nbsp;★&amp;nbsp;唯一变更点：AOSP&amp;nbsp;16&amp;nbsp;固定返回&amp;nbsp;7&amp;nbsp;→&amp;nbsp;AOSP&amp;nbsp;17&amp;nbsp;改为&amp;nbsp;canAddInternalSystemWindow&amp;nbsp;?&amp;nbsp;27&amp;nbsp;:&amp;nbsp;7&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓4.&amp;nbsp;Toast&amp;nbsp;窗口获得&amp;nbsp;layer&amp;nbsp;=&amp;nbsp;27（高于&amp;nbsp;TYPE_NOTIFICATION_SHADE&amp;nbsp;的&amp;nbsp;17）&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓5.&amp;nbsp;DisplayPolicy.applyKeyguardPolicy()&amp;nbsp;遍历窗口&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓6.&amp;nbsp;WindowState.canBeHiddenByKeyguard()&amp;nbsp;判断&amp;nbsp;&amp;nbsp;&amp;nbsp;→&amp;nbsp;default&amp;nbsp;分支：layer&amp;nbsp;(27)&amp;nbsp;&amp;lt;&amp;nbsp;TYPE_NOTIFICATION_SHADE&amp;nbsp;layer&amp;nbsp;(17)&amp;nbsp;?&amp;nbsp;&amp;nbsp;&amp;nbsp;→&amp;nbsp;27&amp;nbsp;&amp;lt;&amp;nbsp;17&amp;nbsp;=&amp;nbsp;false&amp;nbsp;&amp;nbsp;&amp;nbsp;→&amp;nbsp;返回&amp;nbsp;false（不隐藏）&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓7.&amp;nbsp;Toast&amp;nbsp;穿透锁屏，锁屏后仍可见&amp;nbsp;❌
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;整条链路的唯一变更点在第三步——&lt;code&gt;getWindowLayerFromTypeLw&lt;/code&gt; 中 &lt;code&gt;TYPE_TOAST&lt;/code&gt; 分支从固定返回 7 改为 &lt;code&gt;canAddInternalSystemWindow ? 27 : 7&lt;/code&gt;。后续所有方法代码均未变动，只是输入（layer 值）不同导致输出不同。&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;3. 影响范围&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;该问题影响以下两类 Toast：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;所有应用发出的默认文本 Toast&lt;/strong&gt;（&lt;code&gt;Toast.makeText()&lt;/code&gt;）—— 因为 Android 12+ 强制 SystemUI 代理所有文本 Toast 的渲染，SystemUI 持有 &lt;code&gt;INTERNAL_SYSTEM_WINDOW&lt;/code&gt; 权限，所有文本 Toast 被分配 layer 27。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;持有 &lt;code&gt;INTERNAL_SYSTEM_WINDOW&lt;/code&gt; 权限的应用发出的自定义 Toast&lt;/strong&gt; —— 此类应用（系统签名应用 / privileged 应用）自身即满足 &lt;code&gt;canAddInternalSystemWindow&lt;/code&gt; 条件，其自定义 Toast 同样被分配 layer 27。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;不受影响&lt;/strong&gt;：普通第三方应用通过 &lt;code&gt;setView()&lt;/code&gt; 发出的自定义 Toast（layer 保持 7，仍被锁屏遮挡）。&lt;/p&gt;

&lt;p&gt;当上述两类 Toast 显示期间用户锁屏时，Toast 内容将暴露在锁屏界面之上。&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. 修复建议&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;问题的根因是 &lt;code&gt;getWindowLayerFromTypeLw&lt;/code&gt; 中 &lt;code&gt;TYPE_TOAST&lt;/code&gt; 被分配了高于锁屏的 layer 27，而 &lt;code&gt;canBeHiddenByKeyguard()&lt;/code&gt; 的 &lt;code&gt;default&lt;/code&gt; 分支仅做 layer 比较，未对 Toast 做特殊处理，导致 layer 27 的 Toast 穿透锁屏。&lt;/p&gt;

&lt;p&gt;最简洁的修复方案是在 &lt;code&gt;canBeHiddenByKeyguard()&lt;/code&gt; 中为 &lt;code&gt;TYPE_TOAST&lt;/code&gt; 新增一个 &lt;code&gt;case&lt;/code&gt; 分支，直接返回 &lt;code&gt;true&lt;/code&gt;——因为 Android 16 中 Toast 的实际行为就是始终被 Keyguard 隐藏。这样无需改动 layer 分配逻辑，也不影响 layer 27 在其他场景下的作用（如非锁屏时的 Z 序提升）。&lt;/p&gt;

&lt;p&gt;修改位置：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flei3qc511lidx4z6xahu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flei3qc511lidx4z6xahu.png" alt="img-8.png" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;为什么不在 &lt;code&gt;getWindowLayerFromTypeLw&lt;/code&gt; 中回退 layer？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;getWindowLayerFromTypeLw&lt;/code&gt; 中的 layer 分配影响 Z 序（非锁屏场景下决定谁覆盖谁），有其独立的设计意图——系统托管的 Toast 在正常使用场景下确实应该高于某些悬浮窗。在 &lt;code&gt;canBeHiddenByKeyguard&lt;/code&gt; 中处理，语义上更精确：&lt;strong&gt;「Toast 在锁屏时应当被隐藏」是一个锁屏策略决策，而非层级分配问题。&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;5. 总结&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;这不是一个由多条独立链路并发变更导致的复杂回归。根因只有一个：&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;唯一变更点：&lt;code&gt;WindowManagerPolicy.getWindowLayerFromTypeLw&lt;/code&gt; 中 &lt;code&gt;TYPE_TOAST&lt;/code&gt; 分支&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;AOSP 17 将 &lt;code&gt;TYPE_TOAST&lt;/code&gt; 分支从固定返回 7 改为 &lt;code&gt;canAddInternalSystemWindow ? 27 : 7&lt;/code&gt;。这一变更的意图是合理的——区分旧式 Toast（legacy，存在触控劫持风险）和系统托管的 Toast（system-managed，安全可控），给后者更高层级。&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;为什么波及默认文本 Toast 和有 &lt;code&gt;INTERNAL_SYSTEM_WINDOW&lt;/code&gt; 权限的应用？&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Android 12+ 强制 SystemUI 代理所有文本 Toast 的渲染。此外，持有 &lt;code&gt;INTERNAL_SYSTEM_WINDOW&lt;/code&gt; 权限的系统签名应用发出的自定义 Toast 也直接满足条件。两者的共同点是 &lt;code&gt;canAddInternalSystemWindow&lt;/code&gt; 为 &lt;code&gt;true&lt;/code&gt;，因此 &lt;code&gt;getWindowLayerFromTypeLw&lt;/code&gt; 返回 27。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;canAddInternalSystemWindow&lt;/code&gt; 的取值来源于窗口的实际创建进程（SystemUI）或持权应用本身，因此所有文本 Toast 和有权限的自定义 Toast 都被分配 layer 27。&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;为什么 layer 27 导致锁屏可见？&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DisplayPolicy.applyKeyguardPolicy()&lt;/code&gt; 通过 &lt;code&gt;canBeHiddenByKeyguard()&lt;/code&gt; 判断是否隐藏窗口。后者在 Android 16/17 中&lt;strong&gt;代码完全一致&lt;/strong&gt;，其逻辑为：窗口的实际 layer &amp;lt; 锁屏层（17）时隐藏。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;layer 7 → &lt;code&gt;7 &amp;lt; 17&lt;/code&gt; = &lt;code&gt;true&lt;/code&gt; → 隐藏（Android 16 行为）&lt;/li&gt;
&lt;li&gt;layer 27 → &lt;code&gt;27 &amp;lt; 17&lt;/code&gt; = &lt;code&gt;false&lt;/code&gt; → &lt;strong&gt;不隐藏&lt;/strong&gt;（Android 17 问题）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;canBeHiddenByKeyguard&lt;/code&gt; 本身未变，变更的只是传入的 layer 值。&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;完整因果链&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Toast.makeText().show()&amp;nbsp;&amp;nbsp;
   →&amp;nbsp;SystemUI&amp;nbsp;代理渲染&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
     →&amp;nbsp;WindowManagerPolicy.getWindowLayerFromTypeLw()&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
       →&amp;nbsp;canAddInternalSystemWindow&amp;nbsp;=&amp;nbsp;true（SystemUI&amp;nbsp;持有权限）&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
         →返回&amp;nbsp;layer&amp;nbsp;27（AOSP&amp;nbsp;17&amp;nbsp;新增逻辑；AOSP&amp;nbsp;16&amp;nbsp;固定返回&amp;nbsp;7）&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
           →&amp;nbsp;DisplayPolicy.applyKeyguardPolicy()&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
            →&amp;nbsp;canBeHiddenByKeyguard()&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
              →&amp;nbsp;layer(27)&amp;nbsp;&amp;lt;&amp;nbsp;LOCK_SCREEN_LAYER(17)&amp;nbsp;?&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
                 →&amp;nbsp;false&amp;nbsp;→&amp;nbsp;不隐藏&amp;nbsp;→&amp;nbsp;Toast&amp;nbsp;穿透锁屏&amp;nbsp;❌
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;在 AOSP 源码中可通过以下路径定位相关代码：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;文件：&lt;code&gt;frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;方法：&lt;code&gt;getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow, boolean roundedCornerOverlay)&lt;/code&gt;（变更点）&lt;/li&gt;
&lt;li&gt;文件：&lt;code&gt;frameworks/base/services/core/java/com/android/server/wm/WindowState.java&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;• &lt;code&gt;canAddInternalSystemWindow()&lt;/code&gt; 方法&lt;/p&gt;

&lt;p&gt;• &lt;code&gt;canBeHiddenByKeyguard()&lt;/code&gt; 方法（16/17 代码未变）&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;文件：&lt;code&gt;frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;• &lt;code&gt;applyKeyguardPolicy()&lt;/code&gt; 方法&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;层级常量：&lt;code&gt;frameworks/base/core/java/android/view/WindowManagerPolicyConstants.java&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;本文基于 AOSP android-16.0.0_r1 与 android-17.0.0_r1 源码对比分析。&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>debugging</category>
      <category>java</category>
      <category>android</category>
    </item>
  </channel>
</rss>
