WindowManagerService的addWindow方法源码解读

源码链接
https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java;l=9516?q=sanitizeWindowType&hl=zh-cn
addWindow 方法如下:

    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);if (res != ADD_OKAY) {return res;}WindowState parentWindow = null;final int callingUid = Binder.getCallingUid();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");}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;}if (mWindowMap.containsKey(client.asBinder())) {ProtoLog.w(WM_ERROR, "Window %s is already added", client);return WindowManagerGlobal.ADD_DUPLICATE_ADD;}if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {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;}}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;}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;}int userId = UserHandle.getUserId(session.mUid);if (requestUserId != userId) {try {mAmInternal.handleIncomingUser(callingPid, callingUid, requestUserId,false /*allowAll*/, ALLOW_NON_FULL, null, null);} catch (Exception exp) {ProtoLog.w(WM_ERROR, "Trying to add window with invalid user=%d",requestUserId);return WindowManagerGlobal.ADD_INVALID_USER;}// It's fine to use this userIduserId = requestUserId;}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.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) {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 (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) {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;}}} 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;}} 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;}} 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;}} 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;}} 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).attrs.token = null;token = new WindowToken.Builder(this, client.asBinder(), type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).build();}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;}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();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;}final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if  (openInputChannels) {win.openInputChannel(outInputChannel);}// If adding a toast requires a token for this app we always schedule hiding// toast windows to make sure they don't stick around longer then necessary.// We hide instead of remove such windows as apps aren't prepared to handle// windows being removed under them.//// If the app is older it can add toasts without a token and hence overlay// other apps. To be maximally compatible with these apps we will hide the// window after the toast timeout only if the focused window is from another// UID, otherwise we allow unlimited duration. When a UID looses focus we// schedule hiding all of its toast windows.if (type == TYPE_TOAST) {if (!displayContent.canAddToastWindowForUid(callingUid)) {ProtoLog.w(WM_ERROR, "Adding more than one toast window for UID at a time.");return WindowManagerGlobal.ADD_DUPLICATE_ADD;}// Make sure this happens before we moved focus as one can make the// toast focusable to force it not being hidden after the timeout.// Focusable toasts are always timed out to prevent a focused app to// show a focusable toasts while it has focus which will be kept on// the screen after the activity goes away.if (addToastWindowRequiresToken|| (attrs.flags & FLAG_NOT_FOCUSABLE) == 0|| displayContent.mCurrentFocus == null|| displayContent.mCurrentFocus.mOwnerUid != callingUid) {mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),win.mAttrs.hideTimeoutMilliseconds);}}// Switch to listen to the {@link WindowToken token}'s configuration changes when// adding a window to the window context. Filter sub window type here because the sub// window must be attached to the parent window, which is attached to the window context// created window token.if (!win.isChildWindow()&& mWindowContextListenerController.hasListener(windowContextToken)) {final int windowContextType = mWindowContextListenerController.getWindowType(windowContextToken);final Bundle options = mWindowContextListenerController.getOptions(windowContextToken);if (type != windowContextType) {ProtoLog.w(WM_ERROR, "Window types in WindowContext and"+ " LayoutParams.type should match! Type from LayoutParams is %d,"+ " but type from WindowContext is %d", type, windowContextType);// We allow WindowProviderService to add window other than windowContextType,// but the WindowProviderService won't be associated with the window's// WindowToken.if (!isWindowProviderService(options)) {return WindowManagerGlobal.ADD_INVALID_TYPE;}} else {mWindowContextListenerController.registerWindowContainerListener(windowContextToken, token, callingUid, type, options);}}// From now on, no exceptions or errors allowed!res = ADD_OKAY;if (mUseBLAST) {res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;}if (displayContent.mCurrentFocus == null) {displayContent.mWinAddedSinceNullFocus.add(win);}if (excludeWindowTypeFromTapOutTask(type)) {displayContent.mTapExcludedWindows.add(win);}win.attach();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);boolean imMayMove = true;win.mToken.addWindow(win);displayPolicy.addWindowLw(win, attrs);displayPolicy.setDropInputModePolicy(win, win.mAttrs);if (type == TYPE_APPLICATION_STARTING && activity != null) {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) {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 (win.hasWallpaper()) {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;}}final WindowStateAnimator winAnimator = win.mWinAnimator;winAnimator.mEnterAnimationPending = true;winAnimator.mEnteringAnimation = true;if (displayPolicy.areSystemBarsForcedConsumedLw()) {res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;}if (displayContent.isInTouchMode()) {res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;}if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;}displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();boolean focusChanged = false;if (win.canReceiveKeys()) {focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,false /*updateInputWindows*/);if (focusChanged) {imMayMove = false;}}if (imMayMove) {displayContent.computeImeTarget(true /* updateImeTarget */);if (win.isImeOverlayLayeringTarget()) {dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type,win.isVisibleRequestedOrAdding(), false /* removed */);}}// 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*/);ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"+ ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));boolean needToSendNewConfiguration =win.isVisibleRequestedOrAdding() && displayContent.updateOrientation();if (win.providesDisplayDecorInsets()) {needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo();}if (needToSendNewConfiguration) {displayContent.sendNewConfiguration();}// 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);if (win.mLayoutAttached) {outAttachedFrame.set(win.getParentWindow().getFrame());if (win.mInvGlobalScale != 1f) {outAttachedFrame.scale(win.mInvGlobalScale);}} else {// Make this invalid which indicates a null attached frame.outAttachedFrame.set(0, 0, -1, -1);}outSizeCompatScale[0] = win.getCompatScaleForClient();}Binder.restoreCallingIdentity(origId);return res;}

