Android13 WMS窗口相关流程(三)

接着第二篇:https://www.jianshu.com/p/861fb070fed0?v=1698131713139

2.服务端

2.1 窗口添加

WMS通过Session接受客户端添加窗口的请求,因此WMS会新建WindowState、将WindowState加入到WindowToken,并更新WindowToken下所有WindowState的z-order。
客户端通过Binder通信调用WMS端的Session.addToDisplayAsUser进入addWindow的流程。
主要做了这三件事:
1.接收客户端请求
2.WindowState初始化
3.WindowState加入到WIndowToken

2.1.1 接收客户端请求

客户端传递给Session的参数
IWindow window:是WMS与客户端通信的句柄。
WindowManager.LayoutParams arrts:窗口布局参数。
viewVisibility:附着在窗口的rootView的可见性。
displayId:顾名思义,display id表示的是DisplayContent即屏幕的id。
InsetsVisibilities requestedVisibilities:当前对象的mVisibilities记录了insets的可见性。
InputChannel outInputChannel:InputDispatcher接收InputReader读取到的事件,分发给对应窗口,InputDispatcher属于system_server进程和各个应用不在同一进程,它们之间的联系靠的就是InputChannel。
InsetsState outInsetsState:用来保存系统中所有Insets的状态,该对象只是在客户端创建,内部属性需要在WMS端赋值。
InsetsSourceControl[] outActiveControls:InSetsSourceControl数组。该对象也是只在客户端创建,内部属性需要在WMS端赋值。
Session调用WMS.addWindow 将客户端传入的参数传递给WMS。
代码路径:framework/services/core/java/com/android/server/wm/Session.java

    @Override
    public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls) {
        return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
                requestedVisibilities, outInputChannel, outInsetsState, outActiveControls);
    }

2.1.2 addWindow

添加窗口的主要逻辑均在WMS.addWindow执行,该方法主要实现以下功能:
1.首先进行权限验证以及各种条件判断。
2.根据客户端传来的token获取windowToken。
3.借助客户端传来的参数,创建WindowState实例,并将其加入到WMS. mWindowMap中。
4.将新建的WindowState加入到相应的WindowToken,并为每个窗口赋值一个z-order。
代码路径:framework/services/core/java/com/android/server/wm/WindowManagerService.java

    public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
            int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls) {
        ......
        
        /*1.进行权限验证以及各种条件判断*/
        //判断调用者是否有权限add window
        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) {
            ......
            //根据displayId以及客户端传过来的token获取相应的displayContent
            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
            ......
            //判断mWindowMap中是否已经存在当前客户端的key,如果有则已经将当前客户端的window添加了,无需重复添加
            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;
                }
            }
            //判断当前DisplayContent是否是私有的,只拥有该display或者display已经的应用才可以在其上创建
            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;
            }
            ......
            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.
           
            /*2.根据客户端传来的token获取windowToken*/
            //attrs.token去DisplayContent.mTokenMap中去取WindowToken
            //那么WindowToken是什么时候加入到mTokenMap中的呢
            //这就要追溯到Activity的启动时,加入到DisplayContent中
            //在ActivityStarter.startActivityInner中调用addOrReparentStartingActivity通过addChild一步步调用到WindowContainert中。
            //在调用setParent,最终通过onDisplayChanged将ActivityRecord加入到DisplayContent.mTokenMap中
            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) {
            ......
             }else if (rootType >= FIRST_APPLICATION_WINDOW
                    && rootType <= LAST_APPLICATION_WINDOW) {
                //当前窗口为应用窗口,通过token,获取ActivityRecord
                activity = token.asActivityRecord();
            ......
            } 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();
            }
            /*3.创建WindowState*/
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], attrs, viewVisibility, session.mUid, userId,
                    session.mCanAddInternalSystemWindow);
            //将客户端传过来的Insets可见性赋值给WindowState的requestedVisibilities
            win.setRequestedVisibilities(requestedVisibilities);
            //验证当前窗口是否可以添加到WMS
            res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
            if (res != ADD_OKAY) {
                return res;
            }
            //调用openInputChannel,初始化input相关通路
            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
            //创建SufaceSession用于SurfaceFlinger通信
            win.attach();
            //将客户端与WindowState加入到mWindowMap中
            mWindowMap.put(client.asBinder(), win);
            win.initAppOpsState();
            ......
            /*4.将WindowState加入到WindowToken*/
            win.mToken.addWindow(win);
            ......
        return res;
    }

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

