In the previous article, the WindowManagerService#addWindow() method for adding Windows was summarized in a major way. A corresponding window management object is created in system_server and placed in the appropriate container. This article summarizes the relayout process for subsequent Windows.

Back in the ViewRootImpl#setView() method in the add window process, the requestLayout() method is called before the mwindowssession #addToDisplayAsUser() method is called, Finally, a VSync listener is set up via the Chreographer object:

// frameworks/base/core/java/android/view/ViewRootImpl.java

    void scheduleTraversals(a) {
        if(! mTraversalScheduled) { mTraversalScheduled =true;
            // Set a synchronization barrier
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // Set VSync listener
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); }}Copy the code

When the VSync signal is received, the doTraversal() method is performed and the layout request is finally made to the WMS via the IWindowSession interface instance in the relayoutWindow() method:

// frameworks/base/core/java/android/view/ViewRootImpl.java

    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {

        int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5 f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5 f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame, mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mTempControls, mSurfaceSize, mBlastSurfaceControl); .return relayoutResult;
    }
Copy the code

You will then go to the WMS#relayoutWindow() method, where the layout work is done. Let’s look at this method in detail.

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

    / * * *@paramSession IWindowSession object used for ViewRootImpl to initiate interactions with WMS *@paramClient IWindow object, which represents the Window of the client and is used by WMS to initiate an interaction with the View wrootimPL *@paramSeq request sequence *@paramAttrs window various parameters and attributes *@paramRequestedWidth Width of the client request window *@paramRequestedHeight Height of the client request window *@paramViewVisibility View *@paramFlags marked *@param frameNumber
     * @paramOutFrame returns the window frame * of the ViewRootImpl@param outContentInsets		
     * @param outVisibleInsets
     * @param outStableInsets
     * @param outBackdropFrame
     * @param outCutout
     * @param mergedConfiguration
     * @paramOutSurfaceControl returns to the ViewRootImpl Surface managed object *@param outInsetsState
     * @param outActiveControls
     * @param outSurfaceSize
     * @param outBLASTSurfaceControl
     * @return* /
    public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags,
            long frameNumber, Rect outFrame, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
            SurfaceControl outBLASTSurfaceControl) {...boolean configChanged;
        synchronized (mGlobalLock) {
            // Get WindowState, DisplayContent, DisplayPolicy, WindowStateAnimator objects
            final WindowState win = windowForClientLocked(session, client, false);
            final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
            WindowStateAnimator winAnimator = win.mWinAnimator;
            // Set WindowState to the size of the window requested from the application
            if(viewVisibility ! = View.GONE) { win.setRequestedSize(requestedWidth, requestedHeight); }// Set the Frame number
            win.setFrameNumber(frameNumber);

            final DisplayContent dc = win.getDisplayContent();
            // If the Configuration update is not performed at this time, try to end the connection animation
            if(! dc.mWaitingForConfig) { win.finishSeamlessRotation(false /* timeout */);
            }
            // Is used to indicate whether the attribute has changed
            int attrChanges = 0;
            int flagChanges = 0;
            int privateFlagChanges = 0;
            int privflagChanges = 0;
            if(attrs ! =null) {
                // Adjust the special type of Windows #attrs attribute
                displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
                // Adjust the Windows #attrs property for the wallpaper window
                win.mToken.adjustWindowParams(win, attrs);
                // Adjust the mSystemUiVisibility property to control the display of the status bar
                if (seq == win.mSeq) {
                    intsystemUiVisibility = attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility; . } win.mSystemUiVisibility = systemUiVisibility; }// PRIVATE_FLAG_PRESERVE_GEOMETRY will ignore the new x, Y, width, and height values and use the old ones
                if((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY) ! =0) {
                    attrs.x = win.mAttrs.x;
                    attrs.y = win.mAttrs.y;
                    attrs.width = win.mAttrs.width;
                    attrs.height = win.mAttrs.height;
                }
		// Check whether the flag has changed
                flagChanges = win.mAttrs.flags ^ attrs.flags;
                privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
                attrChanges = win.mAttrs.copyFrom(attrs);
		// Respond according to whether the flag changes (.... is omitted). }// Set the width and height according to the application request to get the window scaling scale
            win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
            // The visible state of the window
            final int oldVisibility = win.mViewVisibility;
            // Whether the window should change from invisible to visible
            final boolean becameVisible =
                    (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
                            && viewVisibility == View.VISIBLE;
            // Whether to remove the IME window
            booleanimMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) ! =0
                    || becameVisible;
                    MRelayoutCalled is false the first time relayout is executed
            booleanfocusMayChange = win.mViewVisibility ! = viewVisibility || ((flagChanges & FLAG_NOT_FOCUSABLE) ! =0) | | (! win.mRelayoutCalled);// If the window's visibility changes and the window is allowed to appear on top of the wallpaper, the wallpaper window is processed
            booleanwallpaperMayMove = win.mViewVisibility ! = viewVisibility && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) ! =0; wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) ! =0;
            
            win.mRelayoutCalled = true;		// set WindowState#mRelayoutCalled to true
            win.mInRelayout = true;			// set WindowState#mInRelayout to true, which means that during relayout, after relayout is complete, it is reset to false
            // Update window visibility
            win.setViewVisibility(viewVisibility);
            // Notify DisplayContent that the layout needs to be rearranged
            win.setDisplayLayoutNeeded();
            // Indicates whether Inset is waiting to be setwin.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) ! =0;

            // If the window is visible, rearrange it
            final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                    (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                            || win.mActivityRecord.isClientVisible());
            // Perform a refresh operation
            mWindowPlacerLocked.performSurfacePlacement(true /* force */);
			
            // Whether the relayout operation is required
            if (shouldRelayout) {
                / / layout
                result = win.relayoutVisibleWindow(result, attrChanges);
                try {
                    / / create a Surface
                    result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,
                            result, win, winAnimator);
                } catch (Exception e) {
                    return 0;
                }
		// If this is the first relayout operation, focusMayChange is required
                if((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) ! =0) {
                    focusMayChange = true;
                }
		// Special processing of input window
                if (win.mAttrs.type == TYPE_INPUT_METHOD
                        && displayContent.mInputMethodWindow == null) {
                    displayContent.setInputMethodWindowLocked(win);
                    imMayMove = true; }}else{... }/ / update the FocusWindow
            if (focusMayChange) {
                if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
                    imMayMove = false; }}// This is the first update
            booleantoBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) ! =0;
            // Update the screen orientation
            configChanged = displayContent.updateOrientation();
            // Special treatment for wallpaper window, update offset
            if (toBeDisplayed && win.mIsWallpaper) {
                displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
            }
            if(win.mActivityRecord ! =null) { win.mActivityRecord.updateReportedVisibilityLocked(); }...// Update mergedConfiguration object
            if (shouldRelayout) {
                win.getMergedConfiguration(mergedConfiguration);
            } else {
                win.getLastReportedMergedConfiguration(mergedConfiguration);
            }
            // Set various insets and DisplayCutout
            win.getCompatFrame(outFrame);
            win.getInsetsForRelayout(outContentInsets, outVisibleInsets,
                    outStableInsets);
            outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
            outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
            outInsetsState.set(win.getInsetsState(), win.isClientLocal());

            // Update the relayout flag to indicate that touch events are acceptable
            result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;

            win.mInRelayout = false;		// Reset to false to indicate that relayout is complete
            // Update the global configuration when the configuration changes
            if (configChanged) {
                displayContent.sendNewConfiguration();
            }
            / / set outSurfaceSize
            if(winAnimator.mSurfaceController ! =null) {
                outSurfaceSize.set(winAnimator.mSurfaceController.getWidth(),
                                         winAnimator.mSurfaceController.getHeight());
            }
            getInsetsSourceControls(win, outActiveControls);
        }

        return result;
    }
