Android system startup series Android in-depth four components of the Android application process startup process series Android parsing WindowManager series

preface

In the previous article in this series, we studied the birth of WMS. What are the important members of WMS after it is created? What does the WMS part of the Window add process do? This article will give you the answer.

1. An important member of WMS

The important members of WMS are the important member variables in WMS, as shown below. frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    final WindowManagerPolicy mPolicy;
    final IActivityManager mActivityManager;
    final ActivityManagerInternal mAmInternal;
    final AppOpsManager mAppOps;
    finalDisplaySettings mDisplaySettings; .final ArraySet<Session> mSessions = new ArraySet<>();
    final WindowHashMap mWindowMap = new WindowHashMap();
    final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
    final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>();
    final ArrayList<AppWindowToken> mWindowReplacementTimeouts = new ArrayList<>();
    final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
    final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
    WindowState[] mPendingRemoveTmp = new WindowState[20];
    final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
    final ArrayList<WindowState> mDestroyPreservedSurface = newArrayList<>(); .final H mH = newH(); .finalWindowAnimator mAnimator; .final InputManagerService mInputManagerCopy the code

Some of the WMS member variables are listed here, and they are briefly described below.

MPolicy: WindowManagerPolicy WindowManagerPolicy (WMP) type variable. WindowManagerPolicy is a window management policy interface class that defines a general specification for a window policy to follow and provides all specific UI behavior for WindowManager. Its concrete implementation class is PhoneWindowManager, which is created when WMS is created. WMP allows you to customize window hierarchies and special window types as well as key scheduling and layout.

MSessions: ArraySet A variable of type ArraySet. The element type is Session. It is mainly used for inter-process communication. Other application processes need to go through the Session to communicate with the WMS process. There is also a Session for each application process, and WMS stores these sessions to keep track of all clients that submit window management services to WMS. WindowHashMap: WindowHashMap type WindowHashMap. WindowHashMap inherits the HashMap, which limits the key value of the HashMap to IBinder and the value to WindowState. WindowState is used to hold information about a window, and in WMS it is used to describe a window. To sum up, mWindowMap is used to store the collection of various Windows in WMS.

MFinishedStarting: ArrayList A variable of type ArrayList, element type AppWindowToken, which is a subclass of WindowToken. To understand what mFinishedStarting means, you need to understand what Windows Token is. WindowToken has two main functions:

  • When an application requests WMS to create a new window, it needs to present a valid Windows Token to WMS. AppWindowToken, as a subclass of WindowToken, is mainly used to describe the WindowToken structure of an application. Each Activity in an application corresponds to an AppWindowToken.
  • WindowToken aggregates Windows states from the same component, such as Acitivity, for easy management.

MFinishedStarting is a list of AppWindowTokens used to store application Windows (such as Acitivity) that have been started. In addition to mFinishedStarting, as well as similar mFinishedEarlyAnim and mWindowReplacementTimeouts, Where mFinishedEarlyAnim stores the AppWindowToken that has completed the window drawing and does not need to show any saved Surface application Windows. MWindowReplacementTimeout storage AppWindowToken waiting for replacement of the application window, if change not in time, the old window will need to be processed.

MResizingWindows: ArrayList Variable of type ArrayList, element type WindowState. MResizingWindows is used to store a list of Windows that are being resized. Similar to mResizingWindows are mPendingRemove, mDestroySurface, and mDestroyPreservedSurface. MPendingRemove is set when memory runs out and contains Windows that need to be forcibly removed. MDestroySurface contains the Surface that needs to be destroyed. MDestroyPreservedSurface contains surfaces that Windows need to save to be destroyed. Why should Windows save these surfaces? This is because when a window undergoes a Surface change, it needs to keep the old Surface until the first frame of the new Surface is drawn.

WindowAnimator a WindowAnimator type of variable used to manage window animations and special effects animations.

MH: a variable of type H, the Handler class of the system, used to add tasks to the message queue of the main thread so that the code logic is executed on the main thread.

MInputManager: InputManagerService A variable of type InputManagerService entered to the system manager. The InputManagerService (IMS) handles touch events. It looks for the most appropriate window to handle the touch feedback. WMS is the window manager, so it is “natural” that WMS should be a hub for input to the system.

2.Window adding process (WMS section)

We know that the Window operation is divided into two parts, one part is the WindowManager processing part and the other part is the WMS processing part, as shown below.



Android parse WindowManager (3) Windows add process






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

Part1 addWindow method

 public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {

        int[] appOp = new int[1];
        int res = mPolicy.checkAddPermission(attrs, appOp);/ / 1
        if(res ! = WindowManagerGlobal.ADD_OKAY) {returnres; }...synchronized(mWindowMap) {
            if(! mDisplayReady) {throw new IllegalStateException("Display has not been initialialized");
            }
            final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);/ / 2
            if (displayContent == null) {
                Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
                        + displayId + ". Aborting.");
                returnWindowManagerGlobal.ADD_INVALID_DISPLAY; }...if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {/ / 3
                parentWindow = windowForClientLocked(null, attrs.token, false);/ / 4
                if (parentWindow == null) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
                          + attrs.token + ". Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
                            + attrs.token + ". Aborting.");
                    returnWindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; }}... }... }Copy the code

WMS addWindow returns various states of addWindow, such as adding a Window successfully, invalid display, and so on, which are defined in Windows ManagerGlobal. Note 1 calls WMP’s checkAddPermission method to check the permission according to the property of Window. The specific implementation is in PhoneWindowManager’s checkAddPermission method. If there is no permission, the subsequent code logic will not be executed. In comment 2, the displayId is used to obtain the DisplayContent to which the window should be added. If the DisplayContent is not found, WindowManagerGlobal.ADD_INVALID_DISPLAY is returned. DisplayContent is used to describe a screen. In comment 3, type represents the type of a window and its value is between FIRST_SUB_WINDOW and LAST_SUB_WINDOW (1000~1999). This value is defined in WindowManager, indicating that the window is a child window. If you don’t understand the range of Window types, please read Android parsing WindowManager (2) Window properties. In note 4, attrs.token is an object of type IBinder, and The windowForClientLocked methods internally get the parent window of the child window from mWindowMap based on attrs.token as the key value. The parent window is then checked. If the parent window is null or the value range of type is incorrect, an error status will be returned.

Part2 addWindow method

. AppWindowToken atoken =null;
            final booleanhasParent = parentWindow ! =null;
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);/ / 1
            final int rootType = hasParent ? parentWindow.mAttrs.type : type;/ / 2
            boolean addToastWindowRequiresToken = false;

            if (token == null) {
                if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
                    Slog.w(TAG_WM, "Attempted to add application window with unknown token "
                          + attrs.token + ". Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_INPUT_METHOD) {
                    Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
                          + attrs.token + ". Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_VOICE_INTERACTION) {
                    Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
                          + attrs.token + ". Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_WALLPAPER) {
                    Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
                          + attrs.token + ". Aborting.");
                    returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }...if (type == TYPE_TOAST) {
                    // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
                    if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
                            parentWindow)) {
                        Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
                                + attrs.token + ". Aborting.");
                        returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}finalIBinder binder = attrs.token ! =null ? attrs.token : client.asBinder();
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow);/ / 3
            } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {/ / 4
                atoken = token.asAppWindowToken();/ / 5
                if (atoken == null) {
                    Slog.w(TAG_WM, "Attempted to add window with non-application token "
                          + token + ". Aborting.");
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } else if (atoken.removed) {
                    Slog.w(TAG_WM, "Attempted to add window with exiting application token "
                          + token + ". Aborting.");
                    returnWindowManagerGlobal.ADD_APP_EXITING; }}else if (rootType == TYPE_INPUT_METHOD) {
                if(token.windowType ! = TYPE_INPUT_METHOD) { Slog.w(TAG_WM,"Attempted to add input method window with bad token "
                            + attrs.token + ". Aborting.");
                      returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}...Copy the code

The WindowToken is obtained in comment 1 through the getWindowToken method of displayContent. In comment 2, the type value of the parent window is assigned to rootType if there is a parent window, and the type value of the current window is not assigned to rootType. Next, if WindowToken is null, distinguish between the values of rootType or type. If rootType is equal to the value of TYPE_INPUT_METHOD and TYPE_WALLPAPER, ADD_BAD_APP_TOKEN indicates that WindowManagerGlobal.ADD_BAD_APP_TOKEN is not allowed to be null when the rootType value is equal to the value of TYPE_INPUT_METHOD and TYPE_WALLPAPER. After several conditional filters, WindowToken will be implicitly created in comment 3, which indicates that when we add Windows, we can not provide WindowToken to WMS, provided that the values of rootType and Type are not the values filtered by the previous conditional filters. A distinction must be made between implicit and explicit creation of a WindowToken, as the fourth parameter in comment 3 is false to indicate that the WindowToken was created implicitly. The following code logic is WindowToken is not null, according to the value of rootType and type, such as in comment 4 to determine if the window is an application window, Note 5 converts a WindowToken to an AppWindowToken specific to the application window, and then makes subsequent judgments based on the value of the AppWindowToken.

Part3 addWindow method

.final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);/ / 1
            if (win.mDeathRecipient == null) {/ / 2
                // Client has apparently died, so there is no reason to
                // continue.
                Slog.w(TAG_WM, "Adding window client " + client.asBinder()
                        + " that is dead, aborting.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            }

            if (win.getDisplayContent() == null) {/ / 3
                Slog.w(TAG_WM, "Adding window to Display that has been removed.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

            mPolicy.adjustWindowParamsLw(win.mAttrs);/ / 4
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
            res = mPolicy.prepareAddWindowLw(win, attrs);/ / 5. win.attach(); mWindowMap.put(client.asBinder(), win);/ / 6
            if(win.mAppOp ! = AppOpsManager.OP_NONE) {int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
                        win.getOwningPackage());
                if((startOpResult ! = AppOpsManager.MODE_ALLOWED) && (startOpResult ! = AppOpsManager.MODE_DEFAULT)) { win.setAppOpVisibilityLw(false); }}final AppWindowToken aToken = token.asAppWindowToken();
            if(type == TYPE_APPLICATION_STARTING && aToken ! =null) {
                aToken.startingWindow = win;
                if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
                        + " startingWindow=" + win);
            }

            boolean imMayMove = true;
            win.mToken.addWindow(win);/ / 7
             if (type == TYPE_INPUT_METHOD) {
                win.mGivenInsetsPending = true;
                setInputMethodWindowLocked(win);
                imMayMove = false;
            } else if (type == TYPE_INPUT_METHOD_DIALOG) {
                displayContent.computeImeTarget(true /* updateImeTarget */);
                imMayMove = false;
            } else {
                if (type == TYPE_WALLPAPER) {
                    displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if((attrs.flags&FLAG_SHOW_WALLPAPER) ! =0) {
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if(displayContent.mWallpaperController.isBelowWallpaperTarget(win)) { displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; }}...Copy the code

WindowState is created in comment 1, which holds all the state information for the window, and represents a window in WMS. WindowState contains WMS, Session, WindowToken, parent’s WindowState, LayoutParams, etc. Next, in comments 2 and 3, determine whether the client requesting the window is dead and whether the window’s DisplayContent is null, if so, the following code logic will not be executed. AdjustWindowParamsLw in PhoneWindowManager (adjustWindowParamsLw), adjustWindowParamsLw (adjustWindowParamsLw), adjustWindowParamsLw (adjustWindowParamsLw), adjustWindowParamsLw (adjustWindowParamsLw), adjustWindowParamsLw WMP’s prepareAddWindowLw method is called in comment 5 to prepare the window to be added to the system. Add WindowState to mWindowMap at comment 6. Note 7 adds WindowState to the Corresponding WindowToken (which is actually stored in WindowToken’s parent class, WindowContainer) so that WindowToken contains The WindowState of the same component.

AddWindow method summary

The addWindow method is explained in three parts, which do four things:

  1. The window to be added is checked, and if the window does not meet some criteria, the following code logic is not executed.
  2. Windowtoken-related processing, for example, some window types need to provide a WindowToken, without which the following code logic will not be executed, and some window types need to implicitly create a WindowToken by WMS.
  3. Creation and related processing of WindowState, associating WindowToken with WindowState.
  4. Create and configure DisplayContent in preparation for adding Windows to the system.

conclusion

In this article, we first learned about the key members of WMS, which will facilitate further analysis of WMS. Next, we learned the WMS part of the process of adding Windows, and divided the addWindow method into three parts to explain. From the addWindow method, we learned that WMS has three important classes: WindowToken, WindowState and DisplayContent. They will be covered in future articles in this series.

Resources: In-depth Understanding of Android Volume III