2.1.3 WindowToken的创建
token = new WindowToken.Builder(this, client.asBinder(), type)
        .setDisplayContent(displayContent)
        .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
        .build();

这里调用的是其WindowToken自身的build方法创建
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowToken.java

WindowToken build() {
    return new WindowToken(mService, mToken, mType, mPersistOnEmpty, mDisplayContent,
            mOwnerCanManageAppTokens, mRoundedCornerOverlay, mFromClientToken, mOptions);
}

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);
    }
}

dc.addWindowToken(token, this);在WindowToken构造方法中,调用DisplayContent.addWindowToken将WindowToken添加到以DisplayContent为根节点的WindowContainer层级结构中。
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    void addWindowToken(IBinder binder, WindowToken token) {
        final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
        if (dc != null) {
            // We currently don't support adding a window token to the display if the display
            // already has the binder mapped to another token. If there is a use case for supporting
            // this moving forward we will either need to merge the WindowTokens some how or have
            // the binder map to a list of window tokens.
            throw new IllegalArgumentException("Can't map token=" + token + " to display="
                    + getName() + " already mapped to display=" + dc + " tokens=" + dc.mTokenMap);
        }
        if (binder == null) {
            throw new IllegalArgumentException("Can't map token=" + token + " to display="
                    + getName() + " binder is null");
        }
        if (token == null) {
            throw new IllegalArgumentException("Can't map null token to display="
                    + getName() + " binder=" + binder);
        }

        mTokenMap.put(binder, token);

        if (token.asActivityRecord() == null) {
            // Set displayContent for non-app token to prevent same token will add twice after
            // onDisplayChanged.
            // TODO: Check if it's fine that super.onDisplayChanged of WindowToken
            //  (WindowsContainer#onDisplayChanged) may skipped when token.mDisplayContent assigned.
            token.mDisplayContent = this;
            // Add non-app token to container hierarchy on the display. App tokens are added through
            // the parent container managing them (e.g. Tasks).
            //1.调用DisplayContent.findAreaForToken为当前WindowToken寻找一个合适的父容器,DisplayArea.Tokens对象。
            final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
            //2.将WindowToken添加到父容器中。
            da.addChild(token);
        }
    }

这里我们分两步看
1.final DisplayArea.Tokens da = findAreaForToken(token).asTokens();调用DisplayContent.findAreaForToken为当前WindowToken寻找一个合适的父容器,DisplayArea.Tokens对象。

    /**
     * Finds the {@link DisplayArea} for the {@link WindowToken} to attach to.
     * <p>
     * Note that the differences between this API and
     * {@link RootDisplayArea#findAreaForTokenInLayer(WindowToken)} is that this API finds a
     * {@link DisplayArea} in {@link DisplayContent} level, which may find a {@link DisplayArea}
     * from multiple {@link RootDisplayArea RootDisplayAreas} under this {@link DisplayContent}'s
     * hierarchy, while {@link RootDisplayArea#findAreaForTokenInLayer(WindowToken)} finds a
     * {@link DisplayArea.Tokens} from a {@link DisplayArea.Tokens} list mapped to window layers.
     * </p>
     *
     * @see DisplayContent#findAreaForTokenInLayer(WindowToken)
     */
    DisplayArea findAreaForToken(WindowToken windowToken) {
        return findAreaForWindowType(windowToken.getWindowType(), windowToken.mOptions,
                windowToken.mOwnerCanManageAppTokens, windowToken.mRoundedCornerOverlay);
    }

