窗口类型
窗口类型定义在 WindowManager.LayoutParams 中。可以分为三种:应用窗口、子窗口、系统窗口。取值范围为:
- 应用窗口: 1~999
- 子窗口: 1000~1999
- 系统窗口: 2000~2999
class LayoutParams {
// 应用窗口的取值范围在 1~999
// 一般的应用窗口
public static final int FIRST_APPLICATION_WINDOW = 1;
// 应用窗口起始值
public static final int TYPE_BASE_APPLICATION = 1;
// 应用程序窗口,token 必须是 ActivityRecord
public static final int TYPE_APPLICATION = 2;
// 应用程序启动窗口
public static final int TYPE_APPLICATION_STARTING = 3;
public static final int TYPE_DRAWN_APPLICATION = 4;
// 应用窗口结束值
public static final int LAST_APPLICATION_WINDOW = 99;
// 子窗口的取值范围在 1000~1999
// 应用子窗口起始值
public static final int FIRST_SUB_WINDOW = 1000;
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
// 子窗口结束值
public static final int LAST_SUB_WINDOW = 1999;
// 系统窗口的取值范围在 2000~2999
// 系统窗口起始值
public static final int FIRST_SYSTEM_WINDOW = 2000;
// 状态栏
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
// 搜索栏
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
// 拨号窗口
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
// 提醒窗口
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
// 锁屏窗口
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
// Toast窗口
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
// OVERLAY窗口,不能获取焦点
public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;
// 拨号窗口,可以在锁屏窗口之上,不能获取焦点
public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;
// 从状态栏划出的对话框
public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;
// 锁屏窗口
public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9;
// 系统错误提示窗口
public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10;
// 输入法窗口
public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
// 输入法对话框,在当前的输入法窗口之上
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
// 壁纸窗口
public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
// 从状态栏划出的面板窗口
public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;
// 系统安全相关的窗口
public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
// drag-and-drop 假窗口
public static final int TYPE_DRAG = FIRST_SYSTEM_WINDOW+16;
// 从状态栏划出的面板窗口
public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
// 显示鼠标的窗口
public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
// 导航栏
public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;
// 音量窗口
public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
// 启动进度窗口
public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
// systemUI 隐藏时消费输入事件
public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;
// 导航栏面板
public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
// 屏幕 overlay 窗口
public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;
// 无障碍服务时的放大显示的窗口
public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;
// 虚拟屏的演示窗口
public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
// 语音交换的窗口
public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
// 无障碍服务时的 overlay 窗口
public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
// 语音交换的启动窗口
public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;
// 快速设置磁铁的窗口
public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
// 截屏窗口
public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;
// 演示窗口
public static final int TYPE_PRESENTATION = FIRST_SYSTEM_WINDOW + 37;
// 应用 overlay 窗口,在应用窗口和系统重要窗口之间
public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
// 无障碍服务的 overlay
public static final int TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39;
// 锁屏通知
public static final int TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40;
// 状态栏附加层
public static final int TYPE_STATUS_BAR_ADDITIONAL = FIRST_SYSTEM_WINDOW + 41;
// 系统窗口结束值
public static final int LAST_SYSTEM_WINDOW = 2999;
}
窗口Z轴
窗口的Z轴,由 mBaseLayer 和 mSubLayer 描述,在 WindowState 初始化设置,并且不可改变。
WindowManagerPolicy 根据 windowType 进行转换后,加上乘数(10000)和偏移量(1000)。容器中可能有多个相同类型的窗口,这样可以保证有足够的空间。
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
PowerManagerWrapper powerManagerWrapper) {
...
// 子窗口类型
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
// 使用父窗口的 window type 进行转换
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
// 相对父窗口的偏移量
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
mIsChildWindow = true;
...
// 应用主窗口、系统窗口类型
} else {
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = 0;
...
}
...
}
int TYPE_LAYER_MULTIPLIER = 10000;
int TYPE_LAYER_OFFSET = 1000;
WindowManagerPolicy.java
getWindowLayerFromTypeLw 获取应用窗口和系统窗口的 layer,根据 window type 进行转换。
取值范围: 1~36
应用窗口是 2;壁纸是 1,要求在下层;其它在 3~36;
getSubWindowLayerFromTypeLw 获取子窗口相对于应用窗口的相对位置。
取值范围: -2~3.
负数表示在应用窗口的下方,正数表示在应用窗口的上方。
default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow,
boolean roundedCornerOverlay) {
if (roundedCornerOverlay && canAddInternalSystemWindow) {
return getMaxWindowLayer(); // 36
}
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return APPLICATION_LAYER; // 2
}
switch (type) {
case TYPE_WALLPAPER:
// 壁纸在最下面
return 1;
case TYPE_PRESENTATION:
case TYPE_PRIVATE_PRESENTATION:
case TYPE_DOCK_DIVIDER:
case TYPE_QS_DIALOG:
case TYPE_PHONE:
return 3;
case TYPE_SEARCH_BAR:
return 4;
case TYPE_INPUT_CONSUMER:
return 5;
case TYPE_SYSTEM_DIALOG:
return 6;
case TYPE_TOAST:
return 7;
case TYPE_PRIORITY_PHONE:
return 8;
case TYPE_SYSTEM_ALERT:
// ANR / app crashed dialogs
return canAddInternalSystemWindow ? 12 : 9; // 是否系统APP
case TYPE_APPLICATION_OVERLAY:
return 11;
case TYPE_INPUT_METHOD:
return 13;
case TYPE_INPUT_METHOD_DIALOG:
return 14;
case TYPE_STATUS_BAR:
return 15;
case TYPE_STATUS_BAR_ADDITIONAL:
return 16;
case TYPE_NOTIFICATION_SHADE:
return 17;
case TYPE_STATUS_BAR_SUB_PANEL:
return 18;
case TYPE_KEYGUARD_DIALOG:
return 19;
case TYPE_VOICE_INTERACTION_STARTING:
return 20;
case TYPE_VOICE_INTERACTION:
return 21;
case TYPE_VOLUME_OVERLAY:
return 22;
case TYPE_SYSTEM_OVERLAY:
return canAddInternalSystemWindow ? 23 : 10;
case TYPE_NAVIGATION_BAR:
return 24;
case TYPE_NAVIGATION_BAR_PANEL:
return 25;
case TYPE_SCREENSHOT:
return 26;
case TYPE_SYSTEM_ERROR:
return canAddInternalSystemWindow ? 27 : 9;
case TYPE_MAGNIFICATION_OVERLAY:
return 28;
case TYPE_DISPLAY_OVERLAY:
return 29;
case TYPE_DRAG:
return 30;
case TYPE_ACCESSIBILITY_OVERLAY:
return 31;
case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
return 32;
case TYPE_SECURE_SYSTEM_OVERLAY:
return 33;
case TYPE_BOOT_PROGRESS:
return 34;
case TYPE_POINTER:
return 35;
default:
return 3;
}
}
default int getSubWindowLayerFromTypeLw(int type) {
switch (type) {
case TYPE_APPLICATION_PANEL:
case TYPE_APPLICATION_ATTACHED_DIALOG:
return APPLICATION_PANEL_SUBLAYER; // -1
case TYPE_APPLICATION_MEDIA:
return APPLICATION_MEDIA_SUBLAYER; // -2
case TYPE_APPLICATION_MEDIA_OVERLAY:
return APPLICATION_MEDIA_OVERLAY_SUBLAYER; // -1
case TYPE_APPLICATION_SUB_PANEL:
return APPLICATION_SUB_PANEL_SUBLAYER; // 2
case TYPE_APPLICATION_ABOVE_SUB_PANEL:
return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER; // 3
}
Slog.e("WindowManager", "Unknown sub-window type: " + type);
return 0;
}
int APPLICATION_LAYER = 2;
int APPLICATION_MEDIA_SUBLAYER = -2;
int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
int APPLICATION_PANEL_SUBLAYER = 1;
int APPLICATION_SUB_PANEL_SUBLAYER = 2;
int APPLICATION_ABOVE_SUB_PANEL_SUBLAYER = 3;
addWindow()
- 权限检查、错误检查:屏幕不存在、重复添加Window、布局参数检查(主要基于 window tyep)、WindowToken 与 WindowState 的窗口类型一致(非子窗口)等;
- 根据传入的token(IBinder)查找或创建 WindowToken; 应用使用 ActivityRecord, 子窗口类型使用父窗口的 token;WindowToken 构造方法中加入层级结构和mTokenMap;
- 创建 WindowState, WIndowState 初始化的时候会确定Z轴相关的layer;
- 调用openInputChannel
- 如果是Toast类型,发送延时隐藏消息;
- WindowState 加入 mWindowMap;
- 把 WindowState 加入 WindowToken 的子容器(按照Z轴排序);非子窗口类型,子窗口是其父窗口的子容器。过程中,创建 WindowToken 的 SurfaceControl, 调用 WindowToken 的父容器重新分配Z轴。
- 设置之后的窗口动画的类型为 ENTER;
- 处理可能带来的壁纸、焦点、Ime Target 变化;
- 重新设置Z轴
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) {
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp);
WindowState parentWindow = null;
final int type = attrs.type;
synchronized (mGlobalLock) {
...
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
// 权限检查、错误检查:屏幕不存在、重复添加Window、布局参数检查(主要基于 window tyep)、
// WindowToken 与 WindowState 的窗口类型一致(非子窗口)等
// 省略
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
...
}
ActivityRecord activity = null;
final boolean hasParent = parentWindow != null;
// 子窗口使用父窗口的 token ,可以应用相同的策略
// 根据传入的token(IBinder)查找 WindowToken
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
// 如果是子窗口,检查其父窗口的type
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
final IBinder windowContextToken = attrs.mWindowContextToken;
if (token == null) {
if (hasParent) {
// 子窗口使用父窗口的 token
token = parentWindow.mToken;
} else if (mWindowContextListenerController.hasListener(windowContextToken)) {
final IBinder binder = attrs.token != null ? attrs.token : windowContextToken;
final Bundle options = mWindowContextListenerController
.getOptions(windowContextToken);
token = new WindowToken.Builder(this, binder, type)
.setDisplayContent(displayContent)
.setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
.setRoundedCornerOverlay(isRoundedCornerOverlay)
.setFromClientToken(true)
.setOptions(options)
.build();
} else {
// 创建 WIndowToken, 构造方法中加入层级结构和mTokenMap;
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken.Builder(this, binder, type)
.setDisplayContent(displayContent)
.setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
.setRoundedCornerOverlay(isRoundedCornerOverlay)
.build();
}
} else if (rootType >= FIRST_APPLICATION_WINDOW
&& rootType <= LAST_APPLICATION_WINDOW) {
activity = token.asActivityRecord();
...
} else if (token.asActivityRecord() != null) {
// It is not valid to use an app token with other system types; we will
// instead make a new token for it (as if null had been passed in for the token).
attrs.token = null;
token = new WindowToken.Builder(this, client.asBinder(), type)
.setDisplayContent(displayContent)
.setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
.build();
}
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
win.setRequestedVisibleTypes(requestedVisibleTypes);
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
// 如果是Toast类型,发送延时隐藏消息
if (type == TYPE_TOAST) {
mAtmService.setToastWindow();
if (addToastWindowRequiresToken
|| (attrs.flags & FLAG_NOT_FOCUSABLE) == 0
|| displayContent.mCurrentFocus == null
|| displayContent.mCurrentFocus.mOwnerUid != callingUid) {
mH.sendMessageDelayed(
mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
win.mAttrs.hideTimeoutMilliseconds);
}
}
// From now on, no exceptions or errors allowed!
res = ADD_OKAY;
win.attach(); // 创建 SurfaceSession
// WindowState 加入 mWindowMap
mWindowMap.put(client.asBinder(), win);
// 处理可能带来的壁纸、焦点、Ime Target 变化,省略
boolean imMayMove = true;
// 把 WindowState 加入 WindowToken 的子容器(按照Z轴排序);非子窗口类型,子窗口是其父窗口的子容器。
// 过程中,创建 WindowToken 的 SurfaceControl, 调用 WindowToken 的父容器重新分配Z轴。
win.mToken.addWindow(win); // WindowToken
displayPolicy.addWindowLw(win, attrs);
// 之后的窗口动画类型是 ENTER 而不是 SHOW
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
// window 显示的时候,调用 relayout 计算位置大小
win.getParent().assignChildLayers();
...
}
...
return res;
}