Copy the code

After entering this method, the corresponding WindowState object is first obtained through the IWindow object, and then various operations are performed with the WindowState as the body. It’s a long method, but it basically has the following steps:

  1. WindowState#setRequestedSize() sets the width and height of WindowState;
  2. DisplayPolicy#adjustWindowParamsLw() AdjustWindow properties;
  3. WindowState#setWindowScale() sets the scale;
  4. WindowState#setViewVisibility() updates visibility properties;
  5. WindowState#relayoutVisibleWindow() handles the FLAG_TURN_SCREEN_ON flag;
  6. WMS#createSurfaceControl() creates the Surface;
  7. Update the focus window WMS# updateFocusedWindowLocked ();
  8. Set the MergedConfiguration object back to the client;
  9. Set windowFrame and Inset to return to client;

Each of the above steps is analyzed and summarized below.

1.WindowState#setRequestedSize() sets the width and height of WindowState

With the WindowState#setRequestedSize() method, set the mRequestedWidth and mRequestedHeight properties of the WindowState object, which represent the width and height set from the application request:

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

    void setRequestedSize(int requestedWidth, int requestedHeight) {
        if((mRequestedWidth ! = requestedWidth || mRequestedHeight ! = requestedHeight)) { mLayoutNeeded =true; mRequestedWidth = requestedWidth; mRequestedHeight = requestedHeight; }}Copy the code