为传入的WindowToken找到一个DisplayArea对象来添加进去。

    DisplayArea findAreaForWindowType(int windowType, Bundle options,
            boolean ownerCanManageAppToken, boolean roundedCornerOverlay) {
        // TODO(b/159767464): figure out how to find an appropriate TDA.
        //1.如果是App窗口,那么返回默认的TaskDisplayArea对象。
        if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {
            return getDefaultTaskDisplayArea();
        }

        // Return IME container here because it could be in one of sub RootDisplayAreas depending on
        // the focused edit text. Also, the RootDisplayArea choosing strategy is implemented by
        // the server side, but not mSelectRootForWindowFunc customized by OEM.
        //2.如果是输入法窗口,那么返回ImeContainer。
        if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) {
            return getImeContainer();
        }
        //3.如果是其他类型,继续寻找。
        return mDisplayAreaPolicy.findAreaForWindowType(windowType, options,
                ownerCanManageAppToken, roundedCornerOverlay);
    }

如果是App窗口,那么返回默认的TaskDisplayArea对象。
如果是输入法窗口,那么返回ImeContainer。
如果是其他类型,继续寻找。
mDisplayAreaPolicy.findAreaForWindowType(windowType, options, ownerCanManageAppToken, roundedCornerOverlay);调用的是DisplayAreaPolicy中的findAreaForWindowType方法,DisplayAreaPolicy为抽象类,DisplayAreaPolicyBuilder中的Result继承了该类
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java

static class Result extends DisplayAreaPolicy {
    ......
       @Override
       public DisplayArea.Tokens findAreaForWindowType(int type, Bundle options,
               boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
           return mSelectRootForWindowFunc.apply(type, options).findAreaForWindowTypeInLayer(type,
                   ownerCanManageAppTokens, roundedCornerOverlay);
       }
       ......

代码路径:frameworks/base/services/core/java/com/android/server/wm/RootDisplayArea.java

  DisplayArea.Tokens findAreaForWindowTypeInLayer(int windowType, boolean ownerCanManageAppTokens,
           boolean roundedCornerOverlay) {
       //通过getWindowLayerFromTypeLw方法获取对应的窗口类型
       int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType,
               ownerCanManageAppTokens, roundedCornerOverlay);
       if (windowLayerFromType == APPLICATION_LAYER) {
           throw new IllegalArgumentException(
                   "There shouldn't be WindowToken on APPLICATION_LAYER");
       }
       return mAreaForLayer[windowLayerFromType];
   }