addWindow 是 WindowManagerService 类中的一个公开方法,它的作用是将一个窗口添加到窗口管理系统中,以便在显示器上显示和管理。这个方法的逻辑大致如下:

  • 参数:这个方法接收十五个参数,分别是:
    • session:一个 Session 类型的对象,表示调用者的会话。
    • client:一个 IWindow 类型的对象,表示要添加的窗口的接口。
    • seq:一个 int 类型的值,表示要添加的窗口的序列号。
    • attrs:一个 WindowManager.LayoutParams 类型的对象,表示要添加的窗口的布局参数。
    • viewVisibility:一个 int 类型的值,表示要添加的窗口的可见性。
    • displayId:一个 int 类型的值,表示要添加的窗口所在的显示器的 ID。
    • outContentInsets:一个 Rect 类型的对象,表示一个输出参数,用于返回要添加的窗口的内容区域的内边距。
    • outStableInsets:一个 Rect 类型的对象,表示一个输出参数,用于返回要添加的窗口的稳定区域的内边距。
    • outOutsets:一个 Rect 类型的对象,表示一个输出参数,用于返回要添加的窗口的外边距。
    • outInputChannel:一个 InputChannel 类型的对象,表示一个输出参数,用于返回要添加的窗口的输入通道。
  • 返回值:这个方法返回一个 int 类型的值,表示添加窗口的结果,可能的值有:
    • ADD_OKAY:表示添加窗口成功。
    • ADD_BAD_APP_TOKEN:表示添加窗口失败,因为应用窗口的令牌无效。
    • ADD_BAD_SUBWINDOW_TOKEN:表示添加窗口失败,因为子窗口的令牌无效。
    • ADD_DUPLICATE_ADD:表示添加窗口失败,因为窗口已经存在。
    • ADD_STARTING_NOT_NEEDED:表示添加窗口失败,因为启动窗口不需要。
    • ADD_MULTIPLE_SINGLETON:表示添加窗口失败,因为单例窗口重复。
    • ADD_PERMISSION_DENIED:表示添加窗口失败,因为权限不足。
    • ADD_INVALID_DISPLAY:表示添加窗口失败,因为显示器不存在。
    • ADD_INVALID_TYPE:表示添加窗口失败,因为窗口类型不合法。
  • 逻辑:这个方法的逻辑是:
    • 首先,检查调用者的权限,如果没有 SYSTEM_ALERT_WINDOW 权限,且窗口类型是系统警告窗口,那么返回 ADD_PERMISSION_DENIED。
    • 然后,检查调用者的应用操作,如果有应用操作,且应用操作被拒绝,那么返回 ADD_PERMISSION_DENIED。
    • 接着,检查窗口类型,如果窗口类型是 TYPE_APPLICATION_STARTING,且不需要显示启动窗口,那么返回 ADD_STARTING_NOT_NEEDED。
    • 然后,检查显示器,如果显示器不存在,那么返回 ADD_INVALID_DISPLAY。
    • 接着,检查窗口,如果窗口已经存在,那么返回 ADD_DUPLICATE_ADD。
    • 然后,检查窗口令牌,如果窗口令牌无效,那么返回 ADD_BAD_APP_TOKEN 或 ADD_BAD_SUBWINDOW_TOKEN。
    • 接着,检查窗口类型,如果窗口类型不合法,那么返回 ADD_INVALID_TYPE。
    • 然后,创建一个 WindowState 对象,表示要添加的窗口的状态,这个对象的构造方法接收十六个参数,分别是 this, session, client, windowToken, parentWindow, appOp[0], seq, attrs, viewVisibility, displayContent, mPolicy, mService, mAnimator, mRoot, mInputManager, mAccessibilityController。
    • 接着,调用 mPolicy 的 prepareAddWindowLw 方法,将 win 和 attrs 作为参数传入,这个方法用于在添加窗口之前,根据策略调整窗口的布局参数。
    • 然后,调用 mPolicy 的 addWindowLw 方法,将 win 作为参数传入,这个方法用于在添加窗口时,根据策略返回窗口的层级。
    • 接着,调用 win 的 attach 方法,将 windowToken, mDisplayManagerInternal, mInputManager 作为参数传入,这个方法用于将窗口附加到窗口令牌和显示管理器和输入管理器上。
    • 然后,调用 win 的 computeFrameLw 方法,这个方法用于计算窗口的框架区域和内容区域和稳定区域和外边距等。
    • 接着,将 win 的内容区域的内边距和稳定区域的内边距和外边距分别赋值给 outContentInsets, outStableInsets, outOutsets 参数,用于返回给调用者。
    • 然后,调用 win 的 openInputChannel 方法,将 outInputChannel 作为参数传入,这个方法用于打开窗口的输入通道,并返回给调用者。
    • 接着,调用 mInputManager 的 addInputWindowHandle 方法,将 win 的输入窗口句柄作为参数传入,这个方法用于将窗口的输入窗口句柄添加到输入管理器中。
    • 然后,调用 mPolicy 的 getContentInsetHintLw 方法,将 win 的布局参数和窗口类型和窗口的内容区域的宽度和高度作为参数传入,这个方法用于根据策略返回窗口的内容区域的内边距的提示。
    • 接着,调用 mRoot 的 addWindow 方法,将 win 作为参数传入,这个方法用于将窗口添加到窗口树中,并更新窗口的可见性和焦点和动画等。
    • 最后,返回 ADD_OKAY,表示添加窗口成功。

