WindowManagerService的addWindow方法源码解读

2024-03-01 00:28

本文主要是介绍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 方法是用于获取一个窗口的插图源控制器的私有方法,它根据窗口的显示内容和插图状态,得到插图源控制器的数组,然后复制并设置可序列化的标志,然后返回给输出参数。

这篇关于WindowManagerService的addWindow方法源码解读的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/760589

相关文章

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

浅谈主机加固,六种有效的主机加固方法

在数字化时代,数据的价值不言而喻,但随之而来的安全威胁也日益严峻。从勒索病毒到内部泄露,企业的数据安全面临着前所未有的挑战。为了应对这些挑战,一种全新的主机加固解决方案应运而生。 MCK主机加固解决方案,采用先进的安全容器中间件技术,构建起一套内核级的纵深立体防护体系。这一体系突破了传统安全防护的局限,即使在管理员权限被恶意利用的情况下,也能确保服务器的安全稳定运行。 普适主机加固措施:

webm怎么转换成mp4?这几种方法超多人在用!

webm怎么转换成mp4?WebM作为一种新兴的视频编码格式,近年来逐渐进入大众视野,其背后承载着诸多优势,但同时也伴随着不容忽视的局限性,首要挑战在于其兼容性边界,尽管WebM已广泛适应于众多网站与软件平台,但在特定应用环境或老旧设备上,其兼容难题依旧凸显,为用户体验带来不便,再者,WebM格式的非普适性也体现在编辑流程上,由于它并非行业内的通用标准,编辑过程中可能会遭遇格式不兼容的障碍,导致操

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

MCU7.keil中build产生的hex文件解读

1.hex文件大致解读 闲来无事,查看了MCU6.用keil新建项目的hex文件 用FlexHex打开 给我的第一印象是:经过软件的解释之后,发现这些数据排列地十分整齐 :02000F0080FE71:03000000020003F8:0C000300787FE4F6D8FD75810702000F3D:00000001FF 把解释后的数据当作十六进制来观察 1.每一行数据

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

【北交大信息所AI-Max2】使用方法

BJTU信息所集群AI_MAX2使用方法 使用的前提是预约到相应的算力卡,拥有登录权限的账号密码,一般为导师组共用一个。 有浏览器、ssh工具就可以。 1.新建集群Terminal 浏览器登陆10.126.62.75 (如果是1集群把75改成66) 交互式开发 执行器选Terminal 密码随便设一个(需记住) 工作空间:私有数据、全部文件 加速器选GeForce_RTX_2080_Ti

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、