通过getWindowLayerFromTypeLw方法计算出该窗口的类型对应的层级值windowLayerFromType,然后从mAreaForLayer数组中,找到windowLayerFromType对应的那个DisplayArea.Tokens对象。

  1. da.addChild(token);将WindowToken添加到父容器(叶子节点)中。
    代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayArea.java
    /**
     * DisplayArea that contains WindowTokens, and orders them according to their type.
     */
    public static class Tokens extends DisplayArea<WindowToken> {
        ......
        void addChild(WindowToken token) {
            addChild(token, mWindowComparator);
        }
        ......

addChild(token, mWindowComparator);最终调用到WindowContainer.addChild方法添加WindowToken到叶子节点

2.1.4 WindowState初始化

在addWindow中初始化WindowState

final WindowState win = new WindowState(this, session, client, token, parentWindow,
        appOp[0], attrs, viewVisibility, session.mUid, userId,
        session.mCanAddInternalSystemWindow);

下面我们看一下在WindowState的实例化过程中,都做了什么。
1.根据客户端传过来的参数,对相关属性进行赋值。
2.根据当前窗口的类型获取mBaseLayer,当将WindowState加入到WindowToken时,该值用来确定加入窗口在WindowToken数组中的位置。
3.实例化WindowStateAnimator,该类会跟踪当前WIndowState的动画以及surface操作。
代码路径:framework/services/core/java/com/android/server/wm/WindowState.java

WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
            int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
            PowerManagerWrapper powerManagerWrapper) {
        /*1.根据客户端传递过来的参数,对相关属性进行赋值*/
        //调用WindowState的父类WindowContainer构造方法,将WMS赋值给其父类属性mWmService
        super(service);
        //获取事务
        mTmpTransaction = service.mTransactionFactory.get();
        //将Session赋值给mSession
        mSession = s;
        //将与客户端通信的Binder赋值给mClient
        mClient = c;
        mAppOp = appOp;
        //将当前activity的token赋值给mToken
        mToken = token;
        //通过token,获取当前窗口对的ActivityRecord
        mActivityRecord = mToken.asActivityRecord();
        //赋值id
        mOwnerUid = ownerId;
        mShowUserId = showUserId;
        //是否可以添加系统窗口的标志位
        mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
        mWindowId = new WindowId(this);
        //布局参数赋值给mAttrs
        mAttrs.copyFrom(a);
        //将surfaceInsets赋值给mLastSurfaceInsets
        mLastSurfaceInsets.set(mAttrs.surfaceInsets);
        //将窗口可见性赋值给mViewVisibility 
        mViewVisibility = viewVisibility;
        //将窗口WindowManagerPolicy赋值给mPolicy 
        mPolicy = mWmService.mPolicy;
        mContext = mWmService.mContext;
        ......
        /*2.获取当前窗口的BaseLayer*/
        if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
            ......
        } else {
            // The multiplier here is to reserve space for multiple
            // windows in the same type layer.
            //当前为应用窗口所以mPolicy.getWindowLayerLw(this)获取值为2,即应用层级
            //TYPE_LAYER_MULTIPLIER为同一类型的多窗口保留空间
            //TYPE_LAYER_OFFSET将同一组窗口移动到同一层中其他窗口的上方或者下方
            mBaseLayer = mPolicy.getWindowLayerLw(this)
                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
            mSubLayer = 0;
            mIsChildWindow = false;
            mLayoutAttached = false;
            mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
                    || mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
            mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
        }
        ......
        /*3.新建windowStateAnimator,该类会跟踪当前WindowState的动画以及surface操作*/
        mWinAnimator = new WindowStateAnimator(this);
        //将透明度alpha赋值给mAlpha 
        mWinAnimator.mAlpha = a.alpha;
        ......
    }

2.1.5 将WindowState加入到WindowToken

在addWindow中将WindowState加入到WindowToken
win.mToken.addWindow(win);
WindowState加入到WindowToken中的具体过程:
1.将要加入的WindowState.mBaseLayer与WindowToken中现有的WindowState.mBaseLayer相比,按照mBaseLayer有小到大存放到数组中,若mBaseLayer相等,则后加入的WindowState放在数组后面。
代码路径:framework/services/core/java/com/android/server/wm/WindowToken.java

 void addWindow(final WindowState win) {
        ProtoLog.d(WM_DEBUG_FOCUS,
                "addWindow: win=%s Callers=%s", win, Debug.getCallers(5));

        if (win.isChildWindow()) {
            // Child windows are added to their parent windows.
            //如果是子窗口直接返回
            return;
        }
        // This token is created from WindowContext and the client requests to addView now, create a
        // surface for this token.
        if (mSurfaceControl == null) {
            createSurfaceControl(true /* force */);

            // Layers could have been assigned before the surface was created, update them again
            reassignLayer(getSyncTransaction());
        }
        if (!mChildren.contains(win)) {
            ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
            //调用WindowContainer.addChild方法
            addChild(win, mWindowComparator);
            mWmService.mWindowsChanged = true;
            // TODO: Should we also be setting layout needed here and other places?
        }
    }
 /**
   * Compares two child window of this token and returns -1 if the first is lesser than the
   * second in terms of z-order and 1 otherwise.
   */
  private final Comparator<WindowState> mWindowComparator =
          (WindowState newWindow, WindowState existingWindow) -> {
      final WindowToken token = WindowToken.this;
      ......
      //如果新窗口的mBaseLayer 不小于(大于等于)已经存在的WindowState的BaseLayer,则返回1,否则返回-1
      return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1;
  };
    /**
     * Returns true if the new window is considered greater than the existing window in terms of
     * z-order.
     */
    protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow,
            WindowState existingWindow) {
        // New window is considered greater if it has a higher or equal base layer.
        //此处可以发现比较的是两个窗口的mBaseLayer
        return newWindow.mBaseLayer >= existingWindow.mBaseLayer;
    }