总结:addWindow 方法是用于将一个窗口添加到窗口管理系统中的公开方法,它接收十五个参数,返回一个 int 类型的值,表示添加窗口的结果,它的逻辑是先检查调用者的权限和应用操作和窗口类型和显示器和窗口令牌等,然后创建一个 WindowState 对象,表示要添加的窗口的状态,然后根据策略调整和返回窗口的布局参数和层级,然后将窗口附加到窗口令牌和显示管理器和输入管理器上,然后计算和返回窗口的区域和边距,然后打开和返回窗口的输入通道,然后将窗口的输入窗口句柄添加到输入管理器中,然后返回窗口的内容区域的内边距的提示,然后将窗口添加到窗口树中,并更新窗口的相关状态。

private boolean unprivilegedAppCanCreateTokenWith(WindowState parentWindow,int callingUid, int type, int rootType, IBinder tokenForLog, String packageName) {if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {ProtoLog.w(WM_ERROR, "Attempted to add application window with unknown token "+ "%s.  Aborting.", tokenForLog);return false;}if (rootType == TYPE_INPUT_METHOD) {ProtoLog.w(WM_ERROR, "Attempted to add input method window with unknown token "+ "%s.  Aborting.", tokenForLog);return false;}if (rootType == TYPE_VOICE_INTERACTION) {ProtoLog.w(WM_ERROR,"Attempted to add voice interaction window with unknown token "+ "%s.  Aborting.", tokenForLog);return false;}if (rootType == TYPE_WALLPAPER) {ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with unknown token "+ "%s.  Aborting.", tokenForLog);return false;}if (rootType == TYPE_QS_DIALOG) {ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token "+ "%s.  Aborting.", tokenForLog);return false;}if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {ProtoLog.w(WM_ERROR,"Attempted to add Accessibility overlay window with unknown token "+ "%s.  Aborting.", tokenForLog);return false;}if (type == TYPE_TOAST) {// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.if (doesAddToastWindowRequireToken(packageName, callingUid, parentWindow)) {ProtoLog.w(WM_ERROR, "Attempted to add a toast window with unknown token "+ "%s.  Aborting.", tokenForLog);return false;}}return true;}

unprivilegedAppCanCreateTokenWith,它的作用是判断一个没有特权的应用是否可以使用指定的窗口类型和窗口令牌创建一个窗口。这个方法的逻辑大致如下:

  • 参数:这个方法接收六个参数,分别是:
    • parentWindow:一个 WindowState 类型的对象,表示要创建的窗口的父窗口。
    • callingUid:一个 int 类型的值,表示调用者的用户 ID。
    • type:一个 int 类型的值,表示要创建的窗口的类型。
    • rootType:一个 int 类型的值,表示要创建的窗口的根类型,即窗口令牌对应的窗口类型。
    • tokenForLog:一个 IBinder 类型的对象,表示要创建的窗口的令牌,用于打印日志。
    • packageName:一个 String 类型的对象,表示调用者的包名。
  • 返回值:这个方法返回一个 boolean 类型的值,表示是否可以创建窗口。
  • 逻辑:这个方法的逻辑是:
    • 首先,检查根类型,如果根类型是应用窗口的范围,那么打印一条错误日志,表示尝试使用未知的令牌添加应用窗口,并返回 false。
    • 然后,检查根类型,如果根类型是以下几种特殊的窗口类型,那么打印一条错误日志,表示尝试使用未知的令牌添加这种类型的窗口,并返回 false。这些窗口类型分别是:
      • TYPE_INPUT_METHOD:输入法窗口,用于显示输入法的界面。
      • TYPE_VOICE_INTERACTION:语音交互窗口,用于显示语音助手的界面。
      • TYPE_WALLPAPER:壁纸窗口,用于显示桌面壁纸。
      • TYPE_QS_DIALOG:快速设置对话框窗口,用于显示快速设置的面板。
      • TYPE_ACCESSIBILITY_OVERLAY:辅助功能覆盖窗口,用于显示辅助功能的界面。
    • 接着,检查窗口类型,如果窗口类型是 TYPE_TOAST,即吐司窗口,用于显示短暂的提示信息,那么还需要检查调用者的包名和用户 ID 和父窗口,是否需要令牌才能添加吐司窗口,如果需要,那么打印一条错误日志,表示尝试使用未知的令牌添加吐司窗口,并返回 false。
    • 最后,如果没有遇到以上的情况,那么返回 true,表示可以创建窗口。

总结:unprivilegedAppCanCreateTokenWith 方法是用于判断一个没有特权的应用是否可以创建窗口的私有方法,它根据窗口类型和窗口令牌和调用者的信息进行判断,如果不符合条件,就打印错误日志并返回 false,否则返回 true。

 boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);return changed;}

