当前位置: 首页 > 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后续流程


整个流程如下

bd23232595b941c08f6df03b1354cf03.jpg

 

一. 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;
//标志1int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,appOp);if (res != ADD_OKAY) {return res;}
//父windowWindowState parentWindow = null;
//发起者Uidfinal int callingUid = Binder.getCallingUid();
//发起者Pidfinal 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对应一个绘制屏幕
//标志2final 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;}
//是否已经添加对应的窗口, 去重判断
//标志3if (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;
------------------------------------------int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName, appOp);if (res != ADD_OKAY) {return res;}

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.** @param type The window type--窗口类型* @param isRoundedCornerOverlay {@code true} to indicate the adding window is*           round corner overlay. 指示要add的窗口为圆角叠加层。* @param packageName package name  包名* @param outAppOp First element will be filled with the app op corresponding to this window, or OP_NONE.第一个元素将填充与此窗口对应的应用操作,或OP_NONE。** @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed;*      else an error code, usually*      {@link WindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add.*//返回是否有权限添加,有则返回ADD_OKAY* @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY*/int checkAddPermission(int type, boolean isRoundedCornerOverlay, 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权限,也无法添加windowif (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)!= PERMISSION_GRANTED) {return ADD_PERMISSION_DENIED;}outAppOp[0] = AppOpsManager.OP_NONE;
//如果窗口类型不在系统划分的三大窗口范围内,则返回不合法的type--ADD_INVALID_TYPEif (!((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_OKAYif (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

            final DisplayContent 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中相关联的窗口集合
//tag1final WindowToken wToken = mRoot.getWindowToken(token);if (wToken != null) {
//tag2return wToken.getDisplayContent();}}
//tag3return mRoot.getDisplayContentOrCreate(displayId);}

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

设备的Root WindowContainer,一个设备只有一个RootWindowContainer
class RootWindowContainer extends WindowContainer<DisplayContent>implements DisplayManager.DisplayListener {

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

    private WindowManagerService(Context context, InputManagerService inputManager, boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,DisplayWindowSettingsProvider displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory, Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
...mRoot = new RootWindowContainer(this);

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

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

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

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

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

包含具有重写配置并按层次结构组织的类的通用逻辑。
public abstract class ConfigurationContainer<E extends ConfigurationContainer> {

回归正题,来看看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 (int i = mChildren.size() - 1; i >= 0; --i) {final DisplayContent dc = mChildren.get(i);final WindowToken wtoken = dc.getWindowToken(binder);if (wtoken != null) {return wtoken;}}return null;}
用于跟踪特定 Display 的 WindowStates 和其他相关内容的 辅助类。
DisplayContent 用于管理屏幕,一块屏幕对应一个 DisplayContent 对象,虽然手机只有一个显示屏,但是可以创建多个 DisplayContent 对象
class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {

 

标志3: mWindowMap

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

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

二:窗口类型检查

如果是子窗口类型if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
先找到它的父窗口,子窗口需要依附在父窗口上,如果父窗口为null,则返回ADD_BAD_SUBWINDOW_TOKENparentWindow = 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_TOKENif (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_DENIEDif (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_DISPLAYif (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获取windowTokenWindowToken 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.
如果用户提供了窗口上下文令牌,则用windowContextTokenfinal 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获取ActivityRecordactivity = 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
标志1token = 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对象

 

class WindowToken extends WindowContainer<WindowState> {protected WindowToken(WindowManagerService service, IBinder _token, int type,
boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens) {this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */);}protected WindowToken(WindowManagerService service, IBinder _token, int type,
boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,
boolean roundedCornerOverlay, boolean fromClientToken, @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);}}---------------------------------------------static class Builder {private final WindowManagerService mService;private final IBinder mToken;@WindowTypeprivate final int mType;private boolean mPersistOnEmpty;private DisplayContent mDisplayContent;private boolean mOwnerCanManageAppTokens;@Nullableprivate Bundle mOptions;Builder(WindowManagerService service, IBinder token, int type) {mService = service;mToken = token;mType = type;}/** Sets the {@link DisplayContent} to be associated. */Builder setDisplayContent(DisplayContent dc) {mDisplayContent = dc;return this;}/** @see WindowToken#mOwnerCanManageAppTokens */Builder setOwnerCanManageAppTokens(boolean ownerCanManageAppTokens) {mOwnerCanManageAppTokens = ownerCanManageAppTokens;return this;}WindowToken build() {return new WindowToken(mService, mToken, mType, mPersistOnEmpty, mDisplayContent, mOwnerCanManageAppTokens, mRoundedCornerOverlay, mFromClientToken, mOptions);}}

四: 新建WindowState

创建WindowStatefinal 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. */
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,InsetsControlTarget, InputTarget {

599a875458cc4ae68f6bd9138b6f0a70.png

 五:adjustWindowParamsLw

获取DisplayPolicyfinal 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.*/
public class DisplayPolicy {

在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 channelwin.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添加windowstatewin.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;}

 

 

相关文章:

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

Android14 WMS-窗口添加流程(一)-Client端-CSDN博客 本文接着上文"Android14 WMS-窗口添加流程(一)-Client端"往下讲。也就是WindowManagerService#addWindow流程。 目录 一. WindowManagerService#addWindow 标志1&#xff1a;mPolicy.checkAddPermission 标志…...

【传知代码】DETR[端到端目标检测](论文复现)

前言&#xff1a;想象一下&#xff0c;当自动驾驶汽车行驶在繁忙的街道上&#xff0c;DETR能够实时识别出道路上的行人、车辆、交通标志等目标&#xff0c;并准确预测出它们的位置和轨迹。这对于提高自动驾驶的安全性、减少交通事故具有重要意义。同样&#xff0c;在安防监控、…...

Edge浏览器十大常见问题,一次性解决!

Edge曾被称为最好用的浏览器&#xff0c;拳打Chrome脚踢firefox, 可如今却隐藏着像是播放卡顿、下载缓慢、广告繁多等诸多问题&#xff0c;不知道各位还在用吗&#xff1f; 今天小编收集整理了Edge浏览器十大烦人问题&#xff0c;并提供简单有效的解决办法&#xff0c;让你的E…...

lubuntu / ubuntu 配置静态ip

一、查看原始网络配置信息 1、获取网卡名称 ifconfig 2、查询网关IP route -n 二、编辑配置文件 去/etc/netplan目录找到配置文件&#xff0c;配置文件名一般为01-network-manager-all.yaml sudo vim /etc/netplan/01-network-manager-all.yaml文件打开后内容如下 # This …...

15、matlab绘图汇总(图例、标题、坐标轴、线条格式、颜色和散点格式设置)

1、plot()函数默认格式画图 代码&#xff1a; x0:0.1:20;%绘图默认格式 ysin(x); plot(x,y) 2、X轴和Y轴显示范围/axis()函数 代码&#xff1a; x0:0.1:20;%绘图默认格式 ysin(x); plot(x,y) axis([0 21 -1.1 1.1])%设置范围 3、网格显示/grid on函数 代码&#xff1a; …...

调试环境搭建(Redis 6.X 版本)

今儿&#xff0c;我们来搭建一个 Redis 调试环境&#xff0c;目标是&#xff1a; 启动 Redis Server &#xff0c;成功断点调试 Server 的启动过程。使用 redis-cli 启动一个 Client 连接上 Server&#xff0c;并使用 get key 指令&#xff0c;发起一次 key 的读取。 视频可见…...

postgres数据库报错无法写入文件 “base/pgsql_tmp/pgsql_tmp215574.97“: 设备上没有空间

解决思路&#xff1a; base/pgsql_tmp下临时表空间不够 需要新建一个临时表空间指定到根目录之外的其他目录 并且修改默认临时表空间参数 解决方法&#xff1a; select * from pg_settings where name temp_tablespaces;mkdir /home/postgres/tbs_tmp CREATE TABLESPACE tbs_t…...

力扣2762. 不间断子数组

力扣2762. 不间断子数组 multiset法 multiset&#xff1a;元素从小到大排序 begin()返回头指针 (最小)rbegin()返回尾指针 (最大) class Solution {public:long long continuousSubarrays(vector<int>& nums) {int n nums.size();long long res 0;multiset<…...

OpenCV学习(4.8) 图像金字塔

1.目的 在这一章当中&#xff0c; 我们将了解图像金字塔。我们将使用图像金字塔创建一个新的水果&#xff0c;“Orapple”我们将看到这些功能&#xff1a; cv.pyrUp&#xff08;&#xff09; &#xff0c; cv.pyrDown&#xff08;&#xff09; 在通常情况下我们使用大小恒定…...

【TB作品】msp430f5529单片机,dht22,温湿度传感器,OLED显示屏

使用DHT22温湿度传感器和OLED显示屏的单片机项目 博客名称 利用MSP430单片机读取DHT22并显示温湿度 作品功能 本项目利用MSP430单片机读取DHT22温湿度传感器的数据&#xff0c;并将温湿度信息显示在OLED显示屏上。通过这个项目&#xff0c;您可以学习如何使用单片机与传感器…...

Kotlin 异常处理

文章目录 什么是异常抛出异常通过异常信息解决异常捕获异常 什么是异常 我们在运行程序时&#xff0c;如果代码出现了语法问题或逻辑问题&#xff0c;会导致程序编译失败或退出&#xff0c;称为异常。运行结果会给出一个一长串的红色字&#xff0c;通常会给出异常信息&#xf…...

nltk下载报错

捣鼓voice_clone时报错&#xff1a; 报错信息&#xff1a; mport nltk nltk.download(‘cmudict’)For more information see: https://www.nltk.org/data.htmlAttempted to load tokenizers/punkt/PY3/english.pickleSearched in: - ‘/home/zhangshuai/nltk_data’ - ‘/hom…...

Vulnhub-DC5

靶机IP:192.168.20.139 kaliIP:192.168.20.128 网络有问题的可以看下搭建Vulnhub靶机网络问题(获取不到IP) 信息收集 nmap扫下端口及版本 dirsearch扫下目录 LinuxphpNginx 环境 我们再去看前端界面&#xff0c;发现在contact界面有能提交的地方&#xff0c;但是经过测试不…...

pytorch 笔记:pytorch 优化内容(更新中)

1 Tensor创建类 1.1 直接创建Tensor&#xff0c;而不是从Python或Numpy中转换 不要使用原生Python或NumPy创建数据&#xff0c;然后将其转换为torch.Tensor直接用torch.Tensor创建或者直接&#xff1a;torch.empty(), torch.zeros(), torch.full(), torch.ones(), torch.…...

vue 创建一个新项目 以及 手动配置选项

【Vue】3.0 项目创建 自定义配置_vue3.0-CSDN博客...

c#快速获取超大文件夹文件名

c#快速获取超大文件夹文件名 枚举集合速度快&#xff1a;(10万个文件) //by txwtech IEnumerable<string> files2 Directory.EnumerateFiles("d:\aa", "*.xml", SearchOption.TopDirectoryOnly);//过滤指定查询xml文件 慢&#xff1a; var fi…...

华为OD技术面试-最小异或-2024手撕代码真题

题目:最小异或 给你两个正整数 num1 和 num2 ,找出满足下述条件的正整数 x : x 的置位数和 num2 相同,且 x XOR num1 的值 最小 注意 XOR 是按位异或运算。 返回整数 x 。题目保证,对于生成的测试用例, x 是 唯一确定 的。 整数的 置位数 是其二进制表示中 1 的数目。 示…...

基于SpringBoot+Vue单位考勤系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝1W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还…...

Anaconda软件:安装、管理python相关包

Anaconda的作用 一个python环境中需要有一个解释器, 和一个包集合. 解释器&#xff1a; 根据python的版本大概分为2和3. python2和3之间无法互相兼容, 也就是说用python2语法写出来的脚本不一定能在python3的解释器中运行. 包集合&#xff1a;包含了自带的包和第三方包, 第三…...

pinia 重置状态插件

一、前言 测试提出&#xff0c;登出登录后&#xff0c;再次进入页面后。页面的查询项非初始状态。检查后发现&#xff0c;是因为查询项的值存到了store呢&#xff0c;从store中获取&#xff0c;故需要一个重置store的方法 二、pinia 查阅pinia官网后&#xff0c;发现pinia提…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...