深入Android 12 Input系统:手把手调试‘BlockUntrustedTouches’的三种模式

在Android 12中,Input系统的安全机制迎来了一项重要升级—— BlockUntrustedTouches 策略。这项特性专门针对那些可能被恶意应用利用的触摸事件漏洞,为系统定制开发者和ROM开发者提供了更精细的触摸事件控制能力。本文将带你从源码层深入剖析这一机制,并通过实际操作演示如何调试和验证三种不同模式的效果。

1. BlockUntrustedTouches机制解析

Android 12引入的 BlockUntrustedTouches 机制核心目的是防止"点击劫持"攻击。这种攻击方式下,恶意应用可能通过覆盖透明或半透明窗口来窃取用户的触摸输入。系统通过 InputDispatcher.cpp 中的 findTouchedWindowTargetsLocked 函数实现这一防护。

三种工作模式的区别

模式 常量值 行为特征 适用场景
DISABLED 0 完全禁用拦截功能 兼容性测试场景
PERMISSIVE 1 记录但不拦截可疑触摸 调试过渡阶段
BLOCK 2 主动拦截可疑触摸事件 生产环境安全策略

在底层实现上,系统通过 InputWindowInfo 结构体中的关键属性来判断窗口的可信度:

// InputWindowInfo关键属性
struct InputWindowInfo {
    int32_t touchOcclusionMode;  // 触摸遮挡处理模式
    float alpha;                 // 窗口透明度
    bool visible;                // 窗口可见性
    bool trustedOverlay;         // 是否为可信覆盖层
    // ...其他属性
};

当触摸事件发生时,系统会执行以下判断逻辑:

  1. 检查 mBlockUntrustedTouchesMode 当前设置
  2. 计算触摸点的 TouchOcclusionInfo
  3. 根据 isTouchTrustedLocked 决定是否拦截

2. 调试环境搭建与模式切换

要有效调试 BlockUntrustedTouches 行为,我们需要配置完整的调试环境并掌握模式切换方法。

必备工具准备

  • 已root的Android 12设备或模拟器
  • ADB调试工具(建议使用最新版)
  • 开启开发者选项中的"指针位置"显示
  • 编译好的AOSP源码(用于查阅具体实现)

全局模式切换命令

# 查看当前模式
adb shell settings get global block_untrusted_touches

# 设置为DISABLED模式
adb shell settings put global block_untrusted_touches 0

# 设置为PERMISSIVE模式
adb shell settings put global block_untrusted_touches 1

# 设置为BLOCK模式
adb shell settings put global block_untrusted_touches 2

应用级例外设置

# 为特定应用禁用拦截
adb shell am compat disable BLOCK_UNTRUSTED_TOUCHES com.example.app

# 重置应用的拦截设置
adb shell am compat reset BLOCK_UNTRUSTED_TOUCHES com.example.app

提示:模式切换后需要重启相关Activity才能生效,可以通过 am restart 命令强制刷新。

3. TouchOcclusionInfo生成与验证

TouchOcclusionInfo 是判断是否拦截触摸事件的核心数据结构,了解其生成过程对调试至关重要。

关键字段解析

  • hasBlockingOcclusion :当上层窗口设置 touchOcclusionMode==BLOCK_UNTRUSTED 时为true
  • obscuringOpacity :基于窗口alpha值计算的累积不透明度
  • obscuringPackage/Uid :造成遮挡的应用信息

启用调试日志的方法:

# 启用触摸遮挡调试日志
adb shell setprop log.tag.InputDispatcher DEBUG

典型的日志分析要点:

  1. 查找 Untrusted touch due to occlusion 关键字
  2. 注意 obscuringPackage obscuringUid 的标识
  3. 观察 obscuringOpacity 与阈值(默认0.8)的比较

透明度计算示例 : 假设有两个重叠窗口:

  • 窗口A:alpha=0.5
  • 窗口B:alpha=0.6

实际计算方式为:

总不透明度 = 1 - (1 - 0.5) * (1 - 0.6) = 1 - 0.2 = 0.8

4. 实战调试案例与问题排查

在实际调试过程中,我们可能会遇到各种边界情况。以下是几个典型场景的调试方法。

案例1:意外拦截问题 症状:合法触摸事件被错误拦截 排查步骤:

  1. 检查 DEBUG_TOUCH_OCCLUSION 日志
  2. 确认窗口的 touchOcclusionMode 设置
  3. 验证 visible trustedOverlay 属性
  4. 检查是否有透明但未设置 ALLOW 模式的覆盖层

案例2:拦截失效问题 症状:应该拦截的触摸事件仍然通过 排查步骤:

  1. 确认全局模式是否为 BLOCK
  2. 检查目标应用是否被加入例外列表
  3. 验证 mMaximumObscuringOpacityForTouch 阈值
  4. 检查窗口层级关系是否正确

实用调试技巧

  • 使用 dumpsys input 命令获取当前Input状态
  • 通过 getevent -l 监控原始输入事件
  • 修改 frameworks/base/core/res/res/values/config.xml 中的默认阈值
  • InputDispatcher.cpp 中添加自定义日志输出

5. 高级定制与性能考量

对于系统定制开发者,可能需要深度调整 BlockUntrustedTouches 的行为。这里有几个进阶方向:

策略定制点

  1. 修改默认模式( Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE
  2. 调整透明度阈值( Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH
  3. 扩展例外应用列表( PACKAGE_BLOCKLIST_FOR_UNTRUSTED_TOUCHES_TOAST

性能优化建议

  • 避免在 computeTouchOcclusionInfoLocked 中添加复杂计算
  • 对频繁调用的窗口属性进行缓存
  • 在系统负载高时降级为 PERMISSIVE 模式
  • 优化 canBeObscuredBy 的判断逻辑

在Android 13中,这套机制又有了新的演进,包括:

  • 新增 FLOATING 模式处理悬浮窗口
  • 改进多显示器场景下的行为一致性
  • 增强 TouchOcclusionInfo 的调试信息

通过本文介绍的方法论,开发者可以建立起完整的 BlockUntrustedTouches 调试能力,无论是进行系统定制还是解决实际问题,都能做到有的放矢。

更多推荐