updateFocusedWindowLocked,它的作用是更新系统中的焦点窗口,即当前正在与用户交互的窗口。这个方法的逻辑大致如下:

  • 参数:这个方法接收两个参数,分别是:
    • mode:一个 int 类型的值,表示更新焦点窗口时所处的阶段,共有五个值,分别表示正常更新、在分配窗口层级之前、在放置表面过程中、在放置表面之前、在移除焦点窗口后。
    • updateInputWindows:一个 boolean 类型的值,表示是否同步更新输入窗口,即能接收输入事件的窗口。
  • 返回值:这个方法返回一个 boolean 类型的值,表示焦点窗口是否发生了变化。
  • 逻辑:这个方法的逻辑是:
    • 首先,调用 Trace 的 traceBegin 方法,将 TRACE_TAG_WINDOW_MANAGER 和 “wmUpdateFocus” 作为参数传入,这个方法用于开始追踪一个事件,用于性能分析¹。
    • 然后,调用 mRoot 的 updateFocusedWindowLocked 方法,将 mode 和 updateInputWindows 作为参数传入,这个方法用于更新根窗口容器中的焦点窗口,并返回是否发生了变化,将结果赋值给 changed 变量。
    • 接着,调用 Trace 的 traceEnd 方法,将 TRACE_TAG_WINDOW_MANAGER 作为参数传入,这个方法用于结束追踪一个事件,用于性能分析¹。
    • 最后,返回 changed 变量,表示焦点窗口是否发生了变化。

