当前位置: 首页 > news >正文

Android14 WMS-窗口添加流程(二)-Server端

Android14 WMS-窗口添加流程(一)-Client端-CSDN博客

本文接着上文"Android14 WMS-窗口添加流程(一)-Client端"往下讲。也就是WindowManagerService#addWindow流程。

目录

一. WindowManagerService#addWindow

标志1:mPolicy.checkAddPermission

标志2:getDisplayContentOrCreate

标志3: mWindowMap

二:窗口类型检查

三:新建WindowToken

标志1 WindowToken

四: 新建WindowState

五:adjustWindowParamsLw

六:窗口ADD_OKAY后续流程


Client端流程如下

Server端流程如下

一. WindowManagerService#addWindow

http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java#1431

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; //标志1 int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName, appOp); if (res != ADD_OKAY) { return res; } //父window WindowState parentWindow = null; //发起者Uid final int callingUid = Binder.getCallingUid(); //发起者Pid final int callingPid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); //窗口类型 final int type = attrs.type; synchronized (mGlobalLock) { if (!mDisplayReady) { throw new IllegalStateException("Display has not been initialialized"); } //一个DisplayContent对应一个绘制屏幕 //标志2 final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token); 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.", displayContent.getDisplayId()); return WindowManagerGlobal.ADD_INVALID_DISPLAY; } //是否已经添加对应的窗口, 去重判断 //标志3 if (mWindowMap.containsKey(client.asBinder())) { ProtoLog.w(WM_ERROR, "Window %s is already added", client); return WindowManagerGlobal.ADD_DUPLICATE_ADD; }

由于此方法太长,所以我会分开去讲

标志1:mPolicy.checkAddPermission

mPolicy定义如下,如果此方法返回ADD_OKAY,则代码无权限问题,否则直接返回。

WindowManagerPolicy mPolicy; ------------------------------------------intres = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName, appOp);if(res != ADD_OKAY) {returnres; }

WindowManagerPolicy.java - OpenGrok cross reference for /frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java

/**当add window的时候检查权限 * Check permissions when adding a window. * *@paramtypeThe window type--窗口类型 *@paramisRoundedCornerOverlay{@codetrue} to indicate the adding window is * round corner overlay. 指示要add的窗口为圆角叠加层。 *@parampackageNamepackage name 包名 *@paramoutAppOpFirst element will be filled with the app op corresponding to this window, or OP_NONE.第一个元素将填充与此窗口对应的应用操作,或OP_NONE。 * *@return{@linkWindowManagerGlobal#ADD_OKAY} if the add can proceed; * else an error code, usually * {@linkWindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add. *//返回是否有权限添加,有则返回ADD_OKAY *@seeWindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY */intcheckAddPermission(inttype,booleanisRoundedCornerOverlay, String packageName,int[] outAppOp);

这里只是声明,没有实现,具体实现如下

PhoneWindowManager.java - OpenGrok cross reference for /frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName, int[] outAppOp) { //如果是圆角覆盖并且无PERMISSION_GRANTED权限,也无法添加window if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW) != PERMISSION_GRANTED) { return ADD_PERMISSION_DENIED; } outAppOp[0] = AppOpsManager.OP_NONE; //如果窗口类型不在系统划分的三大窗口范围内,则返回不合法的type--ADD_INVALID_TYPE if (!((type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) || (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) || (type >= FIRST_SYSTEM_WINDOW && type <= LAST_SYSTEM_WINDOW))) { return WindowManagerGlobal.ADD_INVALID_TYPE; } //如果窗口类型不是系统窗口类型,比如是APP窗口类型或者是子窗口类型,系统最大窗口类型为LAST_SYSTEM_WINDOW,则可以添加,返回ADD_OKAY if (type < FIRST_SYSTEM_WINDOW || type > LAST_SYSTEM_WINDOW) { // Window manager will make sure these are okay. return ADD_OKAY; } //如果窗口类型不是alert window, alert window有好几种,可以查看这个方法 if (!isSystemAlertWindowType(type)) { switch (type) { case TYPE_TOAST: // Only apps that target older than O SDK can add window without a token, after // that we require a token so apps cannot add toasts directly as the token is // added by the notification system. // Window manager does the checking for this. outAppOp[0] = OP_TOAST_WINDOW; return ADD_OKAY; case TYPE_INPUT_METHOD: case TYPE_WALLPAPER: case TYPE_PRESENTATION: case TYPE_PRIVATE_PRESENTATION: case TYPE_VOICE_INTERACTION: case TYPE_ACCESSIBILITY_OVERLAY: case TYPE_QS_DIALOG: case TYPE_NAVIGATION_BAR_PANEL: // The window manager will check these. return ADD_OKAY; } return (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED; } // Things get a little more interesting for alert windows... outAppOp[0] = OP_SYSTEM_ALERT_WINDOW; final int callingUid = Binder.getCallingUid(); // system processes will be automatically granted privilege to draw //如果是系统进程,则直接允许 if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) { return ADD_OKAY; } ... }

标志2:getDisplayContentOrCreate

finalDisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);

