addWindow函数分析
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel)
step 1, 权限检查
// step 1, 权限检查
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
step 2, 窗口类型检查
final int type = attrs.type;
// step 1 , 加锁, 直到addWindow函数结尾,才释放
synchronized(mWindowMap) {
// 一个DisplayContent对应一个绘制屏幕
// step 2, 显示相关判断
final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
if (displayContent == null) {
Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
+ displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
// step 3, 是否已经添加对应的窗口, 去重判断
if (mWindowMap.containsKey(client.asBinder())) {
Slog.w(TAG_WM, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
// step 4, 如果是子窗口类型,则父窗口一定要存在
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
}
step 3, WindowToken检查
根据窗口类型找到对应的WindowToken, 如果不是特定的type,且token不存在,则返回。一个WindowToken对应一系列的WindowState, app窗口对应的token为AppWindowToken
AppWindowToken atoken = null;
final boolean hasParent = parentWindow != null;
// step 1, 根据窗口类型, 获取对应的窗口令牌(WindowToken)
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
// step 2, 某些系统窗口,可以由WMS创建WindowToken,且需要系统权限,否则需要有对应的WindowToken才能添加窗口
if (token == null) {
// App 窗口,需要token为AppWindowToken
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
// 输入法窗口,需要token存在
if (rootType == TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
// 壁纸窗口, 需要token存在
if (rootType == TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
// step 3 , 创建对应的token对象
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow);
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
// app窗口,且对应的token类型为AppWindowToken
atoken = token.asAppWindowToken();
if (atoken == null) {
Slog.w(TAG_WM, "Attempted to add window with non-application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (atoken.removed) {
Slog.w(TAG_WM, "Attempted to add window with exiting application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
} else if (rootType == TYPE_INPUT_METHOD) {
// 输入法窗口,窗口类型为为TYPE_INPUT_METHOD
if (token.windowType != TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (token.asAppWindowToken() != null) {
Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
// 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(this, client.asBinder(), type, false, displayContent,
session.mCanAddInternalSystemWindow);
}
step 4, WindowState对象创建
// step 1, 创建WindowState对象,用来保存窗口状态信息, 把token对象传给WindowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
// step 2, 如果添加窗口的客户端死亡,则返回
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
Slog.w(TAG_WM, "Adding window client " + client.asBinder()
+ " that is dead, aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
// step 3 , 窗口对应的绘制屏幕不存在,直接返回
if (win.getDisplayContent() == null) {
Slog.w(TAG_WM, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
step 5, 窗口调整和权限检查
// mPolicy 实现类为PhoneWindowManager
// step 1, 调整窗口属性, 更新一些flag
mPolicy.adjustWindowParamsLw(win.mAttrs);
// step 2, 权限检查, 根据窗口类型做相关处理
res = mPolicy.prepareAddWindowLw(win, attrs);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
step 6, 根据属性来确定是否需要创建接收输入事件通道
// step 1, 创建输入通道, 用来接收按键事件
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
step 7, 将WindowState记录在WMS相关变量中
// step 1, 绑定SurfaceSession
win.attach();
// step 2, 将WindowState添加到mWindowMap中
mWindowMap.put(client.asBinder(), win);
// step 3, AppOps权限检查此窗口是否有权限显示
if (win.mAppOp != AppOpsManager.OP_NONE) {
int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
win.getOwningPackage());
if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
(startOpResult != AppOpsManager.MODE_DEFAULT)) {
// 将窗口设置为不可见
win.setAppOpVisibilityLw(false);
}
}
final AppWindowToken aToken = token.asAppWindowToken();
// step 4 , 窗口类型为启动窗口
if (type == TYPE_APPLICATION_STARTING && aToken != null) {
aToken.startingWindow = win;
if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
+ " startingWindow=" + win);
}
// step 5, 将窗口添加到对应的token中
win.mToken.addWindow(win);
step 8, 输入法和壁纸相关处理
boolean imMayMove = true;
// step 1, 输入法和壁纸相关处理
if (type == TYPE_INPUT_METHOD) {
win.mGivenInsetsPending = true;
setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
displayContent.computeImeTarget(true /* updateImeTarget */);
imMayMove = false;
} else {
if (type == TYPE_WALLPAPER) {
displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
// step 2, 输入法窗口是否需要重新调整
win.applyAdjustForImeIfNeeded();
// DOCK相关
if (type == TYPE_DOCK_DIVIDER) {
mRoot.getDisplayContent(displayId).getDockedDividerController().setWindow(win);
}
step 9, 更新窗口层级
更新窗口层级实现在DisplayContent的assignWindowLayers中,根据传入的参数是否需要layout,传入参数为false, 表示不需要重新layout,layout在client进程调用WMS的relayoutWindow时会进行layout
boolean focusChanged = false;
// step 1, 如果窗口能接收按键, 则更新Foucus窗口
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
// step 2, 如果输入法移除,需要重新调整输入法窗口
if (imMayMove) {
displayContent.computeImeTarget(true /* updateImeTarget */);
}
// step 3, 更新窗口层级, 不分配Surface(不显示)
displayContent.assignWindowLayers(false /* setLayoutNeeded */);
// step 4, foucus窗口改变, 更新获取按键事件窗口
if (focusChanged) {
mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
}
// step 5, 更新输入法窗口参数
mInputMonitor.updateInputWindowsLw(false /*force*/);
assignWindowLayers
void DisplayContent::assignWindowLayers(boolean setLayoutNeeded) {
// step 1, 分配窗口层级
mLayersController.assignWindowLayers(this);
if (setLayoutNeeded) {
// step 2, 将mLayoutNeeded成员变量置为true
setLayoutNeeded();
}
}
final void WindowLayersController::assignWindowLayers(DisplayContent dc) {
// step 1, 清空成员变量参数
reset();
// step 2, 遍历DisplayContent中的所有窗口
dc.forAllWindows(mAssignWindowLayersConsumer, false /* traverseTopToBottom */);
// step 3, 对特殊窗layer做调整
adjustSpecialWindows();
// step 4, 分屏相关处理
if (mService.mAccessibilityController != null && mAnyLayerChanged
&& dc.getDisplayId() == DEFAULT_DISPLAY) {
mService.mAccessibilityController.onWindowLayersChangedLocked();
}
}
WindowState中实现了sWindowSubLayerComparator比较器,但是WindowToken相同的窗口mBaseLayer相同。mBaseLayer是在创建WindowState的时候确定的。同一窗口类型,高优先级的窗口需要在mBaseLayer的基础上再加一个WINDOW_LAYER_MULTIPLIER,才不会互相踩到,子窗口根据mSubLayer来判断是显示在父窗口上面还是下面,大于0显示在父窗口上面,小于0显示在父窗口下面。getWindowLayerLw函数根据窗口类型返回对应的层级值, 最终实现为WindowManagerPolicy的getWindowLayerFromTypeLw
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
// 子窗口
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
// mBaseLayer和父窗口相同
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
// 根据窗口类型再来确定mSubLayer的值
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
mIsChildWindow = true;
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + parentWindow);
parentWindow.addChild(this, sWindowSubLayerComparator);
mLayoutAttached = mAttrs.type !=
WindowManager.LayoutParams.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
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = 0;
}
}
private static final Comparator<WindowState> sWindowSubLayerComparator =
new Comparator<WindowState>() {
@Override
public int compare(WindowState w1, WindowState w2) {
final int layer1 = w1.mSubLayer;
final int layer2 = w2.mSubLayer;
// 根据mSubLayer的大小确定是在父窗口前面还是后面
if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
return -1;
}
return 1;
};
};
private final Consumer<WindowState> mAssignWindowLayersConsumer = w -> {
boolean layerChanged = false;
int oldLayer = w.mLayer;
// step 1, WindowToken下的WindowState的mBaseLayer相同, 向后遍历, 后面优先级高, 相同token的WindowState加一个偏移值
if (w.mBaseLayer == mCurBaseLayer) {
mCurLayer += WINDOW_LAYER_MULTIPLIER;
} else {
mCurBaseLayer = mCurLayer = w.mBaseLayer;
}
// step 2, 给动画分配layer值
assignAnimLayer(w, mCurLayer);
if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
// 层级改变
layerChanged = true;
mAnyLayerChanged = true;
}
// step 3, mHighestApplicationLayer值调整
if (w.mAppToken != null) {
mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
w.mWinAnimator.mAnimLayer);
}
// step 4, mHighestLayerInImeTargetBaseLayer值调整
if (mImeTarget != null && w.mBaseLayer == mImeTarget.mBaseLayer) {
mHighestLayerInImeTargetBaseLayer = Math.max(mHighestLayerInImeTargetBaseLayer,
w.mWinAnimator.mAnimLayer);
}
// step 5, mHighestDockedAffectedLayer调整
if (w.getAppToken() != null && StackId.isResizeableByDockedStack(w.getStackId())) {
mHighestDockedAffectedLayer = Math.max(mHighestDockedAffectedLayer,
w.mWinAnimator.mAnimLayer);
}
// step 6, 收集特殊窗口, 将特殊类型的窗口添加到指定的列表里面
collectSpecialWindows(w);
if (layerChanged) {
// 动画相关
w.scheduleAnimationIfDimming();
}
};
addWindow函数总结
step 1, 权限检查
step 2, 窗口类型检查
step 3, WindowToken检查
step 4, WindowState对象创建
step 5, 窗口层级处理