总结:updateFocusedWindowLocked 方法是用于更新系统中的焦点窗口的公开方法,它根据不同的阶段和是否同步更新输入窗口,调用根窗口容器的方法,然后返回焦点窗口是否发生了变化,并且在开始和结束时进行性能追踪。

    void dispatchImeTargetOverlayVisibilityChanged(@NonNull IBinder token,@WindowManager.LayoutParams.WindowType int windowType, boolean visible,boolean removed) {if (mImeTargetChangeListener != null) {if (DEBUG_INPUT_METHOD) {Slog.d(TAG, "onImeTargetOverlayVisibilityChanged, win=" + mWindowMap.get(token)+ ", type=" + ViewDebug.intToString(WindowManager.LayoutParams.class,"type", windowType) + "visible=" + visible + ", removed=" + removed);}mH.post(() -> mImeTargetChangeListener.onImeTargetOverlayVisibilityChanged(token,windowType, visible, removed));}}

dispatchImeTargetOverlayVisibilityChanged,它的作用是分发输入法目标覆盖层可见性变化的事件,即当一个窗口在输入法窗口上方显示或隐藏时,通知输入法目标变化的监听器。这个方法的逻辑大致如下:

  • 参数:这个方法接收四个参数,分别是:
    • token:一个 IBinder 类型的对象,表示输入法目标覆盖层窗口的令牌。
    • windowType:一个 int 类型的值,表示输入法目标覆盖层窗口的类型。
    • visible:一个 boolean 类型的值,表示输入法目标覆盖层窗口是否可见。
    • removed:一个 boolean 类型的值,表示输入法目标覆盖层窗口是否被移除。
  • 逻辑:这个方法的逻辑是:
    • 首先,检查 mImeTargetChangeListener 变量是否为空,这是一个 OnImeTargetChangeListener 类型的变量,用于存储输入法目标变化的监听器。
    • 然后,如果不为空,那么执行以下操作:
      • 如果 DEBUG_INPUT_METHOD 常量为 true,这是一个 boolean 类型的常量,用于控制是否打印输入法相关的调试日志,那么调用 Slog 的 d 方法,将 TAG, “onImeTargetOverlayVisibilityChanged, win=” + mWindowMap.get(token) + “, type=” + ViewDebug.intToString(WindowManager.LayoutParams.class, “type”, windowType) + “visible=” + visible + “, removed=” + removed 作为参数传入,这个方法用于打印一条调试级别的日志,显示输入法目标覆盖层窗口的信息。
      • 然后,调用 mH 的 post 方法,将一个 lambda 表达式作为参数传入,这个方法用于在主线程上执行一个任务。
      • 最后,这个 lambda 表达式的操作是调用 mImeTargetChangeListener 的 onImeTargetOverlayVisibilityChanged 方法,将 token, windowType, visible, removed 作为参数传入,这个方法用于通知输入法目标变化的监听器,输入法目标覆盖层窗口的可见性发生了变化。

总结:dispatchImeTargetOverlayVisibilityChanged 方法是用于分发输入法目标覆盖层可见性变化的事件的公开方法,它根据输入法目标覆盖层窗口的令牌、类型、可见性和移除状态,通知输入法目标变化的监听器,并且在调试模式下打印相关的日志。

