WMS 如何验证并接纳窗口
2026/8/6大约 3 分钟WMS 窗口生命周期Android 14addWindowWindowTokenWindowState
WMS 如何验证并接纳窗口
加窗请求到达 Session 后,WMS 仍要校验调用身份、Display、token、窗口类型、权限和重复状态。 只有这些条件通过,系统才创建 WindowState 并挂入容器树;这一步成功不代表 Surface 已创建, 更不代表首帧已经出现在屏幕上。
addWindow() 成功只代表 WMS 接受记录
| 常见误解 | 正确理解 |
|---|---|
| 窗口加进来就一定能显示 | 加窗成功只代表 WMS 接受了记录,布局、Surface 和首帧都还没发生 |
addWindow() 失败一定是因为权限 | 失败原因包括 token 不匹配、Display 无效、重复添加等多种返回值 |
addWindow() 像酒店前台的入住校验:先查证件、再查预订记录、再查房态,全部通过才发房卡。房卡不代表房间已经布置好,只代表“系统承认这间房”。
服务端主流程
逐步解释:
- ① 调用身份:Binder 调用方 uid/pid 被 WMS 记录,后续权限和归属判断都基于它。
- ② 权限检查:
checkAddPermission()按窗口类型判断调用方是否有权添加。 - ③ Display 校验:目标 Display 必须存在且调用方有访问权。
- ④ 重复检查:同一个 IWindow 不能注册两次。
- ⑤ token 查找:
DisplayContent.getWindowToken()在显示器的 token 表里找归属凭证。 - ⑥ 类型匹配:窗口类型必须与 token 类型一致(应用窗口必须用应用 token)。
- ⑦ 创建记录:
new WindowState(...)是服务端记录;同时打开输入通道。 - ⑧ 入树:加入
mWindowMap和 WindowToken 的子列表。 - ⑨ 后续状态:标记需要更新输入窗口信息,必要时重新计算焦点。
常见失败返回值
| 返回值常量 | 值 | 含义 |
|---|---|---|
ADD_OKAY | 0 | 添加成功 |
ADD_BAD_APP_TOKEN | -1 | token 是系统 token,不能用于应用窗口 |
ADD_BAD_SUBWINDOW_TOKEN | -2 | 子窗口的父窗口 token 无效 |
ADD_NOT_APP_TOKEN | -3 | 应用窗口类型用了非应用 token |
ADD_APP_EXITING | -4 | 应用 token 已退出 |
ADD_DUPLICATE_ADD | -5 | 窗口重复添加 |
ADD_PERMISSION_DENIED | -8 | 缺少权限 |
ADD_INVALID_DISPLAY | -9 | Display 不存在或无访问权 |
ADD_INVALID_TYPE | -10 | 窗口类型与 WindowContext 不匹配 |
ADD_INVALID_USER | -11 | 用户不合法 |
这些常量定义在 WindowManagerGlobal.java:119-130,应用侧 ViewRootImpl 和开发者都能看到同一份数字。
源码证据
阅读目标:确认校验、WindowState 创建与入树是三个连续但独立的动作。
正文指针:WindowManagerService.java:1494 / 1551 / 1664 / 1751 / 1763 / 1809。
// WindowManagerService.java:1494
if (mWindowMap.containsKey(client.asBinder())) {
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}// WindowManagerService.java:1664
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);// WindowManagerService.java:1751, 1763
mWindowMap.put(client.asBinder(), win);
win.mToken.addWindow(win);这段代码证明: 校验失败直接返回负数常量;通过后创建 WindowState;mWindowMap.put 建立 client → WindowState 映射,win.mToken.addWindow(win) 把窗口挂到所属 token 的容器树。
输入通道
可接收输入的窗口在加窗时打开输入通道:win.openInputChannel(outInputChannel)(WindowManagerService.java:1682)。输入通道随 Binder 返回给应用,之后事件才能从系统送到应用。
源码入口
源码基准:android-14.0.0_r75。
以下文件默认位于 frameworks/base/,表中省略该前缀:
| 文件 | 关键位置 | 作用 |
|---|---|---|
services/core/java/com/android/server/wm/Session.java | 250-258 行 addToDisplayAsUser() | Binder 服务端入口 |
services/core/java/com/android/server/wm/WindowManagerService.java | 1448 行 addWindow() | 校验主方法 |
| 同文件 | 1455 行 mPolicy.checkAddPermission(...) | 类型权限检查 |
| 同文件 | 1494 行 mWindowMap.containsKey(...) | 重复添加检查 |
| 同文件 | 1551 行 displayContent.getWindowToken(...) | token 查找 |
| 同文件 | 1664 行 new WindowState(...) | 服务端窗口记录 |
| 同文件 | 1682 行 win.openInputChannel(...) | 输入通道创建 |
| 同文件 | 1751 / 1763 行 mWindowMap.put / win.mToken.addWindow(win) | 建立映射与入树 |
core/java/android/view/WindowManagerGlobal.java | 119-130 行 ADD_* 常量 | 返回值契约 |