此方法定义如下,其作用是"Get existing {@link DisplayContent} or create a new one if the display is registered in DisplayManager.",即“获取现有的DisplayContent,如果display已在 DisplayManager 中注册,则创建一个新DisplayContent”.

仅当尚未创建与刚刚添加到 DisplayManager 的display相对应的DisplayContent时,才应使用此选项。这通常意味着此方法的调用是从 Activity 或窗口管理器外部启动的。

/** * Get existing {@link DisplayContent} or create a new one if the display is registered in * DisplayManager. * * NOTE: This should only be used in cases when there is a chance that a {@link DisplayContent} * that corresponds to a display just added to DisplayManager has not yet been created. This * usually means that the call of this method was initiated from outside of Activity or Window * Manager. In most cases the regular getter should be used. * @param displayId The preferred display Id. * @param token The window token associated with the window we are trying to get display for. * if not null then the display of the window token will be returned. Set to null * is there isn't an a token associated with the request. * @see RootWindowContainer#getDisplayContent(int) */ private DisplayContent getDisplayContentOrCreate(int displayId, IBinder token) { if (token != null) { //WM中相关联的窗口集合 //tag1 final WindowToken wToken = mRoot.getWindowToken(token); if (wToken != null) { //tag2 return wToken.getDisplayContent(); } } //tag3 return mRoot.getDisplayContentOrCreate(displayId); }

这块涉及到Container,我们来看看继承关系

设备的Root WindowContainer,一个设备只有一个RootWindowContainerclassRootWindowContainerextendsWindowContainer<DisplayContent>implementsDisplayManager.DisplayListener {

一个设备只有一个RootWindowContainer, 因为它的赋值是在WMS的实例化中,WMS实例在全局只有一个,是从SystemServer中发起的。

privateWindowManagerService(Context context, InputManagerService inputManager,booleanshowBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,DisplayWindowSettingsProvider displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory, Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { ... mRoot =newRootWindowContainer(this);

-----------------------------------------------------------------------------------------------------------

一系列相关window的集合 通常,这是一个AppWindowToken(AppWindowToken就是activity),它是用于显示窗口的 Activity的句柄。 这个主要用于表示窗口的令牌(Token)信息,主要负责管理窗口的一些属性和行为,通过WindowToken,WMS可以对窗口进行布局,层级排序,焦点管理,输入事件分发等操作。classWindowTokenextendsWindowContainer<WindowState> {
此外ActivityRecord继承WindowToken,一个ActivityRecord代表一个activityfinalclassActivityRecordextendsWindowTokenimplementsWindowManagerService.AppFreezeListener {

-----------------------------------------------------------------------------------------------------------

WindowContainer作为窗口层级结构中的基本单元,负责承载一个窗口的所有信息和状态。每个窗口容器都对应一个窗口,并包含了窗口的配置信息、绘制信息、动画状态等。 WindowContainer还提供了与窗口相关的操作接口,如设置窗口属性、绘制窗口内容等。 在源码层面,WindowContainer的实现通常涉及到多个类和接口。 例如,WindowContainer类可能包含了一些与窗口容器相关的属性和方法,如窗口的位置、大小、背景色等。此外,WindowContainer还可能与其他组件进行交互,如与SurfaceFlinger进行渲染交互,与InputManager进行输入事件处理等。classWindowContainer<EextendsWindowContainer>extendsConfigurationContainer<E>implementsComparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable, InsetsControlTarget { ... 一系列子级window container集合。此集合按 z 顺序排列,因为子项显示在屏幕上,最上面的window container位于列表的尾部。 mChildren.add和mChildren.remove操作都在WindowContainer.java中protectedfinalWindowList<E> mChildren =newWindowList<E>();
WindowList介绍如下:一个 ArrayList,存储WindowContainer中的子window container classWindowList<E>extendsArrayList<E> { 添加到集合首位voidaddFirst(E e) { add(0, e); } 选择最后一位 E peekLast() {returnsize() > 0 ? get(size() - 1) : null; } 选择第一位 E peekFirst() {returnsize() > 0 ? get(0) : null; } }

-----------------------------------------------------------------------------------------------------------

包含具有重写配置并按层次结构组织的类的通用逻辑。publicabstractclassConfigurationContainer<EextendsConfigurationContainer> {

回归正题,来看看tag1处mRoot.getWindowToken(token); 刚刚已经讲过mChildren是一系列子级window container集合,那我们通过binder对此集合遍历,找到binder对应的windowtoken,然后返回。

/** Returns the window token for the input binder if it exist in the system.*/ WindowToken getWindowToken(IBinder binder) {for(inti = mChildren.size() - 1; i >= 0; --i) {finalDisplayContent dc = mChildren.get(i);finalWindowToken wtoken = dc.getWindowToken(binder);if(wtoken != null) {returnwtoken; } }returnnull; }
用于跟踪特定 Display 的 WindowStates 和其他相关内容的 辅助类。 DisplayContent 用于管理屏幕,一块屏幕对应一个 DisplayContent 对象,虽然手机只有一个显示屏,但是可以创建多个 DisplayContent 对象classDisplayContentextendsRootDisplayAreaimplementsWindowManagerPolicy.DisplayContentInfo {

标志3:mWindowMap

mWindowMap保存了每个WindowState和客户端窗口的映射关系,客户端应用请求窗口操作时,通过mWindowMap查询到对应的WindowState

/** Mapping from an IWindow IBinder to the server's Window object. */finalHashMap<IBinder, WindowState> mWindowMap =newHashMap<>();

二:窗口类型检查

如果是子窗口类型 if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { 先找到它的父窗口,子窗口需要依附在父窗口上,如果父窗口为null,则返回ADD_BAD_SUBWINDOW_TOKEN 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; } 如果父窗口也是一个子窗口,也直接返回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; } } 如果窗口是保密类型,但displayContent不是保密类型,则返回ADD_PERMISSION_DENIED if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) { ProtoLog.w(WM_ERROR, "Attempted to add private presentation window to a non-private display. " + "Aborting."); return WindowManagerGlobal.ADD_PERMISSION_DENIED; } 如果窗口是保密类型,但displayContent的屏幕是公开演示显示器,返回ADD_INVALID_DISPLAY if (type == TYPE_PRESENTATION && !displayContent.getDisplay().isPublicPresentation()) { ProtoLog.w(WM_ERROR, "Attempted to add presentation window to a non-suitable display. " + "Aborting."); return WindowManagerGlobal.ADD_INVALID_DISPLAY; }

三:新建WindowToken

ActivityRecord activity = null; 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. 子窗口使用现有的父窗口令牌,因为它们与父窗口使用相同的令牌,因此我们可以对它们应用相同的策略。 根据客户端传来的token获取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. 如果这是一个子窗口,我们希望应用与父窗口类型相同的类型检查规则。 final int rootType = hasParent ? parentWindow.mAttrs.type : type; boolean addToastWindowRequiresToken = false; final IBinder windowContextToken = attrs.mWindowContextToken; if (token == null) {如果token为空 if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type, rootType, attrs.token, attrs.packageName)) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (hasParent) {如果有父级window // Use existing parent window token for child windows. token = parentWindow.mToken;则直接用父级token给所有的子windows } else if (mWindowContextListenerController.hasListener(windowContextToken)) { // Respect the window context token if the user provided it. 如果用户提供了窗口上下文令牌,则用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 { 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(); } 如果是APP类型窗口 } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { 通过token获取ActivityRecord 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; 窗口类型为starting window } else if (type == TYPE_APPLICATION_STARTING) { if (activity.mStartingWindow != null) { ProtoLog.w(WM_ERROR, "Attempted to add starting window to " + "token with already existing starting window"); return WindowManagerGlobal.ADD_DUPLICATE_ADD; } if (activity.mStartingData == null) { ProtoLog.w(WM_ERROR, "Attempted to add starting window to " + "token but already cleaned"); return WindowManagerGlobal.ADD_DUPLICATE_ADD; } } 窗口类型为input window } else if (rootType == TYPE_INPUT_METHOD) { if (token.windowType != TYPE_INPUT_METHOD) { ProtoLog.w(WM_ERROR, "Attempted to add input method window with bad token " + "%s. Aborting.", attrs.token); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } 窗口类型为voice window } else if (rootType == TYPE_VOICE_INTERACTION) { if (token.windowType != TYPE_VOICE_INTERACTION) { ProtoLog.w(WM_ERROR, "Attempted to add voice interaction window with bad token " + "%s. Aborting.", attrs.token); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } 窗口类型为壁纸 } else if (rootType == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with bad token " + "%s. Aborting.", attrs.token); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } 窗口类型为辅助功能 OVERLAY } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) { if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) { ProtoLog.w(WM_ERROR, "Attempted to add Accessibility overlay window with bad token " + "%s. Aborting.", attrs.token); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } 窗口类型为Toast类型 } 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) { ProtoLog.w(WM_ERROR, "Attempted to add a toast window with bad token " + "%s. Aborting.", attrs.token); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (type == TYPE_QS_DIALOG) { if (token.windowType != TYPE_QS_DIALOG) { ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with bad token " + "%s. Aborting.", attrs.token); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } 通过token获取到的ActivityRecord不为空 } else if (token.asActivityRecord() != null) { ProtoLog.w(WM_ERROR, "Non-null activity for system window of rootType=%d", 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). 将应用令牌用于其他系统类型是无效的; 相反,我们将为它创建一个新的令牌(就好像已经为令牌传入了 null 一样)。 attrs.token = null; 创建WindowToken 标志1 token = new WindowToken.Builder(this, client.asBinder(), type) .setDisplayContent(displayContent) .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow) .build(); }

标志1 WindowToken

通常,这是一个AppWindowToken(AppWindowToken就是activity),它是用于显示窗口的 Activity的句柄。
这个主要用于表示窗口的令牌(Token)信息,主要负责管理窗口的一些属性和行为,通过WindowToken,WMS可以对窗口进行布局,层级排序,焦点管理,输入事件分发等操作。

token = new WindowToken.Builder(this, client.asBinder(), type)
.setDisplayContent(displayContent)
.setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
.build(); //build()就能创建出来WindowToken对象

classWindowTokenextendsWindowContainer<WindowState> {protectedWindowToken(WindowManagerService service, IBinder _token,inttype,booleanpersistOnEmpty, DisplayContent dc,booleanownerCanManageAppTokens) {this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,false/* roundedCornerOverlay */,false/* fromClientToken */, null /* options */); }protectedWindowToken(WindowManagerService service, IBinder _token,inttype,booleanpersistOnEmpty, DisplayContent dc,booleanownerCanManageAppTokens,booleanroundedCornerOverlay,booleanfromClientToken, @Nullable Bundle options) {super(service); token = _token; windowType = type; mOptions = options; mPersistOnEmpty = persistOnEmpty; mOwnerCanManageAppTokens = ownerCanManageAppTokens; mRoundedCornerOverlay = roundedCornerOverlay; mFromClientToken = fromClientToken;if(dc != null) { dc.addWindowToken(token,this); } }---------------------------------------------staticclassBuilder {privatefinalWindowManagerService mService;privatefinalIBinder mToken; @WindowTypeprivatefinalintmType;privatebooleanmPersistOnEmpty;privateDisplayContent mDisplayContent;privatebooleanmOwnerCanManageAppTokens; @NullableprivateBundle mOptions; Builder(WindowManagerService service, IBinder token,inttype) { mService = service; mToken = token; mType = type; } /** Sets the {@linkDisplayContent} to be associated. */ Builder setDisplayContent(DisplayContent dc) { mDisplayContent = dc;returnthis; } /**@seeWindowToken#mOwnerCanManageAppTokens */ Builder setOwnerCanManageAppTokens(booleanownerCanManageAppTokens) { mOwnerCanManageAppTokens = ownerCanManageAppTokens;returnthis; } WindowToken build() {returnnewWindowToken(mService, mToken, mType, mPersistOnEmpty, mDisplayContent, mOwnerCanManageAppTokens, mRoundedCornerOverlay, mFromClientToken, mOptions); } }

四: 新建WindowState

创建WindowState final WindowState win = new WindowState(this, session, client, token, parentWindow, appOp[0], 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; } WindowState对应的DisplayContent为空则没有对应的屏幕去显示window,所以报错 if (win.getDisplayContent() == null) { ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed."); return WindowManagerGlobal.ADD_INVALID_DISPLAY; }

WindowState表示一个窗口的所有属性,一个WindowState对应一个窗口,借用一张图来表示,也就解释了为什么WindowToken是一系列Window的集合的容器了。

WindowToken--"Container of a set of related windows in the window manager"

/** A window in the window manager. */classWindowStateextendsWindowContainer<WindowState>implementsWindowManagerPolicy.WindowState, InsetsControlTarget, InputTarget {

五:adjustWindowParamsLw

获取DisplayPolicy final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); 调整窗口参数 displayPolicy.adjustWindowParamsLw(win, win.mAttrs); 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); if (res != ADD_OKAY) { return res; }
/**提供要显示的UI的基本行为和状态的策略。 * The policy that provides the basic behaviors and states of a display to show UI. */publicclassDisplayPolicy {

在DisplayPolicy实例化中有关于手势相关的,比如下拉状态栏,左滑返回这种。

DisplayPolicy还可以调整布局相关

adjustWindowParamsLw作用:根据客户端的布局参数调整布局。 允许策略执行某些操作,例如确保特定类型的窗口不能采用输入焦点。

public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs) { switch (attrs.type) { case TYPE_SYSTEM_OVERLAY: case TYPE_SECURE_SYSTEM_OVERLAY: 如果窗口类型是这个,则给窗口flag添加如下参数 // These types of windows can't receive input events. attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; break; case TYPE_WALLPAPER: 如果是壁纸类型,则设置让窗口总是可以扩展到刘海区域中 // Dreams and wallpapers don't have an app window token and can thus not be // letterboxed. Hence always let them extend under the cutout. attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; break; case TYPE_TOAST: ... attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; break; case TYPE_BASE_APPLICATION: ... break; } ... }

layoutInDisplayCutoutMode的默认值是LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT。

layoutInDisplayCutoutMode
Value含义
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT0这是默认行为,在竖屏模式下,内容会呈现到刘海区域中;但在横屏模式下,内容会显示黑边。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES1在竖屏模式和横屏模式下,内容都会呈现到刘海区域中
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER2内容从不呈现到刘海区域中
LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS3窗口总是可以扩展到刘海区域中

六:窗口ADD_OKAY后续流程

final boolean openInputChannels = (outInputChannel != null && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0); if (openInputChannels) { 打开input channel win.openInputChannel(outInputChannel); } ... // From now on, no exceptions or errors allowed! 能走到这里那就说明这个窗口可以添加,没有问题 res = ADD_OKAY; 请求创建一个BLAST (Buffer as LayerState)层的标志。 如果没有指定,客户端将接收一个BufferQueue层。 if (mUseBLAST) { res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST; } ... // void attach() { // if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken); // mSession.windowAddedLocked(); // } 将这个windowstate即这个窗口更新到Session中的 mNumWindow中 win.attach(); mWindowMap更新,前面有讲过mWindowMap作用 保存 IWindow IBinder和windowstate的匹配 //"Mapping from an IWindow IBinder to the server's Window object." mWindowMap.put(client.asBinder(), win); win.initAppOpsState(); 当前window对应的token添加windowstate win.mToken.addWindow(win); 策略更新window及其对应的窗口属性 displayPolicy.addWindowLw(win, attrs); displayPolicy.setDropInputModePolicy(win, win.mAttrs); if (type == TYPE_APPLICATION_STARTING && activity != null) { activityrecord添加starting window窗口 activity.attachStartingWindow(win); ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s", activity, win); } else if (type == TYPE_INPUT_METHOD // IME window is always touchable. // Ignore non-touchable windows e.g. Stylus InkWindow.java. && (win.getAttrs().flags & FLAG_NOT_TOUCHABLE) == 0) { 如果是输入法,且可触摸,则给这个display设置输入法 displayContent.setInputMethodWindowLocked(win); imMayMove = false; } else if (type == TYPE_INPUT_METHOD_DIALOG) { 如果是输入法dialog,则计算输入法目标 displayContent.computeImeTarget(true /* updateImeTarget */); imMayMove = false; ... 窗口可以接受输入事件 if (win.canReceiveKeys()) { 更新焦点窗口 focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, false /*updateInputWindows*/); if (focusChanged) { imMayMove = false; } } ... // Don't do layout here, the window must call // relayout to be displayed, so we'll do it there. win.getParent().assignChildLayers(); 窗口焦点更新了,所以当前输入焦点窗口也要重新设置 if (focusChanged) { displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus, false /*updateInputWindows*/); } 更新输入窗口 displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); ... // This window doesn't have a frame yet. Don't let this window cause the insets change. displayContent.getInsetsStateController().updateAboveInsetsState( false /* notifyInsetsChanged */); outInsetsState.set(win.getCompatInsetsState(), true /* copySources */); getInsetsSourceControls(win, outActiveControls); ... return res; }
http://www.jsqmd.com/news/1405118/

相关文章:

  • 看门狗定时器(WDT)的实用技巧
  • Havennlon | 杂谈:AI补出来的,是表达完整性,不是认知完整性
  • 智能堆高机器人:窄通道、集群调度与无人化物流落地实践
  • 亲属关系公证双认证怎么办理?材料有哪些?科普
  • 2026 八月最新廊坊管道维修马桶疏通冻堵处置暗管测漏异物打捞商家汇总 - 启航优客
  • 2026年8月黄山外墙漏水维修靠谱渠道盘点,高层高空渗水修缮避坑指南 - 聪居到家
  • llms-txt-hub安全架构揭秘:CSRF防护、速率限制与Google Web Risk三重防线
  • Android14 WMS-IWindow介绍
  • 电脑搜索应用找不到如何解决
  • 2026年隐形拉链厂家怎么选?一份行业观察与选型指南 - 品牌报告
  • Genesis WordPress缓存优化终极指南:Varnish高性能缓存配置与WordPress命中率提升技巧
  • 7-Zip-zstd压缩工具完全指南:一步到位让7-Zip用上6大现代压缩算法
  • 抖音批量下载工具完全上手手册:5 步用 douyin-downloader 建起无水印视频内容库
  • AXPhotoViewer 网络集成详解:SDWebImage、Kingfisher、Nuke 等 6 大方案对比与选型
  • 微信聊天记录导出终极指南:如何让每一段对话永久留痕?
  • FastCSV 读取 CSV 的 3 种记录类型:CsvRecord、NamedCsvRecord 与 StringArray,一篇搞懂怎么选
  • MobaXterm-keygen 使用指南:三步生成 Custom.mxtpro 注册文件,解锁专业版全部功能
  • 2026年贵阳人工养殖黄鳝基地 解决选场困惑 五家专业参考 - 滚动商讯
  • 温州小程序App制作公司怎么选?2026年十家高口碑服务商评测与实力解析 - 软件测评师
  • 新手养宠避坑指南,线下场地积累真实养犬认知 - 北京同城宠物基地
  • MAC地址每台设备的身份证号
  • 抖音无水印批量下载器完整实战指南
  • 基于SpringBoot的同人创作与分享平台系统(毕业设计项目源码+文档)
  • QSYM 核心架构全解析:从 PIN 插桩到 Z3 求解
  • SmartBugs 跨平台安装终极指南:Windows/macOS/Linux 避坑全攻略
  • 5步搭好免费自托管游戏串流服务:Sunshine 从安装到畅玩全指南
  • SCOOP多机部署实战:用SSH与hostfile构建分布式集群
  • iPhone远程控制安卓手机:Scrcpy-iOS跨平台远程控制从零上手
  • 2026单轴转台选购指南:精度、传动与厂家盘点
  • MiniMax Music 3 环境搭建教程:SGLang-Omni 部署与 API 调用从零开始