基于 AOSP 15 (Android 15) 源码分析
源码根目录:
~/aosp15/
目录
- 1. 整体架构概览
- 2. 完整调用链路
- 3. 客户端侧详细流程
- 4. Binder IPC 跨进程调用
- 5. 服务端侧详细流程
- 6. 关键辅助类分析
- 7. WindowToken 创建与管理
- 8. 窗口类型定义
- 9. 错误码与返回值
- 10. 关键数据结构
- 11. 时序图
1. 整体架构概览
┌─────────────────────────────────────────────────────────────────────┐
│ 应用进程 (App Process) │
│ │
│ ┌───────────────────┐ ┌────────────────────┐ ┌───────────────┐ │
│ │ WindowManagerImpl │─▶│ WindowManagerGlobal│─▶│ ViewRootImpl │ │
│ └───────────────────┘ └────────────────────┘ └───────┬───────┘ │
│ │ │
│ IWindowSession (AIDL/Binder)│ │
└─────────────────────────────────────────────────────────┼───────────┘
│ IPC
┌─────────────────────────────────────────────────────────┼───────────┐
│ system_server 进程 │ │
│ │ │
│ ┌───────────┐ ┌───────────────────────┐ ┌────────────▼─────────┐ │
│ │ Session │─▶│ WindowManagerService │─▶│ WindowState │ │
│ └───────────┘ └──────────┬────────────┘ └──────────────────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ WindowToken │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
核心类职责
| 类名 | 职责 |
|---|---|
WindowManagerImpl | 应用层WindowManager实现,对外暴露addView/removeView接口 |
WindowManagerGlobal | 进程级单例,管理所有View/ViewRootImpl/LayoutParams |
ViewRootImpl | View树根节点,负责布局、绘制、与WMS通信 |
IWindowSession | AIDL接口,客户端与服务端的会话通道 |
Session | IWindowSession的服务端实现,每个进程一个 |
WindowManagerService | 系统窗口管理服务,窗口的最终管理者和仲裁者 |
WindowState | 窗口在WMS中的状态表示 |
WindowToken | 窗口令牌,一组相关窗口的容器 |
DisplayContent | 显示屏内容,管理一个Display上的所有窗口 |
DisplayPolicy | 显示策略,负责窗口权限验证和参数调整 |
2. 完整调用链路
[应用进程]
WindowManagerImpl.addView(view, params)
│
▼
WindowManagerGlobal.addView(view, params, display, parentWindow, userId)
│ ├── 参数校验
│ ├── 创建 ViewRootImpl
│ └── 添加到全局列表 mViews/mRoots/mParams
▼
ViewRootImpl.setView(view, attrs, panelParentView, userId)
│ ├── 设置 mView、复制 attrs
│ ├── requestLayout() 请求首次布局
│ ├── 创建 InputChannel
│ └── mWindowSession.addToDisplayAsUser() ── IPC ──┐
│ │
[system_server] │ Binder
│ │
▼ │
Session.addToDisplayAsUser(...) ◀─────────────────────┘
│
▼
WindowManagerService.addWindow(session, client, attrs, ...)
│
├── Step 1: 权限检查 checkAddPermission()
├── Step 2: 子窗口处理 & DisplayContent校验
├── Step 3: 基础校验 (重复添加 / 用户验证 / Presentation)
├── Step 4: WindowToken 查找/创建 ← 核心逻辑
├── Step 5: 创建 WindowState & DisplayPolicy校验
├── Step 6: InputChannel & 注册到WindowMap
├── Step 7: 添加到Token & 特殊窗口处理
├── Step 8: 焦点 / IME / 层级 / 输入更新
└── Step 9: 输出参数填充 & 返回
3. 客户端侧详细流程
3.1 WindowManagerImpl.addView()
文件: frameworks/base/core/java/android/view/WindowManagerImpl.java:156
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyTokens(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
说明: 应用层入口方法,实现了 ViewManager 接口。
applyTokens(params)— 处理Window token相关的参数,将Activity/Window的token设置到LayoutParams中mParentWindow— 如果是子WindowManager(如window.getWindowManager()),此处为父窗口mContext.getUserId()— 传入当前用户ID,支持多用户
3.2 WindowManagerGlobal.addView()
文件: frameworks/base/core/java/android/view/WindowManagerGlobal.java:350
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
步骤1: 参数校验
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
步骤2: 参数处理
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
// 子窗口:由父窗口调整布局参数(设置token等)
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// 非子窗口:检查是否启用硬件加速
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
步骤3: 查找父View和创建ViewRootImpl
// 如果是子窗口,查找关联的父View
if (wparams.type >= FIRST_SUB_WINDOW && wparams.type <= LAST_SUB_WINDOW) {
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
// 检查是否为无窗口Session (SurfaceControlViewHost)
IWindowSession windowlessSession = null;
if (wparams.token != null && panelParentView == null) {
for (int i = 0; i < mWindowlessRoots.size(); i++) {
ViewRootImpl maybeParent = mWindowlessRoots.get(i);
if (maybeParent.getWindowToken() == wparams.token) {
windowlessSession = maybeParent.getWindowSession();
break;
}
}
}
// 创建 ViewRootImpl
if (windowlessSession == null) {
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
windowlessSession, new WindowlessWindowLayout());
}
步骤4: 添加到全局列表并调用setView
view.setLayoutParams(wparams);
mViews.add(view); // 保存View引用
mRoots.add(root); // 保存ViewRootImpl引用
mParams.add(wparams); // 保存LayoutParams
try {
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException / InvalidDisplayException → 清理
final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
if (viewIndex >= 0) {
removeViewLocked(viewIndex, true);
}
throw e;
}
三个全局列表:
mViews: ArrayList<View>— 所有已添加的ViewmRoots: ArrayList<ViewRootImpl>— 所有ViewRootImplmParams: ArrayList<WindowManager.LayoutParams>— 所有布局参数
3.3 ViewRootImpl.setView()
文件: frameworks/base/core/java/android/view/ViewRootImpl.java:1496
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
步骤1: 设置mView和属性
synchronized (this) {
if (mView == null) {
mView = view; // 保存View引用
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs); // 复制布局参数
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
attrs = mWindowAttributes;
// ... 硬件加速、表面Insets、兼容性信息处理 ...
mAdded = true;
步骤2: 请求首次布局
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
重要: 在添加Window之前先请求布局,确保第一次relayout时View树已准备好。
requestLayout() 实现:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread(); // 检查是否在UI线程
mLayoutRequested = true;
scheduleTraversals(); // 调度performTraversals()
}
}
步骤3: 创建InputChannel
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();
}
步骤4: IPC调用添加Window
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
adjustLayoutParamsForCompatibility(mWindowAttributes, ...);
controlInsetsForCompatibility(mWindowAttributes);
Rect attachedFrame = new Rect();
final float[] compatScale = { 1f };
res = mWindowSession.addToDisplayAsUser(
mWindow, // IWindow 客户端Binder对象
mWindowAttributes, // 窗口布局参数
getHostVisibility(), // 宿主可见性
mDisplay.getDisplayId(), // 目标Display ID
userId, // 用户ID
mInsetsController.getRequestedVisibleTypes(), // 请求可见的Insets类型
inputChannel, // 输入通道 (out)
mTempInsets, // Insets状态 (out)
mTempControls, // Insets控制 (out)
attachedFrame, // 附加帧 (out)
compatScale // 兼容性缩放 (out)
);
// ... 处理返回的attachedFrame和compatScale ...
}
关键参数说明:
| 参数 | 方向 | 说明 |
|---|---|---|
mWindow | in | IWindow Binder对象,WMS通过它回调客户端 |
mWindowAttributes | in | 窗口类型、flags、大小等布局参数 |
getHostVisibility() | in | View的当前可见性 |
mDisplay.getDisplayId() | in | 目标显示屏ID |
userId | in | 用户ID(多用户支持) |
inputChannel | out | 输入事件通道 |
mTempInsets | out | 当前Insets状态 |
mTempControls | out | Insets源控制 |
attachedFrame | out | 父窗口的附加帧 |
compatScale | out | 兼容性缩放因子 |
步骤5: 错误码处理
if (res < WindowManagerGlobal.ADD_OKAY) {
// ... 清理 ...
switch (res) {
case ADD_BAD_APP_TOKEN: // -1: Token无效
case ADD_BAD_SUBWINDOW_TOKEN: // -2: 子窗口Token无效
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case ADD_NOT_APP_TOKEN: // -3: 非应用Token
throw new WindowManager.BadTokenException(...);
case ADD_APP_EXITING: // -4: 应用正在退出
throw new WindowManager.BadTokenException(...);
case ADD_DUPLICATE_ADD: // -5: 重复添加
throw new WindowManager.BadTokenException(...);
case ADD_STARTING_NOT_NEEDED: // -6: 不需要启动窗口
return; // 静默忽略
case ADD_MULTIPLE_SINGLETON: // -7: 单例窗口重复
throw new WindowManager.BadTokenException(...);
case ADD_PERMISSION_DENIED: // -8: 权限拒绝
throw new WindowManager.BadTokenException(...);
case ADD_INVALID_DISPLAY: // -9: 无效Display
throw new WindowManager.InvalidDisplayException(...);
case ADD_INVALID_TYPE: // -10: 无效窗口类型
throw new WindowManager.InvalidDisplayException(...);
case ADD_INVALID_USER: // -11: 无效用户
throw new WindowManager.BadTokenException(...);
}
}
3.4 IWindowSession.addToDisplayAsUser()
文件: frameworks/base/core/java/android/view/IWindowSession.aidl:58
int addToDisplayAsUser(
IWindow window,
in WindowManager.LayoutParams attrs,
in int viewVisibility,
in int layerStackId, // displayId
in int userId,
int requestedVisibleTypes,
out InputChannel outInputChannel,
out InsetsState insetsState,
out InsetsSourceControl.Array activeControls,
out Rect attachedFrame,
out float[] sizeCompatScale
);
IWindowSession 还提供了另外两个添加方法:
| 方法 | 说明 |
|---|---|
addToDisplay() | 不指定userId,使用调用者的userId |
addToDisplayAsUser() | 指定userId,支持多用户 |
addToDisplayWithoutInputChannel() | 不创建输入通道,用于不需要输入的窗口 |
4. Binder IPC 跨进程调用
┌───────────────────┐ ┌───────────────────┐
│ 应用进程 │ │ system_server │
│ │ │ │
│ mWindowSession ──┼─── Binder Proxy ────▶ │ Session (Stub) │
│ (IWindowSession) │ │ │
│ │ │ mWindowMap ──────│──▶ WindowState
│ mWindow ─────────┼─── Binder Proxy ────▶ │ (IWindow Binder) │
│ (IWindow.Stub) │ │ │
│ │◀─── Binder Proxy ─────│ 回调IWindow方法 │
│ │ (resized等) │ (resized, │
│ │ │ insetsChanged等)│
└───────────────────┘ └───────────────────┘
双向通信:
- 客户端 → 服务端: 通过
IWindowSession接口(add、relayout、remove等操作) - 服务端 → 客户端: 通过
IWindow接口(resized、insetsControlChanged、dispatchAppVisibility等回调)
5. 服务端侧详细流程
5.1 Session.addToDisplayAsUser()
文件: frameworks/base/services/core/java/com/android/server/wm/Session.java:247
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, @InsetsType int requestedVisibleTypes,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
float[] outSizeCompatScale) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
requestedVisibleTypes, outInputChannel, outInsetsState, outActiveControls,
outAttachedFrame, outSizeCompatScale);
}
说明: Session是IWindowSession的服务端实现,每个客户端进程对应一个Session实例。它直接委托给 WindowManagerService.addWindow()。
5.2 WindowManagerService.addWindow()
文件: frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java:1527
这是整个添加Window流程的核心方法,包含约430行代码。
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, @InsetsType int requestedVisibleTypes,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
float[] outSizeCompatScale) {
Step 1: 前置准备 & 权限检查
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity(); // 切换为系统身份
final int type = attrs.type;
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp, displayId);
if (res != ADD_OKAY) {
return res;
}
委托给 PhoneWindowManager.checkAddPermission(),按窗口类型检查不同权限(系统窗口需 INTERNAL_SYSTEM_WINDOW,应用覆盖层需 SYSTEM_ALERT_WINDOW AppOps等)。详见 6.1节。
Step 2: synchronized块 — 前置校验
进入全局锁后依次进行:Display就绪检查、Session存活检查、子窗口处理、DisplayContent获取、重复添加检查。
synchronized (mGlobalLock) {
if (!mDisplayReady) throw new IllegalStateException("Display has not been initialialized");
if (session.isClientDead()) return ADD_APP_EXITING;
// 子窗口: 查找父窗口,验证不能嵌套
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null) return ADD_BAD_SUBWINDOW_TOKEN;
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW) return ADD_BAD_SUBWINDOW_TOKEN;
}
// DisplayContent获取 & 访问验证
final DisplayContent displayContent = parentWindow != null
? parentWindow.mDisplayContent
: getDisplayContentOrCreate(displayId, attrs.token);
if (displayContent == null || !displayContent.hasAccess(session.mUid)) {
return ADD_INVALID_DISPLAY;
}
// 重复添加检查
if (mWindowMap.containsKey(client.asBinder())) return ADD_DUPLICATE_ADD;
Step 3: WindowToken查找/创建 — 核心逻辑
这是addWindow中最关键的步骤,决定窗口归属哪个Token。
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
分支逻辑:
Token是否已存在?
├── Token == null(不存在)
│ ├── 检查权限: unprivilegedAppCanCreateTokenWith()
│ ├── 有父窗口 → 共享父窗口Token: parentWindow.mToken
│ ├── 有WindowContext监听器 → new WindowToken.Builder(...).setFromClientToken(true).build()
│ └── 其他 → new WindowToken.Builder(...).build()
│
└── Token != null(已存在)→ 按窗口类型验证
├── TYPE_APPLICATION (1~99)
│ ├── token.asActivityRecord() == null → ADD_NOT_APP_TOKEN
│ ├── activity.getParent() == null → ADD_APP_EXITING
│ └── TYPE_APPLICATION_STARTING → 检查是否已有StartingWindow
├── TYPE_INPUT_METHOD / TYPE_WALLPAPER / TYPE_VOICE_INTERACTION → 验证token.windowType
└── 其他系统窗口 + token是ActivityRecord → 创建新Token
Token为null时核心代码:
if (token == null) {
if (!unprivilegedAppCanCreateTokenWith(...)) return ADD_BAD_APP_TOKEN;
if (hasParent) {
token = parentWindow.mToken; // 子窗口共享父窗口Token
} else if (mWindowContextListenerController.hasListener(windowContextToken)) {
token = new WindowToken.Builder(this, binder, type)
.setDisplayContent(displayContent)
.setFromClientToken(true).setOptions(options).build();
} else {
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken.Builder(this, binder, type)
.setDisplayContent(displayContent).build();
}
}
Token已存在 — 应用窗口验证:
else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
activity = token.asActivityRecord();
if (activity == null) return ADD_NOT_APP_TOKEN;
if (activity.getParent() == null) return ADD_APP_EXITING;
}
Step 4: 创建WindowState & DisplayPolicy校验
// 创建WindowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
// DisplayPolicy参数调整 (强制系统覆盖层不可聚焦/不可触摸等)
displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
attrs.flags = sanitizeFlagSlippery(attrs.flags, ...);
attrs.inputFeatures = sanitizeInputFeatures(attrs.inputFeatures, ...);
win.setRequestedVisibleTypes(requestedVisibleTypes);
// DisplayPolicy最终验证 (TRUSTED_OVERLAY权限、系统UI权限等)
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
if (res != ADD_OKAY) return res;
详见 5.3节 WindowState构造函数 和 6.2/6.3节。
Step 5: InputChannel & 注册到WindowMap
// 打开InputChannel (创建服务端/客户端输入通道对)
if (outInputChannel != null && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
win.openInputChannel(outInputChannel);
}
// --- 从此不允许失败 ---
// Toast窗口: 同UID限制 + 超时隐藏
if (type == TYPE_TOAST) {
if (!displayContent.canAddToastWindowForUid(callingUid)) return ADD_DUPLICATE_ADD;
mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
win.mAttrs.hideTimeoutMilliseconds);
}
// 注册到全局映射
win.mSession.onWindowAdded(win);
mWindowMap.put(client.asBinder(), win); // IBinder → WindowState
win.initAppOpsState();
win.setHiddenWhileSuspended(mPmInternal.isPackageSuspended(...));
openInputChannel 内部逻辑:InputChannel.openInputChannelPair() 创建配对通道 → 服务端注册到 InputManager → 客户端通道通过out参数传回。
Step 6: 添加到Token & 焦点/IME/层级更新
win.mToken.addWindow(win); // 添加到WindowToken的子节点
displayPolicy.addWindowLw(win, attrs); // 通知DisplayPolicy
// 特殊窗口类型处理
if (type == TYPE_APPLICATION_STARTING && activity != null) {
activity.attachStartingWindow(win);
} else if (type == TYPE_INPUT_METHOD) {
displayContent.setInputMethodWindowLocked(win);
} else if (type == TYPE_WALLPAPER) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
// 焦点更新
boolean focusChanged = false;
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, false);
}
// IME目标计算 & 层级分配 & 输入窗口更新
if (imMayMove) displayContent.computeImeTarget(true);
win.getParent().assignChildLayers();
if (focusChanged) displayContent.getInputMonitor().setInputFocusLw(...);
displayContent.getInputMonitor().updateInputWindowsLw(false);
Step 7: 输出参数填充 & 返回
// 方向/配置更新
if (win.isVisibleRequestedOrAdding() && displayContent.updateOrientation()) {
displayContent.sendNewConfiguration();
}
// 填充输出参数
win.fillInsetsState(outInsetsState, true);
getInsetsSourceControls(win, outActiveControls);
if (win.mLayoutAttached) {
outAttachedFrame.set(win.getParentWindow().getFrame());
} else {
outAttachedFrame.set(0, 0, -1, -1);
}
outSizeCompatScale[0] = win.getCompatScaleForClient();
} // end synchronized
Binder.restoreCallingIdentity(origId);
return res;
5.3 WindowState 构造函数
文件: frameworks/base/services/core/java/com/android/server/wm/WindowState.java:1098
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) {
字段初始化
super(service);
mTmpTransaction = service.mTransactionFactory.get();
mSession = s; // 所属Session
mClient = c; // IWindow客户端Binder
mAppOp = appOp; // AppOps操作码
mToken = token; // 所属WindowToken
mDisplayContent = token.mDisplayContent; // 所属DisplayContent
mActivityRecord = mToken.asActivityRecord(); // 关联的Activity(可能为null)
mOwnerUid = ownerId;
mShowUserId = showUserId;
mWindowId = new WindowId(this);
mAttrs.copyFrom(a); // 复制布局参数
mViewVisibility = viewVisibility;
mPolicy = mWmService.mPolicy;
mContext = mWmService.mContext;
// 输入窗口句柄
mInputWindowHandle = new InputWindowHandleWrapper(new InputWindowHandle(
mActivityRecord != null
? mActivityRecord.getInputApplicationHandle(false) : null,
getDisplayId()));
mInputWindowHandle.setFocusable(false);
mInputWindowHandle.setOwnerPid(s.mPid);
mInputWindowHandle.setOwnerUid(s.mUid);
mInputWindowHandle.setName(getName());
mInputWindowHandle.setPackageName(mAttrs.packageName);
mInputWindowHandle.setLayoutParamsType(mAttrs.type);
窗口层级计算
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
// 子窗口: 基于父窗口的层级
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
mIsChildWindow = true;
mLayoutAttached = (mAttrs.type != TYPE_APPLICATION_ATTACHED_DIALOG);
mIsImWindow = (parentWindow.mAttrs.type == TYPE_INPUT_METHOD
|| parentWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG);
mIsWallpaper = (parentWindow.mAttrs.type == TYPE_WALLPAPER);
} else {
// 非子窗口: 基于自身类型
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = 0;
mIsChildWindow = false;
mLayoutAttached = false;
mIsImWindow = (mAttrs.type == TYPE_INPUT_METHOD
|| mAttrs.type == TYPE_INPUT_METHOD_DIALOG);
mIsWallpaper = (mAttrs.type == TYPE_WALLPAPER);
}
mIsFloatingLayer = mIsImWindow || mIsWallpaper;
层级计算规则:
mBaseLayer= 窗口类型对应的层级 × TYPE_LAYER_MULTIPLIER(10000) + TYPE_LAYER_OFFSET(1000)mSubLayer= 子窗口相对于父窗口的偏移层级mIsFloatingLayer= IME窗口或壁纸窗口(特殊浮动层)
动画器和子窗口添加
mWinAnimator = new WindowStateAnimator(this);
mWinAnimator.mAlpha = a.alpha;
mRequestedWidth = UNSPECIFIED_LENGTH;
mRequestedHeight = UNSPECIFIED_LENGTH;
mLayer = 0;
mOverrideScale = mWmService.mAtmService.mCompatModePackages.getCompatScale(
mAttrs.packageName, s.mUid);
updateGlobalScale();
// 子窗口直接添加到父窗口
if (mIsChildWindow) {
parentWindow.addChild(this, sWindowSubLayerComparator);
}
5.4 WindowToken.addWindow()
文件: frameworks/base/services/core/java/com/android/server/wm/WindowToken.java:330
void addWindow(final WindowState win) {
ProtoLog.d(WM_DEBUG_FOCUS,
"addWindow: win=%s Callers=%s", win, Debug.getCallers(5));
if (win.isChildWindow()) {
// 子窗口已经添加到父窗口了,不需要再添加到Token
return;
}
// 如果Token还没有SurfaceControl,创建一个
if (mSurfaceControl == null) {
createSurfaceControl(true /* force */);
reassignLayer(getSyncTransaction());
}
// 将WindowState添加为Token的子节点
if (!mChildren.contains(win)) {
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
addChild(win, mWindowComparator);
mWmService.mWindowsChanged = true;
}
}
说明: WindowToken继承自WindowContainer,使用树形结构管理其下的所有WindowState。每个WindowToken在SurfaceFlinger中对应一个Layer节点。
6. 关键辅助类分析
6.1 DisplayPolicy.checkAddPermission()
文件: frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java:3150
完整权限检查逻辑:
checkAddPermission(type, isRoundedCornerOverlay, packageName, appOp, displayId)
│
├── 圆角覆盖层 → 检查 INTERNAL_SYSTEM_WINDOW 权限
│
├── 类型有效性检查 → 不是有效的应用/子/系统窗口类型 → ADD_INVALID_TYPE
│
├── 非系统窗口 (type < 2000) → ADD_OKAY (由WMS进一步验证)
│
├── 非系统警报窗口类型
│ ├── TYPE_TOAST → appOp = OP_TOAST_WINDOW, ADD_OKAY
│ ├── TYPE_ACCESSIBILITY_OVERLAY → appOp = OP_CREATE_ACCESSIBILITY_OVERLAY
│ ├── TYPE_INPUT_METHOD/WALLPAPER/PRESENTATION/VOICE_INTERACTION等 → ADD_OKAY
│ └── 其他 → 检查 INTERNAL_SYSTEM_WINDOW 权限
│
└── 系统警报窗口类型 (isSystemAlertWindowType)
├── appOp = OP_SYSTEM_ALERT_WINDOW
├── 系统进程 (SYSTEM_UID) → ADD_OKAY
├── targetSdk >= O 且非 TYPE_APPLICATION_OVERLAY → 检查 INTERNAL_SYSTEM_WINDOW
├── 检查 SYSTEM_APPLICATION_OVERLAY 权限
├── 虚拟设备所有者 + CREATE_VIRTUAL_DEVICE → ADD_OKAY
└── AppOps 检查
├── MODE_ALLOWED → ADD_OKAY
├── MODE_IGNORED → ADD_OKAY (但窗口会被隐藏)
├── MODE_ERRORED → targetSdk < M ? ADD_OKAY : ADD_PERMISSION_DENIED
└── 默认 → 检查 SYSTEM_ALERT_WINDOW 权限
6.2 DisplayPolicy.validateAddingWindowLw()
文件: frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java:1057
验证窗口添加的最终策略:
int validateAddingWindowLw(WindowManager.LayoutParams attrs, int callingPid, int callingUid) {
// TRUSTED_OVERLAY 需要 INTERNAL_SYSTEM_WINDOW 权限
if ((attrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) {
mContext.enforcePermission(INTERNAL_SYSTEM_WINDOW, ...);
}
// INTERCEPT_GLOBAL_DRAG_AND_DROP 需要 Task权限
if ((attrs.privateFlags & PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP) != 0) {
ActivityTaskManagerService.enforceTaskPermission("DisplayPolicy");
}
// 系统UI权限 (虚拟设备所有者用 CREATE_VIRTUAL_DEVICE,否则用 STATUS_BAR_SERVICE)
final String systemUiPermission = ...;
switch (attrs.type) {
case TYPE_STATUS_BAR: // 需要系统UI权限,单例检查
case TYPE_NOTIFICATION_SHADE: // 需要系统UI权限,单例检查
case TYPE_NAVIGATION_BAR: // 需要系统UI权限,单例检查
case TYPE_NAVIGATION_BAR_PANEL:
case TYPE_STATUS_BAR_ADDITIONAL:
case TYPE_STATUS_BAR_SUB_PANEL:
case TYPE_VOICE_INTERACTION_STARTING:
mContext.enforcePermission(systemUiPermission, ...);
break;
case TYPE_STATUS_BAR_PANEL:
return ADD_INVALID_TYPE; // 此类型已废弃
}
// providedInsets 需要系统UI权限 (Recents组件除外)
if (attrs.providedInsets != null) {
if (!mService.mAtmService.isCallerRecents(callingUid)) {
mContext.enforcePermission(systemUiPermission, ...);
}
}
return ADD_OKAY;
}
6.3 DisplayPolicy.adjustWindowParamsLw()
文件: frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java:950
在WindowState创建后,调整窗口参数:
| 窗口类型 | 调整内容 |
|---|---|
TYPE_SYSTEM_OVERLAY / TYPE_SECURE_SYSTEM_OVERLAY | 强制设置 FLAG_NOT_FOCUSABLE + FLAG_NOT_TOUCHABLE |
TYPE_WALLPAPER | 设置 layoutInDisplayCutoutMode = ALWAYS |
TYPE_TOAST | 限制超时时间,设置 FLAG_NOT_TOUCHABLE |
TYPE_BASE_APPLICATION | 全屏非透明窗口不允许 fitInsets |
| 系统警报窗口 (SAW) | alpha > 最大遮挡透明度时降级 |
| 所有窗口 | 清理不受限手势排除标志 |
7. WindowToken 创建与管理
7.1 WindowToken.Builder
文件: frameworks/base/services/core/java/com/android/server/wm/WindowToken.java:828
static class Builder {
private final WindowManagerService mService;
private final IBinder mToken; // Token的Binder标识
@WindowType private final int mType; // 窗口类型
private boolean mPersistOnEmpty; // 空Token是否保留
private DisplayContent mDisplayContent; // 关联的DisplayContent
private boolean mOwnerCanManageAppTokens; // 是否可管理App Token
private boolean mRoundedCornerOverlay; // 是否圆角覆盖层
private boolean mFromClientToken; // 是否来自客户端Token
@Nullable private Bundle mOptions; // 选项
// setter方法 (返回this以支持链式调用)
Builder setPersistOnEmpty(boolean);
Builder setDisplayContent(DisplayContent);
Builder setOwnerCanManageAppTokens(boolean);
Builder setRoundedCornerOverlay(boolean);
Builder setFromClientToken(boolean);
Builder setOptions(Bundle);
WindowToken build() {
return new WindowToken(mService, mToken, mType, mPersistOnEmpty,
mDisplayContent, mOwnerCanManageAppTokens, mRoundedCornerOverlay,
mFromClientToken, mOptions);
}
}
7.2 Token创建时机
| 场景 | Token类型 | 创建者 |
|---|---|---|
| Activity窗口 | ActivityRecord (继承WindowToken) | AMS创建Task/Activity时 |
| 系统窗口 (有预注册Token) | WindowToken | WMS.addWindowToken() 调用 |
| 系统窗口 (无预注册Token) | WindowToken | WMS.addWindow() 中创建 |
| 壁纸窗口 | WallpaperWindowToken | WMS.addWindowToken() |
| WindowContext窗口 | WindowToken (fromClientToken=true) | WMS.addWindow() 中创建 |
7.3 WMS.addWindowToken()
文件: frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java:2850
public void addWindowToken(@NonNull IBinder binder, int type, int displayId,
@Nullable Bundle options) {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
synchronized (mGlobalLock) {
final DisplayContent dc = getDisplayContentOrCreate(displayId, null);
if (dc == null) { return; }
if (dc.getWindowToken(binder) != null) { return; } // 已存在
if (type == TYPE_WALLPAPER) {
new WallpaperWindowToken(this, binder, true, dc, true, options);
} else {
new WindowToken.Builder(this, binder, type)
.setDisplayContent(dc)
.setPersistOnEmpty(true) // 空Token也保留
.setOwnerCanManageAppTokens(true)
.setOptions(options)
.build();
}
}
}
8. 窗口类型定义
文件: frameworks/base/core/java/android/view/WindowManager.java
8.1 应用窗口 (1-99)
| 常量 | 值 | 说明 |
|---|---|---|
FIRST_APPLICATION_WINDOW | 1 | 应用窗口起始值 |
TYPE_BASE_APPLICATION | 1 | 基础应用窗口(底层) |
TYPE_APPLICATION | 2 | 标准应用窗口 |
TYPE_APPLICATION_STARTING | 3 | 应用启动窗口(Splash Screen) |
TYPE_DRAWN_APPLICATION | 4 | 已绘制的应用窗口 |
LAST_APPLICATION_WINDOW | 99 | 应用窗口结束值 |
8.2 子窗口 (1000-1999)
| 常量 | 值 | 说明 |
|---|---|---|
FIRST_SUB_WINDOW | 1000 | 子窗口起始值 |
TYPE_APPLICATION_PANEL | 1000 | 应用面板 |
TYPE_APPLICATION_MEDIA | 1001 | 应用媒体 |
TYPE_APPLICATION_SUB_PANEL | 1002 | 应用子面板 |
TYPE_APPLICATION_ATTACHED_DIALOG | 1003 | 附属对话框 |
TYPE_APPLICATION_MEDIA_OVERLAY | 1004 | 媒体覆盖层 |
TYPE_APPLICATION_ABOVE_SUB_PANEL | 1005 | 子面板上方的面板 |
LAST_SUB_WINDOW | 1999 | 子窗口结束值 |
8.3 系统窗口 (2000-2999)
| 常量 | 值 | 说明 |
|---|---|---|
FIRST_SYSTEM_WINDOW | 2000 | 系统窗口起始值 |
TYPE_STATUS_BAR | 2000 | 状态栏 |
TYPE_SEARCH_BAR | 2001 | 搜索栏 |
TYPE_PHONE | 2002 | 电话窗口 |
TYPE_SYSTEM_ALERT | 2003 | 系统警报 |
TYPE_KEYGUARD | 2004 | 锁屏 |
TYPE_TOAST | 2005 | Toast |
TYPE_SYSTEM_OVERLAY | 2006 | 系统覆盖层 |
TYPE_PRIORITY_PHONE | 2007 | 优先电话 |
TYPE_SYSTEM_DIALOG | 2008 | 系统对话框 |
TYPE_KEYGUARD_DIALOG | 2009 | 锁屏对话框 |
TYPE_SYSTEM_ERROR | 2010 | 系统错误 |
TYPE_INPUT_METHOD | 2011 | 输入法 |
TYPE_INPUT_METHOD_DIALOG | 2012 | 输入法对话框 |
TYPE_WALLPAPER | 2013 | 壁纸 |
TYPE_STATUS_BAR_PANEL | 2014 | 状态栏面板 |
TYPE_SECURE_SYSTEM_OVERLAY | 2015 | 安全系统覆盖层 |
TYPE_DRAG | 2016 | 拖拽覆盖层 |
TYPE_STATUS_BAR_SUB_PANEL | 2017 | 状态栏子面板 |
TYPE_POINTER | 2018 | 指针 |
TYPE_NAVIGATION_BAR | 2019 | 导航栏 |
TYPE_VOLUME_OVERLAY | 2020 | 音量覆盖层 |
TYPE_BOOT_PROGRESS | 2021 | 启动进度 |
TYPE_INPUT_CONSUMER | 2022 | 输入消费者 |
TYPE_NAVIGATION_BAR_PANEL | 2024 | 导航栏面板 |
TYPE_DISPLAY_OVERLAY | 2026 | 显示覆盖层 |
TYPE_MAGNIFICATION_OVERLAY | 2027 | 放大覆盖层 |
TYPE_PRIVATE_PRESENTATION | 2030 | 私有Presentation |
TYPE_VOICE_INTERACTION | 2031 | 语音交互 |
TYPE_ACCESSIBILITY_OVERLAY | 2032 | 无障碍覆盖层 |
TYPE_VOICE_INTERACTION_STARTING | 2033 | 语音交互启动窗口 |
TYPE_DOCK_DIVIDER | 2034 | 分屏分割线 |
TYPE_QS_DIALOG | 2035 | 快速设置对话框 |
TYPE_SCREENSHOT | 2036 | 截图 |
TYPE_PRESENTATION | 2037 | Presentation |
TYPE_APPLICATION_OVERLAY | 2038 | 应用覆盖层 (SYSTEM_ALERT_WINDOW) |
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY | 2039 | 无障碍放大覆盖层 |
TYPE_NOTIFICATION_SHADE | 2040 | 通知栏 |
TYPE_STATUS_BAR_ADDITIONAL | 2041 | 附加状态栏 |
LAST_SYSTEM_WINDOW | 2999 | 系统窗口结束值 |
9. 错误码与返回值
9.1 错误码
文件: frameworks/base/core/java/android/view/WindowManagerGlobal.java:124
| 常量 | 值 | 含义 | 触发场景 |
|---|---|---|---|
ADD_OKAY | 0 | 添加成功 | — |
ADD_BAD_APP_TOKEN | -1 | 应用Token无效 | 非特权应用尝试创建应用窗口Token |
ADD_BAD_SUBWINDOW_TOKEN | -2 | 子窗口Token无效 | 子窗口的父窗口不存在或本身是子窗口 |
ADD_NOT_APP_TOKEN | -3 | 非应用Token | 应用窗口使用了非ActivityRecord的Token |
ADD_APP_EXITING | -4 | 应用正在退出 | Activity已退出或Session客户端已死亡 |
ADD_DUPLICATE_ADD | -5 | 重复添加 | 同一IWindow已添加 / 同一UID已有Toast |
ADD_STARTING_NOT_NEEDED | -6 | 不需要启动窗口 | StartingWindow不再需要 |
ADD_MULTIPLE_SINGLETON | -7 | 单例窗口重复 | StatusBar/NavigationBar已存在 |
ADD_PERMISSION_DENIED | -8 | 权限被拒绝 | 缺少必要的系统权限 |
ADD_INVALID_DISPLAY | -9 | 无效Display | Display不存在或无权访问 |
ADD_INVALID_TYPE | -10 | 无效窗口类型 | WindowContext类型不匹配 / TYPE_STATUS_BAR_PANEL |
ADD_INVALID_USER | -11 | 无效用户 | 跨用户添加时用户验证失败 |
9.2 返回标志位(与ADD_OKAY按位或)
| 常量 | 值 | 含义 |
|---|---|---|
ADD_FLAG_IN_TOUCH_MODE | 0x1 | 当前处于触摸模式 |
ADD_FLAG_APP_VISIBLE | 0x2 | 应用可见 |
ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS | 0x4 | 始终消费系统栏 |
9.3 客户端异常映射
// ViewRootImpl.setView() 中的错误码处理
switch (res) {
case ADD_BAD_APP_TOKEN: // → WindowManager.BadTokenException
case ADD_BAD_SUBWINDOW_TOKEN: // → WindowManager.BadTokenException
case ADD_NOT_APP_TOKEN: // → WindowManager.BadTokenException
case ADD_APP_EXITING: // → WindowManager.BadTokenException
case ADD_DUPLICATE_ADD: // → WindowManager.BadTokenException
case ADD_MULTIPLE_SINGLETON: // → WindowManager.BadTokenException
case ADD_PERMISSION_DENIED: // → WindowManager.BadTokenException
case ADD_INVALID_USER: // → WindowManager.BadTokenException
case ADD_STARTING_NOT_NEEDED: // → 静默返回 (不抛异常)
case ADD_INVALID_DISPLAY: // → WindowManager.InvalidDisplayException
case ADD_INVALID_TYPE: // → WindowManager.InvalidDisplayException
}
10. 关键数据结构
10.1 WindowState 关键成员
| 字段 | 类型 | 说明 |
|---|---|---|
mSession | Session | 所属的客户端Session |
mClient | IWindow | 客户端Binder代理 |
mToken | WindowToken | 所属的WindowToken |
mDisplayContent | DisplayContent | 所属的DisplayContent |
mActivityRecord | ActivityRecord | 关联的Activity(可能为null) |
mAttrs | WindowManager.LayoutParams | 窗口布局参数 |
mBaseLayer | int | 基础Z序层级 |
mSubLayer | int | 子窗口Z序偏移 |
mIsChildWindow | boolean | 是否为子窗口 |
mLayoutAttached | boolean | 是否布局附加到父窗口 |
mIsImWindow | boolean | 是否为IME窗口 |
mIsWallpaper | boolean | 是否为壁纸窗口 |
mIsFloatingLayer | boolean | 是否为浮动层 |
mWinAnimator | WindowStateAnimator | 窗口动画器 |
mInputChannel | InputChannel | 服务端输入通道 |
mViewVisibility | int | View可见性 |
10.2 WindowToken 关键成员
| 字段 | 类型 | 说明 |
|---|---|---|
windowType | int | 窗口类型 |
mDisplayContent | DisplayContent | 关联的DisplayContent |
mPersistOnEmpty | boolean | 无子窗口时是否保留 |
mOwnerCanManageAppTokens | boolean | 所有者是否可管理App Token |
mRoundedCornerOverlay | boolean | 是否圆角覆盖层 |
mFromClientToken | boolean | 是否来自客户端Token (WindowContext) |
mOptions | Bundle | 选项 |
mSurfaceControl | SurfaceControl | Surface控制 |
10.3 WMS 关键容器
| 字段 | 类型 | 说明 |
|---|---|---|
mWindowMap | HashMap<IBinder, WindowState> | IWindow Binder → WindowState 映射 |
mGlobalLock | WindowManagerGlobalLock | WMS全局锁 |
mDisplayReady | boolean | Display是否初始化完成 |
mPolicy | WindowManagerPolicy | 窗口策略 (PhoneWindowManager) |
mH | H | 主线程Handler |
11. 时序图
App Process system_server (WMS)
│ │
│ WindowManagerImpl.addView() │
│──────────────────────────────│
│ WindowManagerGlobal.addView()
│ ├── 参数校验
│ ├── new ViewRootImpl()
│ └── root.setView()
│ ├── mView = view
│ ├── requestLayout()
│ ├── new InputChannel()
│ │
│ │ mWindowSession.addToDisplayAsUser()
│──────────────────────────────▶
│ │ │ Session.addToDisplayAsUser()
│ │ │ │
│ │ │ WMS.addWindow()
│ │ │ ├── 1. mPolicy.checkAddPermission()
│ │ │ ├── 2. 子窗口处理
│ │ │ ├── 3. DisplayContent获取
│ │ │ ├── 4. 重复添加检查
│ │ │ ├── 5. Presentation处理
│ │ │ ├── 6. 用户ID验证
│ │ │ ├── 7. WindowToken查找/创建
│ │ │ ├── 8. 类型验证
│ │ │ ├── 9. new WindowState()
│ │ │ │ ├── 字段初始化
│ │ │ │ ├── 层级计算
│ │ │ │ └── 子窗口添加
│ │ │ ├── 10. adjustWindowParamsLw()
│ │ │ ├── 11. validateAddingWindowLw()
│ │ │ ├── 12. openInputChannel()
│ │ │ ├── 13. Toast超时处理
│ │ │ ├── 14. WindowContext处理
│ │ │ ├── 15. mWindowMap.put()
│ │ │ ├── 16. token.addWindow()
│ │ │ ├── 17. 特殊窗口处理
│ │ │ ├── 18. updateFocusedWindowLocked()
│ │ │ ├── 19. computeImeTarget()
│ │ │ ├── 20. assignChildLayers()
│ │ │ ├── 21. updateInputWindowsLw()
│ │ │ └── 22. 填充输出参数
│ │ │
│◀──────────────────────────────│
│ res (int) │
│ ├── ADD_OKAY = 0 │
│ ├── ADD_FLAG_* (按位或) │
│ └── 或负数错误码 │
│ │
│ 错误码处理 / 正常流程继续 │
│ handleInsetsControlChanged()
│ computeFrames()
│ setFrame()
│ │
附录: 源码文件索引
| 文件 | 路径 |
|---|---|
| WindowManagerImpl | frameworks/base/core/java/android/view/WindowManagerImpl.java |
| WindowManagerGlobal | frameworks/base/core/java/android/view/WindowManagerGlobal.java |
| ViewRootImpl | frameworks/base/core/java/android/view/ViewRootImpl.java |
| IWindowSession | frameworks/base/core/java/android/view/IWindowSession.aidl |
| IWindow | frameworks/base/core/java/android/view/IWindow.aidl |
| Session | frameworks/base/services/core/java/com/android/server/wm/Session.java |
| WindowManagerService | frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java |
| WindowState | frameworks/base/services/core/java/com/android/server/wm/WindowState.java |
| WindowToken | frameworks/base/services/core/java/com/android/server/wm/WindowToken.java |
| DisplayPolicy | frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java |
| DisplayContent | frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java |
| PhoneWindowManager | frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java |
| WindowStateAnimator | frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java |