我们看看WindowContainer.addChild方法
代码路径:framework/services/core/java/com/android/server/wm/WindowContainer.java

   /**
     * Adds the input window container has a child of this container in order based on the input
     * comparator.
     * @param child The window container to add as a child of this window container.
     * @param comparator Comparator to use in determining the position the child should be added to.
     *                   If null, the child will be added to the top.
     */
    @CallSuper
    protected void addChild(E child, Comparator<E> comparator) {
        ......
        //记录插入数组的位置,若为-1则将windowState加入到后面
        int positionToAdd = -1;
        if (comparator != null) {
            //判断当前WindowToken中WindowState的数量
            //依次比较将要加入的窗口与已经存在的WindowState的BaseLayer
            //mChildren越大放到数组最前面WindowToken
            final int count = mChildren.size();
            for (int i = 0; i < count; i++) {
                //比较baseLayer,如果child大于列表中已经存在的,则需要返回1,否则返回-1
                //新加入的的child大于mChildren.get(i)则返回1,小于则返回-1
                //注:comparator比较器的逻辑见上面代码的mWindowComparator 
                if (comparator.compare(child, mChildren.get(i)) < 0) {
                    //记录当前要插入的位置
                    positionToAdd = i;
                    break;
                }
            }
        }
        //如果新加入的窗口大于现在所有窗口
        if (positionToAdd == -1) {
            //将该窗口加入到列表最后
            mChildren.add(child);
        } else {
            mChildren.add(positionToAdd, child);
        }

        // Set the parent after we've actually added a child in case a subclass depends on this.
        //此处将child的mParent设置为this
        child.setParent(this);
    }

2.将WindowState的mParent置为刚才的WindowToken,并更新其Parent的mTreeWeight。mTreeWeight记录了其子节点的数量。
继续查看WindowState的父类WindowContainer.setParent

    final protected void setParent(WindowContainer<WindowContainer> parent) {
        //将当前WindowState的mParent设置为相应的WindowToken
        final WindowContainer oldParent = mParent;
        mParent = parent;

        if (mParent != null) {
            //更新parent中的mTreeWeight属性
            //mTreeWeight代表以parent的根节点的子树中的元素的数量
            mParent.onChildAdded(this);
        } else if (mSurfaceAnimator.hasLeash()) {
            mSurfaceAnimator.cancelAnimation();
        }
        if (!mReparenting) {
            onSyncReparent(oldParent, mParent);
            if (mParent != null && mParent.mDisplayContent != null
                    && mDisplayContent != mParent.mDisplayContent) {
                onDisplayChanged(mParent.mDisplayContent);
            }
            //计算显示layer
            onParentChanged(mParent, oldParent);
        }
    }

3.将WindowState加入到WindowToken之后,调用parent的assignChildLayers方法,调整其所有child的z-order。主要经历以下步骤:
初始化layer=0,代表着z-order。
遍历mChildren数组,判断Children是否需要提高到顶部(判断标志位mNeedsZBoost)。如果不需要则调用Children的assignLayer方法调整其z-order为layer,并将layer++。如果需要则执行下一遍循环。
再次遍历mChildren数组,判断Children是否需要提高到顶部。如果需要则则调用Children的assignLayer方法调整其z-order为layer,并将layer++。如果不需要则执行下一次循环。
继续看onParentChanged方法