2.DisplayPolicy#adjustWindowParamsLw() AdjustWindow properties

AdjustWindowParamsLw (Win, AdjustS, PID, UID) DisplayPolicy#adjustWindowParamsLw(AdjustS, PID, UID) AdjustWindowParams () is used in WallpaperWindowToken#adjustWindowParams().

If the layout parameters are changed after adjustment, the related status is processed. This part of the code is skipped.

3.WindowState#setWindowScale() sets the zoom scale

With the WindowState#setWindowScale() method, set the mHScale and mVScale properties of the WindowState object, which represent the scale of the window in both directions:

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

    void setWindowScale(int requestedWidth, int requestedHeight) {
        final booleanscaledWindow = (mAttrs.flags & FLAG_SCALED) ! =0;
        if(scaledWindow) { mHScale = (mAttrs.width ! = requestedWidth) ? (mattrs.width/(fl-oAT)requestedWidth) :1.0 f; mVScale = (mAttrs.height ! = requestedHeight) ? (mAttrs.height / (float)requestedHeight) : 1.0 f;
        } else {
            mHScale = mVScale = 1; }}Copy the code

As you can see, the scaling is calculated only for Windows with the FLAG_SCALED flag set in the layout parameter, otherwise it is 1.

4.WindowState#setViewVisibility() updates visibility

Use the WindowState#setViewVisibility() method to set the WindowState object’s mViewVisibility property, which represents the visibility of the Window:

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

    void setViewVisibility(int viewVisibility) { mViewVisibility = viewVisibility; . }Copy the code

MViewVisibility has three values, all from the View on the APP side:

  • View.VISIBLE: indicates that the window is VISIBLE.
  • View.INVISIBLE: Indicates that the window is INVISIBLE, but still occupies space, and its Surface still exists;
  • View.GONE: Indicates that the window is not visible, no longer occupies space, and its Surface is destroyed.

5. WindowState# relayoutVisibleWindow FLAG_TURN_SCREEN_ON () processing

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java int relayoutVisibleWindow(int result, Int attrChanges) {final Boolean wasVisible = isVisibleLw(); / / if not visible, and for the first time said to relayout result | = (! wasVisible || ! isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0; If (mAnimatingExit) {.... } // If (mDestroying) {mDestroying = false; mWmService.mDestroySurface.remove(this); } if (! WasVisible) {/ / said the next step into the animation mWinAnimator mEnterAnimationPending = true; } / / the last visible screen direction mLastVisibleLayoutRotation = getDisplayContent () getRotation (); . / / said implemented into the animation mWinAnimator mEnteringAnimation = true; try { prepareWindowToDisplayDuringRelayout(wasVisible); } finally { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } // If ((attrChanges & FORMAT_CHANGED)! = 0) {... } // If (isDragResizeChanged()) {...... when drag resize occurs }... return result; }Copy the code

In this method, called prepareWindowToDisplayDuringRelayout () method, in view of the bright screen flag for processing:

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

    void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
	// Whether there is a flag to light up the screen
        final booleanhasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) ! =0|| (mActivityRecord ! =null && mActivityRecord.canTurnScreenOn());
        if (hasTurnScreenOnFlag) {
			......
            if(allowTheaterMode && canTurnScreenOn && (mWmService.mAtmInternal.isDreaming() || ! mPowerManagerWrapper.isInteractive())) { mPowerManagerWrapper.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION,"android.server.wm:SCREEN_ON_FLAG");
            }

            if(mActivityRecord ! =null) {
                mActivityRecord.setCurrentLaunchCanTurnScreenOn(false); }}if (wasVisible) {
            return; }... }Copy the code

In the preceding method, if the layout parameters or the corresponding ActivityRecord contain flags related to screen lighting, the screen is lit. The window is highlighted by FLAG_TURN_SCREEN_ON, which is where it is triggered.

6. WMS# createSurfaceControl () to create a Surface

In the client ViewRootImpl, there is a Surface object that is the final renderer of the View display content in the ViewRootImpl. But when it was created, it was just an empty shell with no content. When relayout(), the ViewRootImpl passes the SurfaceControl object to the WMS, which creates a SurfaceControl object and copies it to the incoming SurfaceControl. Finally the ViewRootImpl retrieves the content from the SurfaceControl:

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

    private int createSurfaceControl(SurfaceControl outSurfaceControl,
            SurfaceControl outBLASTSurfaceControl, int result,
            WindowState win, WindowStateAnimator winAnimator) {
	// The WindowState does not correspond to Surface yet
        if(! win.mHasSurface) { result |= RELAYOUT_RES_SURFACE_CHANGED; } WindowSurfaceController surfaceController;try {
            Create a WindowSurfaceController object
            surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
        } finally{}if(surfaceController ! =null) {
            // Copy the mSurfaceControl from WindowSurfaceController to outSurfaceControl
            surfaceController.getSurfaceControl(outSurfaceControl);
            surfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl);
        } 

        return result;
    }
Copy the code

Among the above methods:

  • First set the result variableRELAYOUT_RES_SURFACE_CHANGEDFlags that represent the second phase of the Relayout process;
  • The WindowSurfaceController object is then created with WindowStateAnimator#createSurfaceLocked();
  • Finally, the WindowSurfaceController copies the internal SurfaceControl to the outSurfaceControl, which is returned to the client’s ViewRootImpl.

The WindowStateAnimator object is used to perform and manage WindowState animations and Surface operations, WindowSurfaceController manages SurfaceControl objects for WindowStateAnimator objects.

Create a WindowSurfaceController object like this:

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

    WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
        final WindowState w = mWin;
	// If it already exists, return it
        if(mSurfaceController ! =null) {
            return mSurfaceController;
        }
	// set the WindowState#mHasSurface property to false to indicate that there is no Surface
        w.setHasSurface(false);
	// Reset the drawing state
        resetDrawState();
	// 
        int flags = SurfaceControl.HIDDEN;
        final WindowManager.LayoutParams attrs = w.mAttrs;
	// Calculate the size of the Surface area. When relayout, the area is 0 because no Frame is filled
        calculateSurfaceBounds(w, attrs, mTmpSize);

        try{...// Create WindowSurfaceController objects
            mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), width,
                    height, format, flags, this, windowType, ownerUid);
            mSurfaceFormat = format;
            // set the WindowState#mHasSurface property to false to indicate that there is no Surface
            w.setHasSurface(true);
        } catch (OutOfResourcesException e) {
        }
        mLastHidden = true;

        return mSurfaceController;
    }
Copy the code

When a WindowSurfaceController object is created, the corresponding SurfaceControl object is created:

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

    WindowSurfaceController(String name, int w, int h, int format,
            int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
        mAnimator = animator;			/ / WindowStateAnimator object
        mSurfaceW = w;					/ / Surface width, create to 1, when the setBufferSizeInTransaction () will be set specific values
        mSurfaceH = h;					// The height of the Surface, created as 1
        title = name;					/ / the window

        mService = animator.mService;	/ / WMS object
        final WindowState win = animator.mWin;	/ / WindowState object
        mWindowType = windowType;				/ / Window types
        mWindowSession = win.mSession;			/ / IWindowSession object
        mTmpTransaction = mService.mTransactionFactory.get();
		/ / create SurfaceControl
        final SurfaceControl.Builder b = win.makeSurface()
                .setParent(win.getSurfaceControl())
                .setName(name)
                .setBufferSize(w, h)
                .setFormat(format)
                .setFlags(flags)
                .setMetadata(METADATA_WINDOW_TYPE, windowType)
                .setMetadata(METADATA_OWNER_UID, ownerUid)
                .setCallsite("WindowSurfaceController");
        mSurfaceControl = b.build();
    }
Copy the code

After creating the WindowSurfaceController object, set the WindowState#mHasSurface property to true via WindowState#setHasSurface(), Indicates that the WindowState already owns Surface.

7. WMS# updateFocusedWindowLocked () to update the focus window

Next, through WMS# updateFocusedWindowLocked () method, focus on the update of the window.

8. Set the MergedConfiguration object to the client

The MergedConfiguration object is a wrapper for both the global Configuration object and the Override Configurationu object, which implements the Parcelable object. Therefore, it can be used for interprocess transfer, and its main function is that WMS synchronizes the Configuration to ViewRootImpl. When relayoutWindow(), the current Global configuration and Override Configuration are assigned to MergedConfiguration and a new Configuration object is created:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java void GetMergedConfiguration (MergedConfiguration outConfiguration) {// Obtain the global Configuration object final Configuration globaluConfig = getProcessGlobalConfiguration(); / / access to cover the Configuration object final Configuration overrideConfig = getMergedOverrideConfiguration (); / / set to MergedConfiguration outConfiguration. SetConfiguration (globalConfig overrideConfig); }Copy the code

9. Set windowFrame and Inset to the client

The WindowFrame class in WMS carries Rect objects that affect how Windows are laid out. Some of the more important Rect objects are:

  • MDisplayFrame: from the DisplayFrame#mUnrestricted property, represents the entire ActivityStack screen area on which the window is located, generally equal to the logical screen size;
  • MParentFrame: comes from mDisplayFrame, except for special Windows such as input methods and wallpaper. In most cases, it is equal to mDisplayFrame.
  • MContainingFrame: mParentFrame for full-screen Windows;

These recTs are set in the DisplayPolicy#layoutWindowLw() method, as described in the Surface placement process.

This will set WindowFrame#mCompatFrame and several other Rect objects to variables passed in the ViewRootImpl, which will output to the ViewRootImpl:

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

    void getCompatFrame(Rect outFrame) {
        outFrame.set(mWindowFrames.mCompatFrame);
    }

    void getInsetsForRelayout(Rect outContentInsets, Rect outVisibleInsets, Rect outStableInsets) {
        outContentInsets.set(mWindowFrames.mContentInsets);
        outVisibleInsets.set(mWindowFrames.mVisibleInsets);
        outStableInsets.set(mWindowFrames.mStableInsets);

        mLastRelayoutContentInsets.set(mWindowFrames.mContentInsets);
    }
Copy the code

10. Summary

  1. Update the size, visibility and other related properties of the corresponding WindowState through relayout operation;
  2. In the first relayout, a Surface will be created, and the size and other properties of the Surface will be set, and returned to the client as the carrier of the View to display the content.
  3. To create a Surface, use the WindowSurfaceController in WindowStateAnimator.

After the Surface is created, it is returned to the client. The client will draw the specific content on the Surface and notify WMS when it is completed. WMS will finally send the Surface to SurfaceFlinger via the WindowSurfacePlacer#performSurfacePlacement() method to complete the display.

The sequence diagram of the whole process is as follows: