29、Input系统与SurfaceFlinger的交互:事件注入与屏幕坐标映射,DisplayInfo的作用 事件注入InputDispatcher的最后一公里事件从驱动层上来经过InputReader、InputDispatcher最终要送到哪里去答案是目标窗口的InputChannel。但这里有个关键问题——InputDispatcher怎么知道事件该发给哪个窗口嗯它需要知道当前屏幕上每个窗口的位置、大小、层级。这些信息从哪里来从SurfaceFlinger来。我给大家画个简化的流程InputReader → InputDispatcher → WindowManagerService ↓ 获取窗口信息位置、大小、Z-order ↓ 计算目标窗口 ↓ 通过InputChannel发送事件你看InputDispatcher本身不直接跟SurfaceFlinger通信。它通过WMSWindowManagerService来获取窗口布局信息。而WMS在管理窗口时会跟SurfaceFlinger协商每个窗口的Surface状态。说白了WMS是Input系统和SurfaceFlinger之间的桥梁。屏幕坐标映射从物理坐标到逻辑坐标事件注入时还有一个很头疼的问题——坐标映射。触摸屏上报的是物理坐标比如(1000, 500)。但应用层期望的是逻辑坐标比如(100, 50)。这中间差了一个缩放因子。为什么会这样因为屏幕有物理分辨率但应用可能运行在虚拟分辨率下。比如手机是1080p的屏幕但某个应用为了省电只渲染了720p的画面。这时候触摸坐标就得做映射。我在项目中遇到过一个问题某款平板在横竖屏切换时触摸点总是偏移。查了半天发现是坐标映射没考虑旋转角度。SurfaceFlinger那边已经旋转了显示缓冲区但Input系统还在用旧的坐标映射表。这个坑我踩过。所以大家记住坐标映射必须与SurfaceFlinger的显示状态同步。包括分辨率、旋转角度、缩放比例。DisplayInfo坐标映射的基石那么坐标映射的依据是什么答案是DisplayInfo。DisplayInfo是SurfaceFlinger暴露给上层的一个数据结构。它包含了显示设备的核心参数字段含义影响displayWidth显示宽度像素水平坐标缩放displayHeight显示高度像素垂直坐标缩放rotation旋转角度0/90/180/270坐标轴交换layerStack图层栈标识多屏显示时的映射density屏幕密度dpi物理尺寸与像素的换算我个人习惯在调试触摸问题时先打印DisplayInfo。看看分辨率对不对旋转角度有没有更新。很多时候问题就出在这里。坐标映射的数学公式讲点干货。坐标映射其实就是一个矩阵变换。假设物理触摸点是(x_phys, y_phys)逻辑坐标是(x_log, y_log)。那么x_log (x_phys - displayOffsetX) * scaleX y_log (y_phys - displayOffsetY) * scaleY其中scaleX和scaleY由DisplayInfo中的分辨率决定scaleX displayWidth_logical / displayWidth_physical scaleY displayHeight_logical / displayHeight_physical如果屏幕有旋转还要加上旋转矩阵// 旋转90度的情况 x_log displayHeight_logical - y_phys * scaleY y_log x_phys * scaleX嗯这里要注意。旋转后的坐标映射逻辑宽高会互换。我见过不少新手在这里写反了。事件注入的完整链路最后我们串一下整个流程。从触摸屏按下到应用收到事件中间经历了什么驱动层触摸屏上报原始坐标物理坐标InputReader读取原始事件进行初步校准InputDispatcher查询WMS获取窗口信息确定目标窗口坐标映射根据DisplayInfo将物理坐标转为逻辑坐标事件注入通过InputChannel将事件发送到目标窗口应用层ViewRootImpl接收事件分发给对应的View你看第3步和第4步都离不开SurfaceFlinger提供的信息。没有DisplayInfo坐标映射就是无源之水。避坑指南我曾经在调试一个双屏设备时发现副屏的触摸点总是偏移。后来发现是DisplayInfo中的layerStack没配对。主屏和副屏的layerStack不同但Input系统默认用了主屏的DisplayInfo来映射副屏的坐标。这个问题折腾了我两天。总结Input系统和SurfaceFlinger的交互说白了就是「事件坐标」与「显示坐标」的对齐。DisplayInfo是它们之间的翻译官。我个人建议大家在分析触摸问题时第一步先看DisplayInfo。分辨率对不对旋转角度有没有更新多屏场景下layerStack是否匹配这些基础信息对了后面的事情就顺了。