private void getInsetsSourceControls(WindowState win, InsetsSourceControl.Array outArray) {final InsetsSourceControl[] controls =win.getDisplayContent().getInsetsStateController().getControlsForDispatch(win);if (controls != null) {final int length = controls.length;final InsetsSourceControl[] outControls = new InsetsSourceControl[length];for (int i = 0; i < length; i++) {// We will leave the critical section before returning the leash to the client,// so we need to copy the leash to prevent others release the one that we are// about to return.if (controls[i] != null) {// This source control is an extra copy if the client is not local. By setting// PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of// SurfaceControl.writeToParcel.outControls[i] = new InsetsSourceControl(controls[i]);outControls[i].setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE);}}outArray.set(outControls);}}

getInsetsSourceControls,它的作用是获取一个窗口的插图源控制器,即用于控制窗口的插图区域的可见性和行为的对象。这个方法的逻辑大致如下:

  • 参数:这个方法接收两个参数,分别是:
    • win:一个 WindowState 类型的对象,表示要获取插图源控制器的窗口。
    • outArray:一个 InsetsSourceControl.Array 类型的对象,表示一个输出参数,用于返回插图源控制器的数组。
  • 逻辑:这个方法的逻辑是:
    • 首先,调用 win 的 getDisplayContent 方法,得到窗口所在的显示内容(DisplayContent),然后调用它的 getInsetsStateController 方法,得到它的插图状态控制器(InsetsStateController),然后调用它的 getControlsForDispatch 方法,将 win 作为参数传入,得到它的插图源控制器的数组(InsetsSourceControl[]),并赋值给 controls 变量。
    • 然后,检查 controls 变量是否为空,如果不为空,那么执行以下操作:
      • 声明一个 int 类型的变量,用于存储 controls 数组的长度,并赋值为 controls.length。
      • 声明一个 InsetsSourceControl 类型的数组,用于存储输出的插图源控制器,并初始化为和 controls 数组一样的长度。
      • 遍历 controls 数组的每个元素,执行以下操作:
        • 如果当前元素不为空,那么执行以下操作:
          • 创建一个新的 InsetsSourceControl 对象,用于复制当前元素,并赋值给输出数组的对应位置。
          • 调用输出数组的当前元素的 setParcelableFlags 方法,将 PARCELABLE_WRITE_RETURN_VALUE 作为参数传入,这个方法用于设置可序列化的标志,表示在 SurfaceControl.writeToParcel 方法结束时,释放当前元素的绳索(leash),即用于控制表面的对象。
      • 调用 outArray 的 set 方法,将输出数组作为参数传入,这个方法用于将输出数组赋值给 outArray 对象。

总结:getInsetsSourceControls 方法是用于获取一个窗口的插图源控制器的私有方法,它根据窗口的显示内容和插图状态,得到插图源控制器的数组,然后复制并设置可序列化的标志,然后返回给输出参数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/709779.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

31-树-找树左下角的值

这是树的第31篇算法&#xff0c;力扣链接。 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1 拿到这道题的第一想法&#xff0c;我可以层级遍历选取最左节点。 func f…

R语言数据可视化之美专业图表绘制指南(增强版):第1章 R语言编程与绘图基础

第1章 R语言编程与绘图基础 目录 第1章 R语言编程与绘图基础前言1.1 学术图表的基本概念1.1.1 学术图表的基本作用1.1.2基本类别1.1.3 学术图表的绘制原则 1.2 你为什么要选择R1.3 安装 前言 这是我第一次在博客里展示学习中国作者的教材的笔记。我选择这本书的依据是作者同时…

团体程序设计天梯赛 L2-002 链表去重

L2-002 链表去重 分数 25 给定一个带整数键值的链表 L&#xff0c;你需要把其中绝对值重复的键值结点删掉。即对每个键值 K&#xff0c;只有第一个绝对值等于 K 的结点被保留。同时&#xff0c;所有被删除的结点须被保存在另一个链表上。例如给定 L 为 21→-15→-15→-7→15&…

C#学习:初识各类应用程序

编写我们第一个程序——Hello,World! 1.编程不是“学”出来的&#xff0c;而是“练”出来的 2.在反复应用中积累&#xff0c;忽然有一天就会顿悟 3.学习原则&#xff1a; 3.1从感官到原理 3.2从使用别人的到创建自己的 3.3必需亲自动手 3.4必需学以致用&#xff0c;紧跟实际…

计算机网络原理--传输层

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f4d5;格言&#xff1a;那些在暗处执拗生长的花&#xff0c;终有一日会馥郁传香欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 TCP/IP五层&#xff08;或四层&#xff09;模型 传输层 TCP和UDP的区别 UDP协议 校验和 如何…

c#压缩文件函数

/// <summary> /// 压缩文件 /// </summary> /// <param name"sourceFile">要压缩的文件名</param> /// <param name"destFile">压缩后的文件名</param> …

ORACLE EBS 润年日期漏入数据库处理

做之前记得做好备份表数据&#xff01; --1、 出错数据日期为&#xff1a;schedule_close_date 2024/2/28 Select * From inv.org_acct_periods Where period_set_name NIPRC_CALENDAR --根据自己的实际命名 And period_name FEB-24; --修正为&#xff1a;schedule_cl…

【Web】get请求和post请求的区别

get请求和post请求的区别 get请求请求参数在请求行中&#xff08;链接后面直接显式说明&#xff09;post请求请求参数在请求体中&#xff0c;通常以JSON形式传送 get请求可以直接看见明文post请求一般有加密 get请求的大小有限制post请求无限制 企业开发中&#xff0c;get经常被…

30天自制操作系统(第20天)

20.1 显示单个字符的API&#xff08;1&#xff09; 需要实现&#xff1a;当在命令窗口输入 ‘hlt’ 时&#xff0c;打印一个字符。思考一下&#xff0c;需要在执行hlt.nas文件时打印的字符&#xff0c;所以需要在文件中规定要打印的字符。打印字符需要调用cons_putchar函数&…

postcss-px-to-viewport include属性

包含include配置的(github)&#xff1a;npm i https://github.com/evrone/postcss-px-to-viewport -S 包含include配置的(npm)&#xff1a;npm i postcss-px-to-viewport-8-with-include -S 不包含包include配置的(npm)&#xff1a;npm i postcss-px-to-viewport 看了一下这篇文…

Linux 基础之 vmstat 命令详解

文章目录 一、前言二、使用说明2.1 vmstat [delay/count/d/D/t/w]2.2.vm模式的字段 一、前言 vmstat(VirtualMeomoryStatistics&#xff0c;虚拟内存统计)是一个不错的 Linux/Unix 监控工具&#xff0c;在性能测试中除了top外也是比较常用的工具之一&#xff0c;它可以监控操作…

同局域网共享虚拟机(VMware)

一、前言 首先我们先来了解下 VMware 的三种网络模式桥接模式、NAT模式、仅主机模式&#xff0c;网络类型介绍详情可以参考下我之前的文档 Linux系统虚拟机安装&#xff08;上&#xff09;第三章 - 第9步指定网络类型。了解三种网络模式的原理之后&#xff0c;再来剖析下需求&…

Python爬虫——Urllib库-上

这几天都在为了蓝桥杯做准备&#xff0c;一直在刷算法题&#xff0c;确实刷算法题的过程是及其的枯燥且枯燥的。于是我还是决定给自己找点成就感出来&#xff0c;那么Python的爬虫就这样开始学习了。 注&#xff1a;文章源于观看尚硅谷爬虫视频后笔记 目录 Urllib库 基本使…

自定义View中的ListView和ScrollView嵌套的问题

当我们在使用到ScrollView和ListView的时候可能会出现显示不全的问题。那我们可以进行以下分析 ScrollView在测量子布局的时候会用UNSPECIFIED。通过源码观察&#xff0c; 在ScrollView的onMeasure方法中 Overrideprotected void onMeasure(int widthMeasureSpec, int heightMe…

MySQL数据库安装与配置全攻略

MySQL安装配置教程 MySQL是一种流行的开源数据库管理系统&#xff0c;广泛用于网站和服务器应用程序中存储数据。本教程将引导你完成在Windows系统上安装和配置MySQL数据库的步骤。 一、下载MySQL安装包 访问MySQL官方网站&#xff1a;MySQL Downloads选择"MySQL Commu…

MySQL进阶:大厂高频面试——各类SQL语句性能调优经验

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;MySQL进阶&#xff1a;强推&#xff0c;冲大厂必精通&#xff01;MySQL索引底层&#xff08;BTree&#xff09;、性能分析、使用…

HTTP笔记(五)

个人学习笔记&#xff08;整理不易&#xff0c;有帮助点个赞&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 目录 一&#xff1a;HTTP报文首部 &#xff08;1&#xff09;HTTP请求报文 &#xff08;2&#xff09…

WPF MVVM中List<>和ObservableCollection<>的区别与对比分析

在WPF MVVM&#xff08;模型-视图-视图模型&#xff09;架构中&#xff0c;数据绑定是实现UI与后端逻辑分离的关键特性。为了使UI能够响应后端数据的变化&#xff0c;通常需要用到特定的集合类型。在WPF中&#xff0c;最常见的两种集合类型是List< T>和ObservableCollect…

Kaggle 竞赛入门

打比赛不用写算法源码&#xff0c;应用的时候不用自己写。学习的时候可以自己写。 Kaggle 竞赛入门 认识 Kaggle 平台Kaggle竞赛知识前提结构化数据前提图像数据文本数据 Kaggle竞赛套路一个赛题的完整流程 认识 Kaggle 平台 Kaggle 官网 主页&#xff0c;比赛&#xff08;数据…

复现nerfstudio并训练自己制作的数据集

网站&#xff1a;安装 - nerfstudio GitHub - nerfstudio-project/nerfstudio&#xff1a;NeRF 的协作友好工作室 安装之前要确保电脑上已经有CUDA11.8或以上版本&#xff08;更高版本的可以安装11.8的toolkit&#xff09; 创建环境 conda create --name nerfstudio -y pyt…