/**
     * Callback that is triggered when @link WindowContainer#setParent(WindowContainer)} was called.
     * Supposed to be overridden and contain actions that should be executed after parent was set.
     */
    @Override
    void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
        onParentChanged(newParent, oldParent, null);
    }

    void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent,
            PreAssignChildLayersCallback callback) {
        ......
        if (mSurfaceControl == null) {
            // If we don't yet have a surface, but we now have a parent, we should
            // build a surface.
            //创建一个SurfaceControl来调整窗口的z-order
            createSurfaceControl(false /*force*/);
        } else {
            ......
        }
        ......
        // Either way we need to ask the parent to assign us a Z-order.
        //进入WindowToken的父类WindowContainer中,调整窗口的z-order
        mParent.assignChildLayers();
        scheduleAnimation();
    }
    
    void assignChildLayers() {
        assignChildLayers(getSyncTransaction());
        scheduleAnimation();
    }
    
    void assignChildLayers(Transaction t) {
    //分配给当前窗口的z-order,初始化为0
        int layer = 0;

        // We use two passes as a way to promote children which
        // need Z-boosting to the end of the list.
        //此处会以parent为根节点向下遍历到子节点,再从下到上依次进行处理
        for (int j = 0; j < mChildren.size(); ++j) {
            final WindowContainer wc = mChildren.get(j);
            wc.assignChildLayers(t);
            //needsZBoot是用来判断当前窗口是否应该提升到容器的顶部
            //若不需要提升到容器的顶部
            if (!wc.needsZBoost()) {
                //调用WindowState的父类WindowContainer中的assignLayer
                wc.assignLayer(t, layer++);
            }
        }
        //处理需要提升到容器顶部的窗口
        for (int j = 0; j < mChildren.size(); ++j) {
            final WindowContainer wc = mChildren.get(j);
            if (wc.needsZBoost()) {
                wc.assignLayer(t, layer++);
            }
        }
        if (mOverlayHost != null) {
            mOverlayHost.setLayer(t, layer++);
        }
    }

4.在Children的assignLayer中会首先判断此次要调整的layer与自己上次layer是否相等,不相等则最终会调用nativeSetLayer来调整自己的z-order。

    void assignLayer(Transaction t, int layer) {
        // Don't assign layers while a transition animation is playing
        // TODO(b/173528115): establish robust best-practices around z-order fighting.
        //如果正在执行Transaction,则不需要进行assignLayer
        if (mTransitionController.isPlaying()) return;
        //layer为此次要调整的z-order
        final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
        //如果需要调整
        if (mSurfaceControl != null && changed) {
            //调用setLayer调整窗口的z-order
            setLayer(t, layer);
            //将mLastLayer调整为新的z-order
            mLastLayer = layer;
            mLastRelativeToLayer = null;
        }
    }
    
    protected void setLayer(Transaction t, int layer) {
        if (mSurfaceFreezer.hasLeash()) {
            ......
        } else {
            // Route through surface animator to accommodate that our surface control might be
            // attached to the leash, and leash is attached to parent container.
            //调用SurfaceAnimator中的setLayer
            mSurfaceAnimator.setLayer(t, layer);
        }
    }

代码路径:framework/services/core/java/com/android/server/wm/SurfaceAnimator.java

    /**
     * Sets the layer of the surface.
     * <p>
     * When the layer of the surface needs to be adjusted, we need to set it on the leash if the
     * surface is reparented to the leash. This method takes care of that.
     */
    void setLayer(Transaction t, int layer) {
        //调用SurfaceControl中的setlayer方法
        t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), layer);
    }

代码路径:framework/core/java/android/view/SurfaceControl.java

        /**
         * Set the Z-order for a given SurfaceControl, relative to it's siblings.
         * If two siblings share the same Z order the ordering is undefined. Surfaces
         * with a negative Z will be placed below the parent surface.
         *
         * @param sc The SurfaceControl to set the Z order on
         * @param z The Z-order
         * @return This Transaction.
         */
        @NonNull
        public Transaction setLayer(@NonNull SurfaceControl sc,
                @IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE) int z) {
            //调用调整layer
            checkPreconditions(sc);
            nativeSetLayer(mNativeObject, sc.mNativeObject, z);
            return this;
        }

————————————————
版权声明:本文为CSDN博主「yi诺千金」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yimelancholy/article/details/130339779

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容