Window的添加过程(WMS处理部分)

基于Android U

我们知道Window的操作分为两大部分,一部分是WindowManager处理部分,另一部分是WMS处理部分。对于WMS处理部分,无论是系统窗口还是Activity,它们的Window的添加过程都会调用WMS的addWindow()方法。

addWindow()方法part1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
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) {
outActiveControls.set(null);
int[] appOp = new int[1];
final boolean isRoundedCornerOverlay = (attrs.privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp); // 1
if (res != ADD_OKAY) {
return res;
}
...
synchronized (mGlobalLock) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}

final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token); // 2

if (displayContent == null) {
ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "
+ "not exist: %d. Aborting.", displayId);
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
...
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { // 3
parentWindow = windowForClientLocked(null, attrs.token, false); // 4
if (parentWindow == null) {
ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
+ "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
+ "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}

...
}
...
return res;
}

WMS的addWindow()返回的是addWindow的各种状态,比如添加Window成功,无效的display等,这些状态被定义在WindowManagerGlobal中。

在注释1处根据Window的属性,调用WMP的checkAddPermission()方法来检查权限,具体在PhoneWindowManager的checkAddPermission()方法中实现,如果没有权限则不会执行后续的代码逻辑。

在注释2处通过displayId来获得窗口要添加到哪个DisplayContent上,如果没有找到DisplayContent,则返回WindowManagerGlobal.ADD_INVALID_DISPLAY这一状态,其中DisplayContent用来描述一块屏幕。

在注释3处,type代表一个窗口的类型,它的数值介于FIRST_SUB_WINDOW和LAST_SUB_WINDOW之间(1000~1999),这个数值定义在WindowManager中,说明这个窗口是一个子窗口。

在注释4处,attrs.token是IBinder类型的对象,windowForClientLocked()方法内部会根据attrs.token作为key值从mWindowMap中得到该子窗口的父窗口。接着对父窗口进行判断,如果父窗口为null或者type的取值范围不正确则会返回错误的状态。

addWindow()方法part2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
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) {
...
synchronized (mGlobalLock) {
...
final boolean hasParent = parentWindow != null;
// Use existing parent window token for child windows since they go in the same token
// as there parent window so we can apply the same policy on them.
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token); // 1
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
final int rootType = hasParent ? parentWindow.mAttrs.type : type; // 2

boolean addToastWindowRequiresToken = false;

final IBinder windowContextToken = attrs.mWindowContextToken;

if (token == null) { // 3
if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
rootType, attrs.token, attrs.packageName)) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (hasParent) {
// Use existing parent window token for child windows.
token = parentWindow.mToken;
} else if (mWindowContextListenerController.hasListener(windowContextToken)) {
// Respect the window context token if the user provided it.
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 {
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
...
}
...
return res;
}

注释1处通过displayContent的getWindowToken()方法得到WindowToken。

注释2处,如果有父窗口就将父窗口的type值赋值给rootType,如果没有,将当前窗口的type值赋值给rootType。

注释3处,如果WindowToken为null,则创建WindowToken。

addWindow()方法part3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
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) {
...
synchronized (mGlobalLock) {
...
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow); // 1
if (win.mDeathRecipient == null) { // 2
// Client has apparently died, so there is no reason to
// continue.
ProtoLog.w(WM_ERROR, "Adding window client %s"
+ " that is dead, aborting.", client.asBinder());
return WindowManagerGlobal.ADD_APP_EXITING;
}

if (win.getDisplayContent() == null) { // 3
ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}

final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs); // 4
attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid);
attrs.inputFeatures = sanitizeSpyWindow(attrs.inputFeatures, win.getName(), callingUid,
callingPid);
win.setRequestedVisibleTypes(requestedVisibleTypes);

res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid); // 5
...
win.attach();
mWindowMap.put(client.asBinder(), win); // 6
win.initAppOpsState();
...
win.mToken.addWindow(win); // 7
displayPolicy.addWindowLw(win, attrs);
displayPolicy.setDropInputModePolicy(win, win.mAttrs);
...
}
...
return res;
}

注释1处创建了WindowState,它存有窗口的所有的状态信息,在WMS中它代表一个窗口。在创建WindowState传入的参数中,this指的是WMS,client指的是IWindow,IWindow会将WMS中窗口管理的操作回调给ViewRootImpl,token指的是WindowToken。

注释2、3处分别判断请求添加窗口的客户端是否已经死亡、窗口的DisplayContent是否为null,如果是则不会再执行下面的代码逻辑。

注释4处调用了DisplayPolicy的adjustWindowParamsLw()方法,此方法会根据窗口的type对窗口的LayoutParams的一些成员变量进行修改。

注释5处检查Window是否可以被添加到系统中。

注释6处将WindowState添加到mWindowMap中。

注释7处将WindowState添加到该WindowState对应的WindowToken中(实际是保存在WindowToken的父类WindowContainer中),这样WindowToken就包含了同一个组件的WindowState。


Window的添加过程(WMS处理部分)
https://citrus-maxima.github.io/2024/03/10/Window的添加过程(WMS处理部分)/
作者
柚子树
发布于
2024年3月10日
许可协议