In the first two articles, the process of adding Windows was analyzed. In this article, the positioning process of Windows was analyzed and summarized.

The first thing to mention is the WindowSurfacePlacer class. When WMS starts, the WindowSurfacePlacer object is created to perform the Surface “placement” operation of the window.

    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,
            Supplier<Surface> surfaceFactory,
            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {...Create a WindowSurfacePlacer object
        mWindowPlacerLocked = new WindowSurfacePlacer(this); . }Copy the code

The WindowSurfacePlacer#performSurfacePlacement() method, which is the core method in WMS, is responsible for placing the Surface of all Windows, how they are displayed, where they are displayed, and how large the display area is. Confirm and send to SurfaceFlinger for display. When any state changes in THE WMS, the method will be triggered to traverse the entire WMS tree structure to determine whether all the surfaces are visible or not. Let’s start with this method and examine the process in detail.

1.WindowSurfacePlacer#performSurfacePlacement()

When some state changes in WMS, the window will be placed through this method, which will call the overloaded method to continue execution:

// frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java

    final void performSurfacePlacement(boolean force) {
        if (mDeferDepth > 0 && !force) {
            mDeferredRequests++;
            return;
        }
        int loopCount = 6;
        do {
            mTraversalScheduled = false;
            performSurfacePlacementLoop();
            mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
            loopCount--;
        } while (mTraversalScheduled && loopCount > 0);
        mService.mRoot.mWallpaperActionPending = false;
    }
Copy the code

MDeferredRequests indicate the number of times Surface Placement requests were made while the layout was delayed. Both mDeferredRequests are added to optimize performance and delay Placement when multiple system state changes occur. Do this once and for all until the change is complete.

Here is also equipped with a loop body, through implementing internal performSurfacePlacementLoop () method to perform 6 times at most.

MTraversalScheduled indicates whether a request to perform traversal has been received, and it is set to true in only one place:

// frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java

    void requestTraversal(a) {
    
        // Set to true
        mTraversalScheduled = true;
        if (mDeferDepth > 0) {
            mDeferredRequests++;
            return;
        }
	// Start in the andriod.animation thread
        mService.mAnimationHandler.post(mPerformSurfacePlacement);
    }
Copy the code

PerformSurfacePlacementLoop () as follows:

// frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java

    private void performSurfacePlacementLoop(a) {
	// If you are in the window placement process, return directly
        if (mInLayout) {
            return;
        }
	/ / if defaultDisplay mWaitingForConfig to true said DisplayContent waiting for update configuration, without window is placed at this time, upon the configuration
        final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
        if (defaultDisplay.mWaitingForConfig) {
            return;
        }
	// Indicates window placement
        mInLayout = true; .try {
            // Enter RootWindowContainer
            mService.mRoot.performSurfacePlacement();
            / / reset value
            mInLayout = false;
            // Whether DisplayContent still needs to be placed
            if (mService.mRoot.isLayoutNeeded()) {
                if (++mLayoutRepeatCount < 6) {
                    requestTraversal();
                } else {
                    mLayoutRepeatCount = 0; }}else {
                mLayoutRepeatCount = 0; }}catch (RuntimeException e) {}
    }
Copy the code

In this method, mInLayout indicates whether the window Surface is being placed, which will enter the RootWindowContainer#performSurfacePlacement() method and start traversing the WMS tree starting at the root of the WMS tree structure.

2. RootWindowContainer# performSurfacePlacementNoTrace atomic operations in the ()

In this method, atomic operations are performed on the Transaction object to locate all Windows:

// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

    void performSurfacePlacementNoTrace(a) {...// Start the transaction
        mWmService.openSurfaceTransaction();			
        try {
            // Perform atomic operations
            applySurfaceChangesTransaction();
        } catch (RuntimeException e) {
        } finally {
            // Commit the transaction
            mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces"); }... }Copy the code

In this method:

    1. First, a surfacecontrolle #sGlobalTransaction object is obtained using the WMS#openSurfaceTransaction() method.
    1. Then execute applySurfaceChangesTransaction all SurfaceControl traversal ();
    1. Finally, a series of SurfaceControl’s on sGlobalTransaction are committed to SurfaceFlinger using the WMS#closeSurfaceTransaction() method. All SurfaceControl atomic operations are performed through sGlobalTransaction.

Then we see applySurfaceChangesTransaction () method, here will traverse each DisplayContent:

// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

    private void applySurfaceChangesTransaction(a) {
        mHoldScreenWindow = null;
        mObscuringWindow = null;

        final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked();	/ / the default DisplayContent
        final DisplayInfo defaultInfo = defaultDc.getDisplayInfo();			// Default DisplayContent Specifies the logical screen information
        final int defaultDw = defaultInfo.logicalWidth;					// Logical screen width and height
        final intdefaultDh = defaultInfo.logicalHeight; .// Iterate over each DisplayContent, placing the Surface in each Display
        final int count = mChildren.size();
        for (int j = 0; j < count; ++j) {
            final DisplayContent dc = mChildren.get(j);
            dc.applySurfaceChangesTransaction();
        }
        // Notify THE DMS to adjust the Display attributes
        mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
	// Merge mDisplayTransaction into sGlobalTransaction
        SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
    }
Copy the code

In this method, several special SurfaceControl operations are performed on the mDisplayTransaction object. MDisplayTransaction is a separate transaction object, dedicated to the Display related atomic operation, when complete DisplayContent# applySurfaceChangesTransaction (after), Is merged into the global Transaction object.

3.DisplayContent#applySurfaceChangesTransaction()

Will traverse the next DisplayContent, execute each DisplayContent applySurfaceChangesTransaction () method, DisplayContent object and logical screen is a one-to-one relationship, represents a logical screen shows the container.

DisplayContent# applySurfaceChangesTransaction () method is as follows:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    void applySurfaceChangesTransaction(a) {
        final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;

        int repeats = 0;
        do{...// 1. Perform layout operations
            if (repeats < LAYOUT_REPEAT_THRESHOLD) {
                performLayout(repeats == 1.false /* updateInputWindows */);
            } 
            pendingLayoutChanges = 0;
            // 2. Apply Display policy and Window Policy
            try {
                mDisplayPolicy.beginPostLayoutPolicyLw();
                forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
                pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
            } finally {
            }
            mInsetsStateController.onPostLayout();
        } while(pendingLayoutChanges ! =0);

        mTmpApplySurfaceChangesTransactionState.reset();

        try {
            // 3. Perform a WindowState transition
            forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
        } finally{}// 4. Make final preparationsprepareSurfaces(); . }Copy the code

In this method, there are three steps:

  • Step1. Perform the performLayout() method to perform the layout operation.

The WindowFrame of each WindowState is confirmed in this step;

  • Step2. Perform the later operation of layout and apply the Policy Settings related to Display and Window.

Apply some of the current policies to each WindowState and control the display and hiding states of Windows.

  • Step3. Perform all window updates and state changes;

Through each WindowState, determine whether to modify the logical screen display parameters according to the carried parameters, and change the WindowState state from COMMIT_DRAW_PENDING to READY_TO_SHOW.

Step4. Prepare the Surface and wait for the submission of things.

Make final preparations for the location, size, and so on of each Surface and submit it to the sGlobalTransation object for show or hide.

3.1. PerformLayout () determines DisplayFrame and WindowFrame

Through this step, the DisplayFrame will be updated according to some current system Windows, such as status Bar, Navigation Bar, etc., and the WindowFrame of all Windows will be determined. The performLayout() method calls the performLayoutNoTrace() method directly:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
        // If mLayoutNeeded is false, return it
        if(! isLayoutNeeded()) {return; }...// DisplayPolicy#beginLayoutLw() does the initial work of the layout, which will reset some states
        mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
	// Perform the layout of non-child Windows
        forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
	// Layout the child window
        forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */); . }Copy the code

