As we all know, WMS is an important service. Its main functions are:
- Window management: responsible for adding, starting, and deleting Windows, window size, level is also WMS management
- Window animation: Window switch animation management, management class as WindowAnimator
- Input system relay: InputManagerService handles touch events, and WMS acts as a relay to find the appropriate window to process feedback.
- Surface management: Each window has a Surface, Surface to draw.
The creation of a WMS
WMS is created in the SystemServer process, in the SystemServer main method.
In the main method of SystemServer, the system respectively called startBootstrapServices, startCoreServices, startOtherServices three methods to respectively start the boot service, core service, other services.
The WMS is started in the startOtherServices method.
In the startOtherServices method, you first get the Watchdog instance, which is used to monitor the health of key services. Then the IMS (InputManagerService) is created. The WMS instance is then obtained through the WindowManagerService main method, which passes in IMS. Then register the IMS and WMS with the ServiceManager. Finally, WMS’s displayReady method is called to initialize the screen display information, and WMS’s systemReady method is called to notify WMS that the system initialization is complete.
Let’s look at the WMS constructor:
private WindowManagerService(Context context, InputManagerService inputManager, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy) { ... mInputManager = inputManager; // Must be before createDisplayContentLocked. ... mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); mDisplays = mDisplayManager.getDisplays(); for (Display display : mDisplays) { createDisplayContentLocked(display); }... mActivityManager = ActivityManager.getService(); . mAnimator = new WindowAnimator(this); mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout); LocalServices.addService(WindowManagerInternal.class, new LocalService()); initPolicy(); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); . }Copy the code
The code is simplified and we can see that in the WMS constructor, the inputManager is assigned first.
Then you get the DisplayManager, and you get the Display array via the getDisplays method, which is supposed to have an instance of Display on every Display device.
By traversal will display by encapsulated into DisplayContent createDisplayContentLocked method, and set in the WMS, DisplayContent used to describe a screen.
The AMS instance is then retrieved and the WindowAnimator is created to manage window animations.
We then initialize the window management policy interface class WindowManagerPolicy, which defines the specification that a window policy follows. Then add the WMS to the Watchdog with addMonitor. The Watchdog checks system services every minute. If a deadlock occurs, the SystemServer process is killed.
An important member of WMS
To understand WMS, you first need to link the important member variables in WMS:
//WindowManagerPolicy is a window management policy interface class that defines a general specification for window policies and provides all specific UI behavior for WindowManager. The implementation class is PhoneWindowManager, which is created when WMS is created. final WindowManagerPolicy mPolicy; final IActivityManager mActivityManager; final ActivityManagerInternal mAmInternal; final AppOpsManager mAppOps; final DisplaySettings mDisplaySettings; As mentioned earlier, for inter-process communication, other applications that want to communicate with the WMS need to use a Session, // Each application process has a Session. Final ArraySet<Session> mSessions = new ArraySet<>(); // the WindowHashMap value is limited to a WindowState. A WindowState contains information about a window. Final WindowHashMap mWindowMap = new WindowHashMap(); WindowToken has two functions. One is used as a WindowToken. When an application wants to create a window with WMS, it needs to present a valid WindowToken to WMS. AppWindowTOken is a subclass of WindowToken and is used to describe the //WindowToken structure of an application. Each Activity in the application corresponds to an AppWindowTOken. //WindowToken also groups Windows on the same Page to Advantage on Advantage. //mFinishedStarting is an AppWindowToken used to store acElasticity that has been started and completed. final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>(); // Use it to store when the drawing is done, AppWindowToken final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>(); // Save information about the window being resized. final ArrayList<WindowState> mResizingWindows = new ArrayList<>(); // Now we have a final WindowAnimator. Final H mH = new H(); final H mH = new H(); // The input system manager manages touch events. final InputManagerService mInputManager;Copy the code
Window adding process
Either to Ace Association or to system Windows, their addition processes call the addWindow method of WMS, which we will explain next
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
int[] appOp = new int[1];
//这里最终调用的是PhoneWindowManager的checkAddPermission方法,
//根据传入的attrs中的type来判断是否可以添加窗口,如果不行,直接返回ADD_INVALID_DISPLAY状态
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
...
synchronized(mWindowMap) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
//根据displayId获取窗口要添加到哪个DisplayContent,
//如果没有找到返回ADD_INVALID_DISPLAY状态。
//DisplayContent用来描述一块屏幕
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;
}
...
//这里判断type是否是大于1000小于1999,如果是,说明是一个子窗口,类似PopupWindow这种
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
//通过windowForClientLocked方法,传入token,token是IBinder类对象,
//方法内部会把token作为key,从mWindowMap中查找该子窗口的父窗口
parentWindow = windowForClientLocked(null, attrs.token, false);
//对父窗口进行判断,如果父窗口为null或者父窗口type不正确,都会返回错误的状态。
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;
}
}
...
AppWindowToken atoken = null;
final boolean hasParent = parentWindow != null;
//通过getWindowToken方法获取WindoToken
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
//根据是否有父窗口,获取正确的type
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
//如果token为null,进行其他判断
if (token == null) {
//如果添加的type为应用程序窗口,token不能为null,这里直接返回错误状态
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;
}
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;
}
if (rootType == TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_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;
}
if (rootType == TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
parentWindow)) {
Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
//进过一系列的判断,会在这里创建WindowToken,这里传入的第四个参数为false,代表这里是
//隐式创建的
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow);
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
//如果token不为null,创建的是应用程序窗口,直接获取AppWindowToken
atoken = token.asAppWindowToken();
//根据atoken是否为null返回不同的返回值。
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) {
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 (rootType == TYPE_VOICE_INTERACTION) {
if (token.windowType != TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_WALLPAPER) {
if (token.windowType != TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_DREAM) {
if (token.windowType != TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
callingUid, parentWindow);
if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
Slog.w(TAG_WM, "Attempted to add a toast window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_QS_DIALOG) {
if (token.windowType != TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog 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);
}
//如果上面的的判断没有return,会在这里创建一个WindowState,this为当前WMS,
//session是用来和WMS通信的,client是IWindow对象,
//IWindow会将WMS操作回调给ViewRootImpl,token是WindowToken。
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
//判断添加窗口的客户端是否还存在
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;
}
//判断窗口的DisplayContent是否还存在
if (win.getDisplayContent() == null) {
Slog.w(TAG_WM, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
//调用PhoneWindowManager的adjustWindowParamsLw,
//在方法中会根据窗口的type对LayoutParamas进行一些修改
//这里传入的win.mAttrs其实就是WindowManager.LayoutParams
mPolicy.adjustWindowParamsLw(win.mAttrs);
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
//调用prepareAddWindowLw准备将窗口添加到系统中。如果添加的是应用程序窗口
//这里不会做额外操作,会回直接返回ADD_OKAY状态
res = mPolicy.prepareAddWindowLw(win, attrs);
...
win.attach();
//将WindowState添加到WindowHashMap中,key就是将IWindow转换成Binder
mWindowMap.put(client.asBinder(), win);
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();
if (type == TYPE_APPLICATION_STARTING && aToken != null) {
aToken.startingWindow = win;
if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
+ " startingWindow=" + win);
}
boolean imMayMove = true;
//将WindowState添加到对应的WindowToken中
win.mToken.addWindow(win);
...
boolean focusChanged = false;
if (win.canReceiveKeys()) {
//更新焦点窗口
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
...
//window已经展示,如果方向有变动,更新相应参数
if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false, displayId)) {
reportNewConfig = true;
}
return res;
}
Copy the code
So that’s the process of adding Windows. There’s a lot of stuff. Take your time.
Window deletion procedure
Deleting a window first calls the removeView method of Windows Global:
public void removeView(View view, boolean immediate) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } synchronized (mLock) { int index = findViewLocked(view, true); View curView = mRoots.get(index).getView(); removeViewLocked(index, immediate); if (curView == view) { return; } throw new IllegalStateException("Calling with view " + view + " but the ViewAncestor is attached to " + curView); }}Copy the code
Here we make some judgments about the view and call removeViewLocked:
private void removeViewLocked(int index, boolean immediate) { ViewRootImpl root = mRoots.get(index); View view = root.getView(); if (view ! = null) { InputMethodManager imm = InputMethodManager.getInstance(); if (imm ! = null) { imm.windowDismissed(mViews.get(index).getWindowToken()); } } boolean deferred = root.die(immediate); if (view ! = null) { view.assignParent(null); if (deferred) { mDyingViews.add(view); }}}Copy the code
Get the View View based on the index passed in. It then gets InputMethodManager and calls windowDismissed to end the touch-related logic. Then call the ViewRootImpl’s die method.
Let’s look at the die method:
boolean die(boolean immediate) { // Make sure we do execute immediately if we are in the middle of a traversal or the damage // done by dispatchDetachedFromWindow will cause havoc on return. if (immediate && ! mIsInTraversal) { doDie(); return false; } if (! mIsDrawing) { destroyHardwareRenderer(); } else { Log.e(mTag, "Attempting to destroy the window while drawing! \n" + " window=" + this + ", title=" + mWindowAttributes.getTitle()); } mHandler.sendEmptyMessage(MSG_DIE); return true; }Copy the code
Here we can see that if immediate is true and mIsInTraversal is false, doDie() is executed immediately and the following code is not executed. MIsInTraversal is set to true when performing ViewRootImpl’s performTraversals method and to false when the method is finished. This means that if performTraversals are not executed at this time, the doDie method can be executed immediately.
Let’s look at the doDie method:
Void doDie() {// Check whether the current thread is the primary thread. if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface); Synchronized (this) {// The method is returned if it was already called, but mRemoved is set to true if it was not. if (mRemoved) { return; } mRemoved = true; / / if there is a child View, back to call dispatchDetachedFromWindow method to destroy the View the if (mAdded) {dispatchDetachedFromWindow (); }... mAdded = false; } / / the last call doRemove method WindowManagerGlobal. GetInstance () doRemoveView (this); }Copy the code
Here’s the code for the doRemoveView method:
void doRemoveView(ViewRootImpl root) { synchronized (mLock) { final int index = mRoots.indexOf(root); if (index >= 0) { mRoots.remove(index); mParams.remove(index); final View view = mViews.remove(index); mDyingViews.remove(view); } } if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) { doTrimForeground(); }}Copy the code
Delete view-related information
Let’s take a look at the dispatchDetachedFromWindow doDie method methods:
void dispatchDetachedFromWindow() {
...
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
...
}
Copy the code
We can see that we’re calling the remove method of mWindowSession. MWindowSession is IWindowSession, and IWindowSession is implemented on the server side.
mService.removeWindow(this,window);
Copy the code
Here the mService is WMS, let’s look at the removeWindow method in WMS:
void removeWindow(Session session, IWindow client) { synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null) { return; } win.removeIfPossible(); }}Copy the code
Now get WindowState, which is used to hold window information, and call the removeIfPossible method of WindowState
@Override
void removeIfPossible() {
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
}
private void removeIfPossible(boolean keepVisibleDeadWindow) {
...
removeImmediately();
// Removing a visible window will effect the computed orientation
// So just update orientation if needed.
if (wasVisible && mService.updateOrientationFromAppTokensLocked(false, displayId)) {
mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
Binder.restoreCallingIdentity(origId);
}
Copy the code
Advanced some judgments, if true will delay deleting the View first. If not, the removeImmediately method is executed.
@Override void removeImmediately() { super.removeImmediately(); If (mRemoved) {// Nothing to do. if (DEBUG_ADD_REMOVE) slog. v(TAG_WM, "WS. RemoveImmediately: " + this + " Already removed..." ); return; } mRemoved = true; mWillReplaceWindow = false; if (mReplacementWindow ! = null) { mReplacementWindow.mSkipEnterAnimationForSeamlessReplacement = false; } final DisplayContent dc = getDisplayContent(); if (mService.mInputMethodTarget == this) { dc.computeImeTarget(true /* updateImeTarget */); } final int type = mAttrs.type; if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) { dc.mTapExcludedWindows.remove(this); } mPolicy.removeWindowLw(this); disposeInputChannel(); mWinAnimator.destroyDeferredSurfaceLocked(); mWinAnimator.destroySurfaceLocked(); mSession.windowRemovedLocked(); try { mClient.asBinder().unlinkToDeath(mDeathRecipient, 0); } catch (RuntimeException e) { // Ignore if it has already been removed (usually because // we are doing this as part of processing a death note.) } mService.postWindowRemoveCleanupLocked(this); }Copy the code
The method is to View some information about the processing, finally call postWindowRemoveCleanupLocked is convenient and clear View. At this point, the general deletion of the Window process is complete.