当前位置: 首页 > 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提…...

一千题,No.0049(跟奥巴马一起编程)

美国总统奥巴马不仅呼吁所有人都学习编程&#xff0c;甚至以身作则编写代码&#xff0c;成为美国历史上首位编写计算机代码的总统。2014 年底&#xff0c;为庆祝“计算机科学教育周”正式启动&#xff0c;奥巴马编写了很简单的计算机代码&#xff1a;在屏幕上画一个正方形。现在…...

《python程序语言设计》2018版第5章第46题均值和标准方差-上部(我又一次被作者的出题击倒)

第N次被作者打倒了&#xff0c;第5章46题解题上集的记录 计算均值的代码段 step_num 0num_c 0 pow_c 0 while step_num < 10:a eval(input("Enter number is: "))num_c apow_c pow(a, 2)step_num 1 t2 num_c / 10这个结果和书里的答案差一点。书里写的是…...

自己做的精灵图制作,图片合成,卓宠,窗口置顶,磁力链下载等工具软件

欢迎使用和提bug&#xff0c;才v1.0.2&#xff0c;有新奇的自己需要的功能可以提给我&#xff0c;我看看能不能做。 网站地址 github...

C++协程

什么是协程 协程&#xff08;Coroutine&#xff09;是程序组件&#xff0c;可以在执行过程中暂停并在稍后继续执行。与传统的子例程&#xff08;如函数或过程&#xff09;不同&#xff0c;子例程一旦调用&#xff0c;必须等其返回后才能继续执行调用它的代码。协程则可以在执行…...

linux系统——ping命令

ping命令可以用来判断对远端ip的连通性&#xff0c;可以加域名也可以加公共ip地址 这里发送出56字节&#xff0c;返回64字节...

vue3第三十七节(自定义插件之自定义指令)防重指令

引言&#xff1a;自定义指令&#xff0c;我们可以通过插件的形式进行全局注册&#xff1a; 例如&#xff1a;在提交按钮请求接口时候&#xff0c;为了防止重复提交&#xff0c;而导致的请求资源浪费&#xff0c;或者是新增提交时候&#xff0c;防止新增相同的数据。 我们的全局…...

面试高频问题----5

一、线程池参数的执行顺序 1.如果线程池中的线程数量小于核心线程数&#xff0c;则创建新的线程来处理任务 2.如果线程池中的线程数量等于核心线程数&#xff0c;但工作队列未满&#xff0c;将任务放入工作队列中执行 3.如果工作队列已满&#xff0c;但线程数小于最大线程数…...

计算机网络 —— 网络层(子网掩码和子网划分)

计算机网络 —— 网络层&#xff08;子网掩码和子网划分&#xff09; 网络地址转换NAT子网掩码和子网划分举个例子第一步&#xff1a;看类型第二步&#xff1a;从主机号开始比对第三步&#xff1a;去头去尾 我们今天来看子网掩码和子网划分&#xff1a; 网络地址转换NAT 网络…...

2024 IDEA最新永久使用码教程(2099版)

本篇文章我就来分享一下2024年当前最新版 IntelliJ IDEA 最新注册码&#xff0c;教程如下&#xff0c;可免费永久&#xff0c;亲测有效&#xff0c;适合Windows和Mac。 本教程适用于 J B 全系列产品&#xff0c;包括 Pycharm、IDEA、WebStorm、Phpstorm、Datagrip、RubyMine、…...

http协议,tomcat的作用

HTTP 概念:Hyper Text Transfer Protocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则。 特点: 1.基于TCP协议:面向连接&#xff0c;安全 2. 基于请求-响应模型的:一次请求对应一次响应 3HTTP协议是无状态的协议:对于事务处理没有记忆能…...

wordpress添加标签云/做百度网站一年多少钱

中新经纬客户端11月4日电 4日晚间&#xff0c;网易严选官方微博发文称&#xff0c;“网易严选退出双十一大战。”截图来源&#xff1a;新浪微博网易严选网易严选表示&#xff0c;我们要退出的是这个鼓吹过度消费、为销售数字狂欢的双十一。网易严选称&#xff0c;今年双十一&am…...

怎么做直播室的网站/灰色词排名上首页

第一部分需要三个步骤&#xff1a; 选择输入步骤&#xff0c;“生成记录”&#xff0c;将步骤里设置记录数为1&#xff0c;并设置一个类型为String的字段country&#xff08;名字随便&#xff09;&#xff0c;这个字段的值应设置为我们要抽取数据的URL&#xff0c;如&#xff1…...

十大软件公司/googleseo优化

一客户一线程 在一客户一线程&#xff08;thread-per-client&#xff09;的服务器中&#xff0c;为每个连接都创建了一个新的线程来处理。服务器循环执行一些任务&#xff0c;在指定端口上侦听连接&#xff0c;反复接收客户端传入的连接请求&#xff0c;并为每个连接创建一个新…...

做搜狗pc网站优化/百度关键词优化工具

Word中分节符的奇妙用法(转)在对Word文档进行排版时&#xff0c;经常会要求对同一个文档中的不同部分采用不同的版面设置&#xff0c;例如要设置不同的页面方向、页边距、页眉和页脚&#xff0c;或重新分栏排版等。这时&#xff0c;如果通过“文件”菜单中的“页面设置”来改变…...

深圳广东网站建设套餐/郑州网站优化排名

01 listPython内置的一种数据类型是列表&#xff1a;list。list是一种有序的集合&#xff0c;可以随时添加和删除其中的元素。比如&#xff0c;列出班里所有同学的名字&#xff0c;就可以用一个list表示&#xff1a;classmates [Michael, Bob, Tracy]print(classmates)变量cla…...

基于jquery做的网站/深圳产品网络推广

编译/运行 编译时是静态加载&#xff0c;如我们的new();运行时是动态加载&#xff0c;Class.forName(); Demo走起 class Main {public static void main(String[] args) throws Exception{if("Excel".equals(args[0])){Excel excel new Excel();excel.start();…...