一. WMS
WMS 是 Android 中很重要的一个服务,它是 WindowManager 的管理者,WMS 无论对于应用开发还是 Framework 开发来说都是重要的知识点,其原因是因为 WMS 有很多职责,每个职责都会涉及重要且复杂的系统,这使得 WMS 就像一个十字路口的交通灯一样,没有了这个交通灯,十字路口就无法正常通车。
WMS 的职责简单来说主要有以下几点:
- 窗口管理:WMS 是窗口的管理者,它负责窗口的启动、添加和删除。另外窗口的大小和层级也是由 WMS 进行管理的。
- 窗口动画:窗口动画由 WMS 的动画子系统来负责,动画子系统的管理者为 WindowAnimator。
- 输入系统中转站:通过对窗口的触摸从而产生触摸事件,InputManagerService(IMS)会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS 是窗口的管理者,它作为输入系统的中转站再合适不过了。
- Surface 管理:窗口不具备绘制功能,因此每个窗口都需要有一块 Surface 来供自己绘制,为每个窗口分配 Surface 是由 WMS 来完成的。
WMS的职责可以简单总结为下图:
二. WMS 的重要成员
WMS 的部分成员变量如下,本文源码基于 Android 11.0。
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
WindowManagerPolicy mPolicy;
final ArraySet<Session> mSessions = new ArraySet<>();
final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();
/**
* Windows that are being resized. Used so we can tell the client about
* the resize after closing the transaction in which we resized the
* underlying surface.
*/
final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
/**
* Windows whose animations have ended and now must be removed.
*/
final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
/**
* Used when processing mPendingRemove to avoid working on the original array.
*/
WindowState[] mPendingRemoveTmp = new WindowState[20];
/**
* Windows whose surface should be destroyed.
*/
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
/**
* Windows with a preserved surface waiting to be destroyed. These windows
* are going through a surface change. We keep the old surface around until
* the first frame on the new surface finishes drawing.
*/
final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
final InputManagerService mInputManager;
final WindowAnimator mAnimator;
final H mH = new H();
}
1. mPolicy:WindowManagerPolicy
WindowManagerPolicy(WMP)类型的变量。WindowManagerPolicy 是窗口管理策略的接口类,用来定义一个窗口策略所要遵循的通用规范,并提供了 WindowManager 所有的特定的 UI 行为。它的具体实现类为 PhoneWindowManager ,这个实现类在 WMS 创建时被创建。WMP 允许定制窗口层级和特殊窗口类型以及关键的调度和布局。
2. mSessions:ArraySet<Session>
ArraySet<Session> 类型的变量。它主要用于进程间通信,其他的应用程序进程想要和 WMS 进程进行通信就需要经过 Session ,并且每个应用程序进程都会对应一个 Session ,WMS 保存这些 Session 用来记录所有向 WMS 提出窗口管理服务的客户端。
3. mWindowMap:HashMap<IBinder, WindowState>
mWindowMap 是一个 HashMap,key 的类型为 IBinder ,value 的类型为 WindowState 。WindowState 用于保存窗口的信息,在 WMS 中它用来描述一个窗口。综上得出结论,mWindowMap 就是用来保存 WMS 中各种窗口的集合。
4.mResizingWindows:ArrayList<WindowState>
mResizingWindows 是用来存储正在调整大小的窗口的列表的。与 mResizingWindows 类似的还有 mPendingRemove、mDestroySurface 和 mDestroyPreservedSurface 等,其中 mPendingRemove 是在内存耗尽时设置的,里面存有需要强制删除的窗口,mDestroySurface 里面存有需要被销毁的Surface,mDestroyPreservedSurface 里面存有窗口需要保存的等待销毁的 Surface,为什么窗口要保存这些 Surface ?这是因为当窗口经历 Surface 变化时,窗口需要一直保持旧的 Surface,直到新 Surface 的第一帧绘制完成。
5.mAnimator:WindowAnimator
mAnimator 是 WindowAnimator 类型的变量,用于管理窗口的动画以及特效动画。
6.mH:H
mH 是 H 类型的变量,系统的 Handler 类,用于将任务加入到主线程的消息队列中,这样代码逻辑就会在主线程中执行。
7.mInputManager:InputManagerService
InputManagerService 类型的变量,输入系统的管理者。InputManagerService(IMS)会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS 是窗口的管理者,因此,WMS “理所应当” 的成为了输入系统的中转站。
还需要理解的几个概念:
WindowToken:
- 可以理解为窗口令牌,当应用程序想要向 WMS 申请新创建一个窗口,则需要向 WMS 出示有效的 WindowToken。WindowToken 会将相同组件(比如同一个Acitivity)的窗口(WindowState)集合在一起,方便管理。
WindowState:
- WindowState 表示一个窗口的所有属性,且存在于 WMS 端,所以它是 WMS 中事实上的窗口。App 端的一个 Window,在 WMS 端就会有一个 WindowState 。
三. Window 的添加过程(WMS 处理的部分)
对 Window 的操作分为两大部分,一部分是 WindowManger 的处理,在 Window和WindowManager源码解析 中已经分析过,另一部分是 WMS 的处理,这里我们来分析 WMS 处理的部分。Window 的添加过程都会调用 WMS 的 addWindow() 方法,由于这个方法代码逻辑比较多,这里分成3个部分来分析。
3.1 窗口检查
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
int requestUserId) {
Arrays.fill(outActiveControls, null);
int[] appOp = new int[1];
final boolean isRoundedCornerOverlay = (attrs.privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
// 调用 WindowManagerPolicy 的 checkAddPermission() 方法来检查权限
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
...
synchronized (mGlobalLock) {
...
// 通过 DisplayId 来获得窗口要添加到哪个 DisplayContent 上
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
// 如果没有找到 DisplayContent,返回 WindowManagerGlobal.ADD_INVALID_DISPLAY
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 (!displayContent.hasAccess(session.mUid)) {
ProtoLog.w(WM_ERROR,
"Attempted to add window to a display for which the application "
+ "does not have access: %d. Aborting.", displayId);
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
if (mWindowMap.containsKey(client.asBinder())) {
ProtoLog.w(WM_ERROR, "Window %s is already added", client);
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
// type 在 FIRST_SUB_WINDOW 与 LAST_SUB_WINDOW 之间(1000~1999),说明是子窗口
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
// attrs.token 是 IBinder 类型的对象
parentWindow = windowForClientLocked(null, attrs.token, false);
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() 的各种状态,是一个 int 值,定义在 WindowManagerGlobal 中。在 addWindow() 方法中先调用 WindowManagerPolicy 的 checkAddPermission() 方法来检查权限,具体在 PhoneWindowManager 的 checkAddPermission() 方法中实现,如果没有权限就不会执行后续的代码逻辑。然后通过 DisplayId 来获得窗口要添加到哪个 DisplayContent 上,如果没有找到 DisplayContent ,返回 ADD_INVALID_DISPLAY,其中 DisplayContent 是用来描述一块屏幕的。其中 type 表示窗口的类型,如果介于 FIRST_SUB_WINDOW 和 LAST_SUB_WINDOW 之间,说明这个窗口是一个子窗口,要拿到父窗口的 WindowState,attrs.token 是 IBinder 类型,windowForClientLocked() 方法会根据 attrs.token 作为 key 值从 mWindowMap 中得到该子窗口的父窗口。接着对父窗口进行判断,如果父窗口的 WindowState 为 null 或者父窗口的 type 也是子窗口的范围,会返回错误状态。
3.2 WindowToken 相关处理
// 通过 DisplayContent 尝试获取 WindowToken
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
// 如果有父窗口,就将父窗口的 type 赋值给 rootType,如果没有就将当前窗口的 type 赋值给 rootType
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
// 如果 token 为 null
if (token == null) {
// 使用 unprivilegedAppCanCreateTokenWith() 方法对 rootType 和 type 进行检查
if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
rootType, attrs.token, attrs.packageName)) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
// 创建 WindowToken,第 4 个参数为 false ,表示这个 WindowToken 是隐式创建的
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
} else if (rootType >= FIRST_APPLICATION_WINDOW
&& rootType <= LAST_APPLICATION_WINDOW) {
activity = token.asActivityRecord();
if (activity == null) {
ProtoLog.w(WM_ERROR, "Attempted to add window with non-application token "
+ ".%s Aborting.", token);
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (activity.getParent() == null) {
ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token "
+ ".%s Aborting.", token);
return WindowManagerGlobal.ADD_APP_EXITING;
} else if (type == TYPE_APPLICATION_STARTING && activity.startingWindow != null) {
ProtoLog.w(WM_ERROR,
"Attempted to add starting window to token with already existing"
+ " starting window");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
}
...
这里先通过 DisplayContent 的 getWindowToken() 方法尝试获取 WindowToken ,如果有父窗口,就将父窗口的 type 赋值给 rootType ,如果没有,就将当前窗口的 type 赋值给 rootType 。接下来如果 token 为 null ,使用 unprivilegedAppCanCreateTokenWith() 方法对 rootType 和 type 进行检查,里面检查如果 rootType 值等于 TYPE_INPUT_METHOD、TYPE_WALLPAPER 等值时,则返回 ADD_BAD_APP_TOKEN,说明 rootType 值等于 TYPE_INPUT_METHOD、TYPE_WALLPAPER 等值时是不允许 WindowToken 为 null 的。如果检查通过了,会创建 WidowToken,这说明当我们添加窗口时可以不向 WMS 提供 WindowToken,前提是 rootType 和 type 的值不为前面条件判断筛选的值。WindowToken 隐式和显式的创建肯定是要加以区分的,创建 WidowToken 传入的第 4 个参数为 false 就表示这个 WindowToken 是隐式创建的。
接下来的代码逻辑就是 WindowToken 不为 null 的情况,根据 rootType 和 type 的值进行判断。先判断如果窗口为应用程序窗口,通过 WindowToken 的 asActivityRecord() 方法拿到 ActivityRecord,然后根据 ActivityRecord 进行后续的判断,返回不同的状态码。
3.3 WindowState 的创建和处理
// 创建 WindowState,它存有窗口的所有状态信息,在 WMS 中它代表一个窗口,
// this 指的是 WMS,client 指的是 IWindow, token 指的是 WindowToken。
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
// 判断请求添加窗口的客户端是否已经死亡
if (win.mDeathRecipient == null) {
// 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;
}
// 判断窗口的 DisplayContent 是否为 null
if (win.getDisplayContent() == null) {
ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
// 根据窗口的 type 对窗口的 LayoutParams 的一些成员变量进行修改
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
// 根据窗口的 type 检查是否可以把这个窗口添加到系统中
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
...
win.attach();
//将 WindowState 添加到 mWindowMap 中
mWindowMap.put(client.asBinder(), win);
win.initAppOpsState();
final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(),
UserHandle.getUserId(win.getOwningUid()));
win.setHiddenWhileSuspended(suspended);
final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
final ActivityRecord tokenActivity = token.asActivityRecord();
if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) {
tokenActivity.startingWindow = win;
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
activity, win);
}
boolean imMayMove = true;
//将 WindowState 添加到该 WindowState 对应的 WindowToken 中
win.mToken.addWindow(win);
displayPolicy.addWindowLw(win, attrs);
if (type == TYPE_INPUT_METHOD) {
displayContent.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)) {
// If there is currently a wallpaper being shown, and
// the base layer of the new window is below the current
// layer of the target window, then adjust the wallpaper.
// This is to avoid a new window being placed between the
// wallpaper and its target.
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
...
先创建了 WindowState,它存有窗口的所有的状态信息,在 WMS 中它代表一个窗口。在创建 WindowState 传入的参数中,this 指的是 WMS,client 指的是 IWindow,IWindow 会将 WMS 中窗口管理的操作回调给 ViewRootImpl,token 指的是 WindowToken ,紧接着分别判断请求添加窗口的客户端是否已经死亡、窗口的 DisplayContent 是否为 null,如果是则不会再执行下面的代码逻辑。然后调用了 DisplayPolicy 的 adiustWindowParamsLw() 方法,此方法会根据窗口的 type 对窗口的 LayoutParams 的一些成员变量进行修改。然后调用 DisplayPolicy 的 validateAddingWindowLw() 方法,用于检查是否可以把这个窗口添加到系统中。接着将 WindowState 添加到 mWindowMap 中,然后将 WindowState 添加到该 WindowState 对应的 WindowToken 中(实际是保存在 WndowToken 的父类 WindowContainer 中),这样 WindowToken 就包含了同一个组件的 WindowState。
3.4 addWindow()方法总结
addWindow() 方法分了 3 个部分来进行讲解,主要就是做了下面 4 件事:
- 对所要添加的窗口进行检查,如果窗口不满足一些条件,就不会再执行下面的代码逻辑。
- WindowToken 相关的处理,比如有的窗口类型需要提供 WindowToken ,没有提供的话就不会执行下面的代码逻辑,有的窗口类型则可以由 WMS 隐式创建 WindowToken。
- WindowState的创建和相关处理,将 WindowToken 和 WindowState 相关联。
- 创建和配置 DisplayContent,完成窗口添加到系统前的准备工作。
四. Window 的删除过程
Window 的删除过程会调用 WMS 的 removeWindow() 方法:
void removeWindow(Session session, IWindow client) {
synchronized (mGlobalLock) {
WindowState win = windowForClientLocked(session, client, false); // 1
if (win != null) {
win.removeIfPossible(); // 2
return;
}
// Remove embedded window map if the token belongs to an embedded window
mEmbeddedWindowController.remove(client);
}
}
在注释 1 处获取 Window 对应的 WindowState,WindowState 用于保存窗口的信息,在 WMS 中它用来描述一个窗口。接着在注释 2 处调用 WindowState 的 removelfPossible() 方法,如下所示:
void removeIfPossible() {
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
immediatelyNotifyBlastSync();
}
里面接着又调用了带参数的 removeIfPossible() 方法:
private void removeIfPossible(boolean keepVisibleDeadWindow) {
// 条件判断过滤,满足其中一个条件就会 return,推迟删除操作
...
removeImmediately(); // 1
// Removing a visible window will effect the computed orientation
// So just update orientation if needed.
if (wasVisible) {
final DisplayContent displayContent = getDisplayContent();
if (displayContent.updateOrientation()) {
displayContent.sendNewConfiguration();
}
}
mWmService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
: UPDATE_FOCUS_NORMAL,
true /*updateInputWindows*/);
}
removelfPossible() 方法和它的名字一样,并不是直接执行删除操作的,而是进行多个条件判断过滤,满足其中一个条件就会 return,推迟删除操作。比如 View 正在运行一个动画这时就得推迟删除操作,直到动画完成。通过这些条件判断过滤就会执行注释 1 处的 removeImmediately() 方法:
void removeImmediately() {
super.removeImmediately();
if (mRemoved) { // 1
// Nothing to do.
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
"WS.removeImmediately: %s Already removed...", this);
return;
}
mRemoved = true;
...
dc.getDisplayPolicy().removeWindowLw(this); // 2
disposeInputChannel();
mWinAnimator.destroyDeferredSurfaceLocked();
mWinAnimator.destroySurfaceLocked();
mSession.windowRemovedLocked(); // 3
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.)
}
mWmService.postWindowRemoveCleanupLocked(this); // 4
}
removelmmediately() 方法如同它的名字一样,用于立即进行删除操作。在注释 1 处的 mRemoved 为 true 意味着正在执行删除 Window 操作。在注释 2 处如果当前要删除的 Window 是 StatusBar 或者 NavigationBar,就会将这个 Window 从对应的控制器中删除。在注释 3 处将对应的 Session 从 WMS 的 ArraySet<Session> 类型的参数 mSessions 中删除并清除 Session 对应的 SurfaceSession 资源(SurfaceSession 是 SurfaceFlinger 的一个连接,通过这个连接可以创建 1 个或者多个 Surface 并渲染到屏幕上)。在注释 4 处调用了 WMS 的 postWindowRemoveCleanupLocked() 方法用于对 View 进行一些集中的清理工作。