【全栈第23课】实战:触控失灵从 App 一路追到 I2C
Android 全栈工程师进阶教程 第23课。实战:触控失灵从 App 一路追到 I2C。基于公开 AOSP 知识整理,含原理、可编译示例、常见问题定位。
> 本文是《Android 全栈工程师进阶教程》系列第 23 课。完整 26 课见 GitHub 仓库。
【全栈第23课】实战:触控失灵从 App 一路追到 I2C
0. 这节课你将搞懂什么
拿一个真实的高频问题"屏幕点击没反应",从 App 一层层追到最底层的 I2C,把前面所有课的知识和 L22 的武器库实战串起来。 学完你能回答:用户报"屏幕失灵",我作为 input 工程师该怎么系统地查?怎么快速判断是 App 拦了、还是 framework 没派发、还是驱动没上报、还是硬件挂了? 这是 示例项目/input 域最高频的 ISS 单类型,学会这套你能独立接单。
1. 背景:触控失灵是 input 域第一高频问题
"屏幕某区域/整屏点击没反应"——这是输入子系统最常见的用户反馈。难点在于:触控链路极长(App→IMS→input 子系统→驱动→I2C→IC),任何一环断了都表现为"失灵"。乱查会浪费大量时间,必须有系统的追查方法。
2. 全局图:触控事件的完整链路(也是追查路线)
触控 IC(硬件)
▲ 手指触摸 → IC 产生坐标数据
│ ⑥ I2C 总线:IC 通过 I2C 把数据给 SoC(L16)
│ ⑤ kernel 触控驱动:中断 → 读 I2C → input_report 上报(L14/L16/L17)
│ ④ input 子系统:/dev/input/eventX 产生 event
│ ③ IMS(InputManagerService):InputReader 读 event → InputDispatcher 派发
│ ② WMS:确定派给哪个窗口(焦点)
│ ① App:onTouchEvent 处理
▼
追查方向:从上(App)往下(I2C)逐层确认"事件到这层了没"
3. 追查路线(逐层排除,每层一个判定命令)
App 收不到触摸
├─ ① App 层:getevent 看原始事件有没有
│ adb shell getevent -lt /dev/input/event<X> # 触摸屏幕,看有没有 event 刷出来
│ 有事件 → App 层问题(窗口焦点/触摸区域/onTouch 拦截)→ 查 App
│ 没事件 → 事件没上来,继续往下
├─ ② IMS/Framework 层
│ adb shell dumpsys input # InputReader/Dispatcher 状态、有没有这个设备
│ adb logcat | grep -iE "InputDispatcher|ANR" # 主线程卡(L4)导致派发超时?
│ IMS 收到但没派发 → 窗口/焦点问题(WMS)
│ IMS 没收到 → 继续往下
├─ ③ input 子系统
│ adb shell cat /proc/bus/input/devices # 触控 input device 注册了吗
│ adb shell ls /dev/input/ # eventX 节点在吗
│ 节点不在 → 驱动没注册 input device(L14/L17)
├─ ④ kernel 驱动层
│ adb shell dmesg | grep -iE "touch|tp|focal|goodix|i2c"
│ probe 失败?→ 驱动没加载(L15 compatible?L13 modules.load?)
│ 中断没来?→ cat /proc/interrupts(L17 极性/GPIO?)
│ I2C 读失败?→ 继续往下(L16)
├─ ⑤ I2C 总线层
│ adb shell dmesg | grep -iE "i2c.*timeout|transfer error|nack"
│ adb shell i2cdetect -y <bus> # IC 地址在总线上吗(L16)
│ 扫不到 IC → 硬件层
└─ ⑥ 硬件层
示波器看 I2C SCL/SDA 波形:
没波形 → IC 没上电(regulator?L18 电源没开?)
有波形但 NAK → IC 地址错/IC 挂了/排线松
4. 前置准备
- 依赖 L14-L18(驱动/I2C/中断/电源)、L22(武器库)、L4(IMS/ANR)。
- 本课是实战方法,产出追查 SOP 文档。
5. 动手:跟着追一遍(假设用户报整屏失灵)
第 1 步:getevent —— 划清"驱动以下"还是"framework 以上"(最关键的分水岭)
adb shell getevent -lt # 列出所有 input 设备,找触摸屏那个 eventX
adb shell getevent -lt /dev/input/event3 # 假设触摸屏是 event3,触摸屏幕观察
- 屏幕上有 ABS_MT_POSITION 之类的 event 刷出来 → 驱动/硬件正常,问题在 framework/App 以上(查 ②①)。
- 完全没 event → 事件根本没产生/上报,问题在驱动/硬件(查 ③④⑤⑥)。
这一步把问题范围砍掉一半,是触控失灵定位最重要的一刀。
第 2 步(若 getevent 有事件,往上查 framework):
adb shell dumpsys input | grep -A10 "Touch\|Device" # 设备状态、能力
adb logcat | grep -iE "InputDispatcher|not responding" # 派发超时/ANR(L4)
adb shell dumpsys window | grep -iE "mFocusedWindow" # 当前焦点窗口对不对
- 焦点窗口不对/被某个窗口挡住 → 窗口层问题(WMS);Dispatcher ANR → 主线程卡(L4)。
第 3 步(若 getevent 没事件,往下查驱动/硬件):
adb shell cat /proc/bus/input/devices | grep -A5 touch # input device 在吗
adb shell dmesg | grep -iE "touch|focal|goodix|i2c" # 驱动 probe/中断/I2C 报错
adb shell cat /proc/interrupts | grep -i touch # 中断计数涨吗(触摸时)
adb shell i2cdetect -y <bus> # IC 在 I2C 总线上吗
- 一路往下,第一个"不正常"的层就是断点。最后到示波器看波形(硬件层)。
6. 看底层:一次触摸事件的内核之旅
手指按下 → 触控 IC 检测到 → 拉中断脚(L17)
→ 驱动上半部 schedule_work → 下半部 i2c/regmap_read 读坐标(L16)
→ input_report_abs(dev, ABS_MT_POSITION_X, x) 上报坐标(input 子系统)
→ /dev/input/eventX 产生 event
→ IMS 的 InputReader 读 event → InputDispatcher 找焦点窗口派发(L4 消息机制)
→ App 的 ViewRootImpl → onTouchEvent
理解这条链,getevent 在第④步("event 产生了没"),正好卡在驱动和 framework 的交界——所以它能一刀分清上下。
7. 编译 & 运行(本课无编译产物)
见第 5 节命令,真机上跟着追。 > 验证状态(诚实):本课为实战方法论,无编译产物;命令为标准 input 调试命令,示例项目/input ISS 单实际就这么查。
8. 踩坑提醒
- 不先 getevent 就乱猜:不划上下分水岭,在 framework 和驱动之间反复横跳浪费时间。getevent 是第一刀。
- event 编号找错:
/dev/input/下有多个 eventX(触摸/按键/传感器),getevent -lt(不带具体设备)先看哪个是触摸屏。 - 局部失灵 vs 整屏失灵:局部失灵(某区域)往往是 IC 坏点/校准问题(硬件),整屏失灵更可能是驱动/通信。现象描述要细。
- getevent 有事件就赖驱动:有事件说明驱动/硬件没问题,别再往下查,往上查 framework/App。
- i2cdetect 扫不到先看上电:别急着怀疑 IC 坏,先确认 regulator 给 IC 上电了(L18)。
9. 常见问题分析与定位(触控失灵各场景)
9.1 常见问题清单(都表现为"失灵",根因不同)
- 整屏完全无反应
- 局部区域失灵
- 触摸偶尔失灵/跳点
- 息屏唤醒后失灵
- 某 App 内失灵(别的 App 正常)
9.2 分析与定位
① 整屏无反应
- getevent 没事件 → 往下:probe 失败(dmesg)?中断没来(/proc/interrupts)?I2C 不通(i2cdetect)?最后示波器。
- getevent 有事件 → 往上:焦点窗口/Dispatcher ANR(全局性问题,如某个全屏窗口拦了)。
② 局部失灵
- getevent 在失灵区域有没有事件 → 没有多是 IC 该区域坏点/校准(硬件);有事件但 App 不响应 → App 那块区域的 onTouch 逻辑。
③ 偶尔失灵/跳点
- 多为 I2C 偶发错误(L16)、中断丢失(L17)、干扰、IC 固件。dmesg 看有没有偶发 i2c error;示波器看波形质量。
④ 息屏唤醒后失灵
- 电源管理问题(L18):resume 没重新初始化 IC。dmesg 看 resume 时触控驱动有没有重新 init;读 IC 寄存器是不是默认值。
⑤ 某 App 内失灵
- 别的 App 正常 = 驱动/framework 没问题,是这个 App 的问题(onTouch 拦截、窗口 flag、WebView 等)。getevent 肯定有事件。
9.3 定位决策树
触控失灵
├─ 第一刀:getevent -lt /dev/input/eventX 触摸看有无事件
│ ├─ 有事件 → 驱动/硬件正常 → 往上查 framework/App
│ │ ├─ 所有 App 都失灵 → 焦点窗口/Dispatcher ANR(L4)
│ │ └─ 某 App 失灵 → 这个 App 的 onTouch/窗口 flag
│ └─ 没事件 → 往下查驱动/硬件
│ ├─ input device 在吗(/proc/bus/input/devices)→ 不在=驱动没注册(L14/L17)
│ ├─ probe 成功吗(dmesg)→ 没有=compatible/modules.load(L15/L13)
│ ├─ 中断涨吗(/proc/interrupts)→ 不涨=中断极性/GPIO(L17)
│ ├─ I2C 通吗(i2cdetect)→ 扫不到=硬件
│ └─ 硬件:示波器 SCL/SDA → 没波形=没上电(L18);NAK=地址/排线/IC
├─ 局部失灵 → 失灵区 getevent 有无 → 无=IC 坏点/校准;有=App
├─ 偶尔失灵/跳点 → dmesg i2c error + 示波器波形质量(L16/L17)
└─ 息屏后失灵 → resume 重新 init?读 IC 寄存器默认值?(L18)
10. 小结
- 触控链路极长(App→IMS→input→驱动→I2C→IC),任一环断都"失灵",必须系统追查。
- getevent 是第一刀:有事件→往上查 framework/App;没事件→往下查驱动/硬件。一刀砍掉一半。
- 逐层排除:每层一个判定命令,第一个不正常的层就是断点。
- 不同"失灵"根因不同:整屏/局部/偶发/息屏后/单 App,对应不同层。
- 排查口诀:先 getevent 分上下;往下 dmesg→interrupts→i2cdetect→示波器;往上 dumpsys input/window。
下节预告
L24:实战——异常重启看 ramdump —— 设备无征兆重启,怎么从 kernel panic 现场找根因。