As you can see, the DisplayContent#mLayoutNeeded property is the key to controlling whether the operation is performed. There are also three steps in this method:

  1. Call the DisplayPolicy#beginLayoutLw() method, which lays out the status bar and nav bar, and updates each Rect property in the DisplayFrame based on the status bar and nav bar states, Complete the determination of DisplayFrame;
  2. To all WindowState layout operation, mPerformLayout is an instance of the function interface Consumer, through forAllWindows() to complete all WindowState traversal, complete the WindowFrame determination;
  3. Additional processing is done for attached Windows that are not of type TYPE_APPLICATION_ATTACHED_DIALOG. The WindowState#mLayoutAttached property represents attached Windows, that is, Windows of type SubWindow.

Next, the main analysis of the second step, mPerformLayout is as follows:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    private final Consumer<WindowState> mPerformLayout = w -> {
	// Determine whether layout can be performed
        final booleangone = (mTmpWindow ! =null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w))
                || w.isGoneForLayoutLw();

	// update the WindowState#mBehindIme property
        if(w.mBehindIme ! = mTmpWindowsBehindIme) { w.mBehindIme = mTmpWindowsBehindIme; . }// When iterating over InputMethodWindow, set mTmpWindowsBehindIme to true, and the mBehindIme property of WindowState thereafter will be set to true
        if (w == mInputMethodWindow) {
            mTmpWindowsBehindIme = true;
        }
	// Layout is not performed when WindowState is not visible and a frame already exists
        if((! gone || ! w.mHaveFrame || w.mLayoutNeeded) && ! w.mLayoutAttached) {// Reset parameters
            w.mLayoutNeeded = false;
            // whether the layout is the first time, according to the WindowState#mLayoutSeq property to determine
            final booleanfirstLayout = ! w.isLaidOut();// Set WindowFrame Rect properties
            getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames); w.mLayoutSeq = mLayoutSeq; . }};Copy the code

In this method, the WindowState after InputMethodWindow, whose mBehindIme property is set to true, is under the InputMethodWindow window.

Then, when the WindowState is visible, the DisplayPolicy#layoutWindowLw() method is executed, which determines the individual Rect object properties in the WindowFrame based on the DisplayFrame object. The process of determining a WindowFrame is a long one, so I won’t analyze it here.

After this step, the WindowFrame for each WindowState is determined, which means that the size of the window is calculated.

3.2. MApplyPostLayoutPolicy WindowState for policy applications

Next comes the application of WindowState to Policy, which also takes place in three steps:

  1. DisplayPolicy. BeginPostLayoutPolicyLw () to reset the related parameters in DisplayPolicy;
  2. Function interface mApplyPostLayoutPolicy applies policy to each WindowState;
  3. DisplayPolicy. FinishPostLayoutPolicyLw () after completion of the policy application operation;

Again, only the most critical part — the application of WindowState to Policy — is analyzed. This is done through the function interface object mApplyPostLayoutPolicy. The DisplayPolicy#applyPostLayoutPolicyLw() method is called directly, and in applyPostLayoutPolicyLw(), WindowManagerPolicy#applyKeyguardPolicyLw()

// frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java

    public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs, WindowState attached, WindowState imeTarget) {...// Apply Policy to winmService.mPolicy.applyKeyguardPolicyLw(win, imeTarget); . }Copy the code

The applyKeyguardPolicyLw() method sets the appropriate visible flags for WindowState based on whether the screen is currently locked and whether WindowState can be displayed on the locked screen:

// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

    public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) {
	// Whether this win can be hidden by keyGuard
        if (canBeHiddenByKeyguardLw(win)) {
            // Whether it should be hidden currently
            if (shouldBeHiddenByKeyguard(win, imeTarget)) {
                win.hideLw(false);
            } else {
                win.showLw(false); }}}Copy the code

The first step is to determine whether the current WindowState can be hidden by KeyGuard, which directly affects the display of the window. If it can be hidden, the next step is to determine whether it should be hidden, and if it needs to be hidden, call WindowState#hideLw() to hide it, or WindowState#showLw() to display it otherwise. Let’s take a look at the process in turn.

1. Whether WindowState can be hidden by keyGuard

Let’s see how to verify that WindowState can be hidden by Keyguard:

// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
    
    public boolean canBeHiddenByKeyguardLw(WindowState win) {
	// If it is a WindowState of Activity type, visibility is determined by ActivityRecord and is not overridden by KeyGuard
        if(win.getAppToken() ! =null) {
            return false;
        }
	// The following four window types are not overridden by Keyguard
        switch (win.getAttrs().type) {
            case TYPE_NOTIFICATION_SHADE:
            case TYPE_STATUS_BAR:
            case TYPE_NAVIGATION_BAR:
            case TYPE_WALLPAPER:
                return false;
            default:
                // Check whether the current Layer of Win is smaller than
                returngetWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE); }}Copy the code

Keyguard does not overwrite Activity Windows, four system Windows, or Windows whose Layer value is greater than that of TYPE_NOTIFICATION_SHADE Windows.

2. Whether the current WindowState should be overwritten

If it can be overwritten, then it confirms whether it should be overwritten:

// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

    private boolean shouldBeHiddenByKeyguard(WindowState win, WindowState imeTarget) {
        finalLayoutParams attrs = win.getAttrs(); .// If the current window is an IME window and in AOD state, the IME window is hidden
        final booleanhideIme = win.isInputMethodWindow() && (mAodShowing || ! mDefaultDisplayPolicy.isWindowManagerDrawComplete());if (hideIme) {
            return true;
        }
	// If the current IME Target window exists and is visible, and the IME Target can be displayed on the lock screen
        final booleanshowImeOverKeyguard = imeTarget ! =null&& imeTarget.isVisibleLw() && (imeTarget.canShowWhenLocked() || ! canBeHiddenByKeyguardLw(imeTarget));// The IME window is displayed when the screen is locked
        boolean allowWhenLocked = win.isInputMethodWindow() && showImeOverKeyguard;
	// Check whether the screen is locked
        final boolean isKeyguardShowing = mKeyguardDelegate.isShowing();
	// If the screen is locked but blocked
        if(isKeyguardShowing && isKeyguardOccluded()) { allowWhenLocked |= win.canShowWhenLocked() || (attrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) ! =0;
        }
	// Returns "Is the screen currently locked and not allowed to be displayed on the lock screen"
        returnisKeyguardShowing && ! allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY; }Copy the code

In this method, if it is determined that the current system is in the lock screen state and the current Windows State does not allow display on the lock screen, then it needs to be hidden.

3. Hide and show WindowState

If the conditions are met, hide WindowState using the hideLw() and showLw() methods. This is done directly by setting flags to the WindowState#mPolicyVisibility property:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    boolean hideLw(boolean doAnimation, boolean requestAnim) {...if(! doAnimation) {// Clear the LEGACY_POLICY_VISIBILITY flag
            clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
        }
        return true;
    }

    boolean showLw(boolean doAnimation, boolean requestAnim) {
        // Add the LEGACY_POLICY_VISIBILITY tag
        setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
        return true;
    }
Copy the code

Whether WindowState is ultimately displayed or not depends on the many variables that affect its visibility. In this case, the window is hidden by policy whether the client and WMS state make it display or not.

3.3. MApplySurfaceChangesTransaction

MApplyPostLayoutPolicy interface implementation has been completed, the next execution mApplySurfaceChangesTransaction function interface, in the interface, traverses the WindowState according to carry parameters to determine whether to modify logical screen, And change the WindowState state from COMMIT_DRAW_PENDING to READY_TO_SHOW:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
        final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
	// Determine whether the current WindowState is obscured by the above WindowState
        w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
		
	// The current WindowState that does not block other Windows will get the logical screen display properties set in Win
        if(! mTmpApplySurfaceChangesTransactionState.obscured) {final boolean isDisplayed = w.isDisplayedLw();
			
            // Determine whether the WindowState will be obscured for subsequent traversal
            if (isDisplayed && w.isObscuringDisplay()) {
                root.mObscuringWindow = w;
                mTmpApplySurfaceChangesTransactionState.obscured = true;
            }
            // Whether there is content displayed on the logical screen to control multi-screen mirroring
            final boolean displayHasContent = root.handleNotObscuredLocked(w,
                    mTmpApplySurfaceChangesTransactionState.obscured,
                    mTmpApplySurfaceChangesTransactionState.syswin);
            if(! mTmpApplySurfaceChangesTransactionState.displayHasContent && ! getDisplayPolicy().isWindowExcludedFromContent(w)) { mTmpApplySurfaceChangesTransactionState.displayHasContent |= displayHasContent; }// Check whether WindowState contains preferredRefreshRate, preferredDisplayModeId, etc
            if (w.mHasSurface && isDisplayed) {
                final inttype = w.mAttrs.type; .if (mTmpApplySurfaceChangesTransactionState.preferredRefreshRate == 0&& w.mAttrs.preferredRefreshRate ! =0) { mTmpApplySurfaceChangesTransactionState.preferredRefreshRate = w.mAttrs.preferredRefreshRate; }... }}// Surface state transition
        if (w.mHasSurface) {
            final booleancommitted = winAnimator.commitFinishDrawingLocked(); . }... };Copy the code

In this method, three main things are done:

  1. Set the WindowState# mobsmobile property value;
  2. Carried by the window control logic screen to display the related parameters of filling to mTmpApplySurfaceChangesTransactionState object properties;
  3. Commit the drawn window.

1. Set the WindowState# mobsmobile property

WindowState# mobsmobile indicates whether the window is shaded by other Windows. As long as a window is displayed in full screen during this traverse, the mObscured property of WindowState for subsequent traverse will be set to true, indicating that it will be obscured. At the same time will mTmpApplySurfaceChangesTransactionState. Obscured set to true.

MTmpApplySurfaceChangesTransactionState object is used to save on all Windows in the process of traversal traversal results, is named after “temp”, because every time Windows traversed will reset the object.

2. Fill mTmpApplySurfaceChangesTransactionState object

After determining the WindowState# mobsmobile property, if there is no window that is not covered by other WindowStates, the properties carried in the window property to control the logical screen display parameters will be obtained. These properties are:

  • W.m Attrs. PreferredRefreshRate, w.m Attrs. PreferredDisplayModeId carry the refresh rate;
  • W.m Attrs. Carry screenBrightness window brightness;
  • W.m Attrs. UserActivityTimeout carrying automatic sleep time;

At the same time, the displayHasContent variable is determined by RootWindowContainer#handleNotObscuredLocked(), indicating whether the corresponding window needs to be mirrored to other displays:

// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

    boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) {...if (w.mHasSurface && canBeSeen) {
            ......
            final int type = attrs.type;
            final DisplayContent displayContent = w.getDisplayContent();
            if(displayContent ! =null && displayContent.isDefaultDisplay) {
		// For Dream window or lock screen display, do not display on secondary screen
                if (w.isDreamWindow() || mWmService.mPolicy.isKeyguardShowing()) {
                    mObscureApplicationContentOnSecondaryDisplays = true;
                }
		// The default screen is always true
                displayHasContent = true;
            } else if(displayContent ! =null&& (! mObscureApplicationContentOnSecondaryDisplays || (obscured && type == TYPE_KEYGUARD_DIALOG))) {// Windows that meet this condition will also be displayed on the secondary screen
                displayHasContent = true; }... }return displayHasContent;
    }
Copy the code

Eventually traverse is completed, the value stored in mTmpApplySurfaceChangesTransactionState, and in the last request to DMS for top-level window display parameters, such as high refresh rate, wide, whether the mirror…

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    void applySurfaceChangesTransaction(a) { mTmpApplySurfaceChangesTransactionState.reset(); . mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId, mLastHasContent, mTmpApplySurfaceChangesTransactionState.preferredRefreshRate, mTmpApplySurfaceChangesTransactionState.preferredModeId, mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,true /* inTraversal, must call performTraversalInTrans... below */);
    }
Copy the code

3. Commit the drawn SurfaceControl

The last operation is to perform WindowStateAnimator# commitFinishDrawingLocked () method, in this way, will have the window of the mapped to submit:

// frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
    boolean commitFinishDrawingLocked(a) {...// The drawing state changes to READY_TO_SHOW
        mDrawState = READY_TO_SHOW;
        boolean result = false;
        final ActivityRecord activity = mWin.mActivityRecord;
	// If all windowStates managed by the WindowToken of the WindowState are drawn, the show operation is performed directly
        if (activity == null || activity.canShowWindows()
                || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
            result = mWin.performShowLocked();
        }
        return result;
    }
Copy the code

After this method, the window’s drawing state changes from COMMIT_DRAW_PENDING to READY_TO_SHOW. Entering this state means that the window has been drawn and committed, and the final show operation will be performed after the window of the same WindowToken has been drawn.

The window display process has five states:

  • NO_SURFACE: the default state after creating WindowState, indicating that the current window has not executed the relayout() method to create the Surface;
  • DRAW_PENDING: The state after the Surface is created after the relayout() method is executed.
  • COMMIT_DRAW_PENDING: state after drawing on a window Surface. WindowStateAnimator#finishDrawingLocked() indicates that the drawing is complete and waiting for the next brush frame to commit.
  • READY_TO_SHOW: indicates that the window has been drawn and submitted. If all sibling Windows of this window are drawn and meet the display requirements, the HAS_DRAWN transformation is performed directly. Otherwise, the HAS_DRAWN transformation is performed after other sibling Windows are drawn.
  • HAS_DRAWN: indicates that the window is displayed formally.

At this point, the contents of the mApplySurfaceChangesTransaction all over.

Back in the DisplayContent#app() method, the prepareSurface() method will be executed next.

3.4. PrepareSurfaces () perform the final preparation of all SurfaceControl surfaces

The prepareSurfaces() method does the final preparation for each WindowContainer Surface, setting its position, size, and whether or not it will be displayed on the screen. Submit to SurfaceFlinger for display.

Compared with the previous function interfaces, which are all traversal for WindowState, this method traverses for all WindowContainers, traversing from parent container to child container. The traversal path is shown as follows:

The classes in yellow are those that implement the WindowContainer#prepareSurfaces() method. Here, in order, we look at the prepareSurfaces() methods of several important classes.

1.DisplayContent#prepareSurfaces()

In DisplayContent#prepareSurfaces(), the main effect is to get a Transaction object, mPendingTransaction, and continue iterating through the subcontainer:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    void prepareSurfaces(a) {
        try {
            / / get mPendingTransaction
            final Transaction transaction = getPendingTransaction();
            / / execution WindowContainer# prepareSurfaces ()
            super.prepareSurfaces();
            // Merge mPendingTransaction into sGlobalTransaction
            SurfaceControl.mergeToGlobalTransaction(transaction);
        } finally{}}Copy the code

As you’ll see later, all prepareSurfaces() commit the SurfaceControl to a mPendingTransaction. The mPendingTransaction is then merged into the global Transaction object and submitted to SurfaceFlinger once the traversal is complete.

2.WindowContainer#prepareSurfaces()

In WindowContainer, we provide uniform logic for traversing child containers:

// frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
    void prepareSurfaces(a) {...// Iterate over the child container
        for (int i = 0; i < mChildren.size(); i++) { mChildren.get(i).prepareSurfaces(); }}Copy the code

Therefore, the corresponding methods for the child containers of DisplayContent will then be executed, and so on…… Let’s focus on the implementation in ActivityRecord and Windows State

3.ActivityRecord#prepareSurfaces()

In ActivityRecord, show or hide the mSurfaceControl based on its mVisible property, while iterates through the prepareSurfaces subcontainer:

// frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

    void prepareSurfaces(a) {
	// Whether the Window managed by the ActivityRecord should be visible
        final boolean show = isVisible() || isAnimatingExcluding(PARENTS,
                ANIMATION_TYPE_SCREEN_ROTATION);

        if(mSurfaceControl ! =null) {
            // Show the SurfaceControl
            if(show && ! mLastSurfaceShowing) { getSyncTransaction().show(mSurfaceControl); }else if(! show && mLastSurfaceShowing) {// Show the SurfaceControlgetSyncTransaction().hide(mSurfaceControl); }}...// represents the last prepareSurfaces operation
        mLastSurfaceShowing = show;
        super.prepareSurfaces();	// Continue iterating through the child containers
    }
Copy the code

In this method, we get a Transaction object using the getSyncTransaction() method and show or hide the container’s mSurfaceControl on that Transaction. This Transaction object is actually the DisplayContent#mPendingTransaction object.

For any WindowContainer below DisplayContent, its mSurfaceControl object is not a Surface carrier that directly displays the content, The real vectors are the mSurfaceControl objects managed by ActivityRecord and created during relayout() by WindowSurfaceController, And WindowContainer#mSurfaceControl is the parent of WindowSurfaceController#mSurfaceControl. For SurfaceFlinger, WindowContainer#mSurfaceControl is a ContainerLayer, WindowSurfaceController#mSurfaceControl is the BufferQueueLayer that’s actually drawing.

This is why for Windows State managed by ActivityRecord, its visible state is controlled by ActivityRecord (canBeHiddenByKeyguardLw() first return false).

4.WindState#prepareSurfaces()

The WindowState#prepareSurfaces() method, the most important of all prepareSurfaces() methods, sets the SurfaceControl position, Alpha, Matrix, Crop, and other properties. Here’s the method:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    void prepareSurfaces(a) {
        mIsDimming = false;
	// Apply Dim Layer
        applyDims();
	// Update the Surface location
        updateSurfacePosition();
        updateFrameRateSelectionPriorityIfNeeded();
	// Go to WindowStateAnimator to prepare the actual Surface
        mWinAnimator.prepareSurfaceLocked(true);
        notifyBlastSyncTransaction();
        super.prepareSurfaces();	// Continue with sub-WindowState
    }
Copy the code

1. UpdateSurfacePosition () sets the position

First, we set the SurfaceControl position to updateSurfacePosition(), again using mPendingTransaction:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    void updateSurfacePosition(Transaction t) {
        // convert WindowState#mWindowFrames to Position
        transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
                mSurfacePosition);

        if(! mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate ==null
                && !mLastSurfacePosition.equals(mSurfacePosition)) {
            / / set the positiont.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y); . }}Copy the code

In this method, the SurfaceControl position is first obtained using mWindowFrames. MFrame of WindowState, and then set via Transaction#setPosition(). Therefore, the mFrame property of the WindowFrame object of each window determines where the window should be displayed.

2.WindowStateAnimator#prepareSurfaceLocked()

Next, go to the WindowStateAnimator#prepareSurfaceLocked() method

// frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java

    void prepareSurfaceLocked(final boolean recoveringMemory) {
        finalWindowState w = mWin; .boolean displayed = false;
	// Calculate Alpha and Matrix values
        computeShownFrameLocked();
	// Calculate the margins and clipping regions of the SurfaceControl,......
        setSurfaceBoundariesLocked(recoveringMemory);
        if(mIsWallpaper && ! w.mWallpaperVisible) {// Special processing of wallpaper. }else if(mLastAlpha ! = mShownAlpha || mLastDsDx ! = mDsDx) {if (mIsWallpaper) {// Special processing of wallpaper
                setWallpaperPositionAndScale(
                        mXOffset, mYOffset, mWallpaperScale, recoveringMemory);
            } else {
		// Set the Alpha and matrix values for the SurfaceControl
                prepared =
                    mSurfaceController.prepareToShowInTransaction(mShownAlpha,
                        mDsDx * w.mHScale * mExtraHScale,
                        mDtDx * w.mVScale * mExtraVScale,
                        mDtDy * w.mHScale * mExtraHScale,
                        mDsDy * w.mVScale * mExtraVScale,
                        recoveringMemory);
            }
            // When mDrawState is HAS_DRAWN, the show operation is performed
            if (prepared && mDrawState == HAS_DRAWN) {
                if (mLastHidden) {
                    if (showSurfaceRobustlyLocked()) {
                        markPreservedSurfaceForDestroy();
                        mAnimator.requestRemovalOfReplacedWindows(w);
                        mLastHidden = false; }... }}if (hasSurface()) {
                w.mToken.hasVisible = true; }}else {
            displayed = true; }... }Copy the code

In this method, the final preparation of the SurfaceControl is done using the following four methods:

  • ComputeShownFrameLocked (): Computes the Alpha and Matrix values of the SurfaceControl;
  • SetSurfaceBoundariesLocked () : calculate and set SurfaceControl buffer size, window crop;
  • WindowSurfaceController# prepareToShowInTransaction () : sets the SurfaceControl Alpha and Matrix values;
  • WindowSurfaceController# showSurfaceRobustlyLocked () : to show or hide SurfaceControl;

Here we mainly look at showSurfaceRobustlyLocked () method, when mDrawState status to HAS_DRAWN will execute this method, And finally execute WindowSurfaceController#updateVisibility() to show or hide SurfaceControl:

// frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceController.java

    private boolean updateVisibility(a) {
        if (mHiddenForCrop || mHiddenForOtherReasons) {
            if (mSurfaceShown) {
		// Perform the hide operation
                hideSurface(mTmpTransaction);
                SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
            }
            return false;
        } else {
            if(! mSurfaceShown) {// Perform the show operation
                return showSurface();
            } else {
                return true; }}}Copy the code

When all WindowContainer# prepareSurfaces (), after the completion of DisplayContent# applySurfaceChangesTransaction () most of the logic is performed.

Back to RootWindowContainer# performSurfacePlacementNoTrace () method, through SurfaceControl. CloseTransaction () method to submit the transaction, The SurfaceControl of all shows on this Transaction object will be sent to SurfaceFlinger for further processing, synthesis, and display on the SurfaceFlinger interface. In this approach, the post-transaction flow is skipped.

4. To summarize

This is an analysis of the Surface fixed workflow for all Windows in Windows ManagerService, focusing on some of the logic within the transaction. In the whole process, the relationship between each container and function interface were fully utilized, from DisplayContent to leaf node WindowState, and the placement of all Surface was completed. The whole process can be divided into five stages:

  • 1. After addWindow() is executed, the NO_SURFACE phase enters, indicating that no Surface has been created.
  • Relayout (), which creates the Surface, enters the DRAW_PENDING phase, and returns the Surface to the client View;
  • 3.View starts onMeasure\onDraw() and submits it to WMS via Session#finishedDraw. COMMIT_DRAW_PENDING
  • 4. In the COMMIT_DRAW_PENDING stage, the window is directly transferred to the READY_TO_SHOW stage, which means that the window has been drawn and submitted. At this time, if the siblings of the window are all drawn and meet the display requirements, the HAS_DRAWN transformation is directly displayed.

5. After all Windows of a WindowToken are drawn, HAS_DRAWN indicates that the window is formally displayed.

In the whole process, there are four traversal processes, and different processes perform different operations in each traversal. The following figure summarizes some key points in transaction operations (zoom in) :

The sequence diagram of the whole process is as follows: