preface

In this article we will continue to analyze the manager of Windows Manager while the iron is hot. Windows ManagerService (WMS) is not only the manager of Windows Manager. It also has many important responsibilities. This article will analyze the responsibilities of WMS, the creation process of WMS, the members of WMS, and the operation of Adding and removing Windows from WMS. Make sure you are familiar with Windows Manager before reading this article. If you do not understand please first look at Android 8.0 source analysis (nine) WindowManager content.

Android 8.0 source code analysis (a) SystemServer process started

Android 8.0 source code analysis (ii) Launcher

Android 8.0 source code analysis (three) application process creation to the application startup process

Android 8.0 source code analysis (four) Activity start

Android 8.0 source code analysis (5) Service startup

Android 8.0 source code analysis (6) BroadcastReceiver launch

Android 8.0 source code analysis (seven) ContentProvider start

ActivityManagerService

Android 8.0 source code analysis (nine) WindowManager

Android 8.0 source code analysis (ten) WindowManagerService window management

The responsibilities of WMS

Many developers know that WMS is an important service in Android. It is the manager of Windows Manager. WMS is an important knowledge point for both application development and Framework development because WMS has many responsibilities. Each responsibility involves an important and complex system, making THE WMS like a traffic light at an intersection without which traffic can’t flow. The responsibilities of WMS are briefly described as follows.

1. Window management

The WMS is the window manager, responsible for starting, adding, and deleting Windows. The size and hierarchy of Windows are also managed by WMS. The core members of window management are DisplayContent, WindowToken, and WindowState.

2. Window animation

The use of animations for window-switching can be more window-switching, which is done by WMS’s animation subsystem, which is managed by the WindowAnimator.

3. Enter the system transfer station

Touch events are generated by touching a window. InputManagerService(IMS) processes the touch events by looking for the most appropriate window to process the touch feedback. WMS is the window manager, and it is a perfect hub for the input system.

4. Surface management

Windows do not have drawing function, so each window needs a Surface to draw, and WMS is responsible for assigning Surface to each window.

Summary:

The responsibilities of WMS can be summarized as follows:

From the figure above, we know that WMS is very complex, and it is associated with window management, window animation, input system transfer station and Surface management, each of them is an important and complex system, this chapter will analyze the window management, because it is really closely related to our application development.

WMS creation process

Before introducing WMS, it is important to know when WMS was created. If you know SystemServer, WMS is started in a system process. SystemServer process start inside have a detailed introduction, below we directly look at the SystemServer entry main method, the code is as follows:

//SystemServer.java
    /** * The main function here is mainly called by zygote via reflection */
    public static void main(String[] args) {
        new SystemServer().run();
    }
Copy the code

SystemServer run ();

//SystemServer.java
    private void run(a) {
        try{.../** * Create a message for the main thread Looper */

            Looper.prepareMainLooper();

            // Initialize native services.
            /** * 1. Load libandroid_servers. so */
            System.loadLibrary("android_servers"); ./** * Create system-level Context */
            createSystemContext();

            /** * 2. Create SystemServiceManager. It creates, starts, and manages the lifecycle of system services */
            mSystemServiceManager = new SystemServiceManager(mSystemContext);
            mSystemServiceManager.setRuntimeRestarted(mRuntimeRestart);
            LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
            // Prepare the thread pool for init tasks that can be parallelized
            SystemServerInitThreadPool.get();
        } finally {
            traceEnd();  // InitBeforeStartServices
        }

        // Start services.
        try {
            traceBeginAndSlog("StartServices");
            /** * 3. SystemServiceManager starts AMS, PowerMS, and PackageMS services */
            startBootstrapServices();
            /** * 4. Start DropBoxManagerService, BatteryService, UsageStatsService, and WebViewUpdateService */
            startCoreServices();
            /** * 5. CameraService, AlarmManagerService, and VrManagerService are enabled. * /
            startOtherServices();
            SystemServerInitThreadPool.shutdown();
        } catch (Throwable ex) {
          ...
        } finally{ traceEnd(); }... }Copy the code

If you have seen the Android 8.0 source code, you should know that the main function here, we have met many times, the above comment is also clear, so we go directly to the WMS initialization place, please see the 5 startOtherServices method, the code is as follows:

//SystemServer.java
    private void startOtherServices(a) {.../** * 1. Get Watchdog, which monitors the health of some key services of the system */
            final Watchdog watchdog = Watchdog.getInstance();
            /** * 2. Do some initialization for the Watchdog */
            watchdog.init(context, mActivityManagerService);
            traceEnd();

            traceBeginAndSlog("StartInputManagerService");
            /** * 3. Create the IMS service and assign the value to inputManager */
            inputManager = new InputManagerService(context);
            traceEnd();

            traceBeginAndSlog("StartWindowManagerService");
            // WMS needs sensor service ready
            ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
            mSensorServiceStart = null;
            /** * 4. Execute the WMS main method, which internally initializes WMS */wm = WindowManagerService.main(context, inputManager, mFactoryTestMode ! = FactoryTest.FACTORY_TEST_LOW_LEVEL, ! mFirstBoot, mOnlyCore,new PhoneWindowManager());
            /** * 5. Register the WMS with the ServiceManager */
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
            /** * 6. Register the IMS with the ServiceManager */ServiceManager.addService(Context.INPUT_SERVICE, inputManager); traceEnd(); .try {
            /** * 7. Initialize the screen display */
            wm.displayReady();
        } catch(Throwable e) { ... }...try {
            /** * 8. To inform WMS that system initialization is complete, it internally calls the WindowManagerPolicy systemReady method */
            wm.systemReady();
        } catch(Throwable e) { ... }... }Copy the code

The startOtherServices method is mainly used to startOtherServices in the system, about 100 services are started, the above code is only listed with the core code related to this chapter, we directly read the comments 1 and 2, respectively, get Watchdog and initialize it. Watchdog is used to monitor the running status of some key services in the system. Note 3 creates the IMS, note 4 executes the main method of the WMS, which creates the WMS internally, and note 5 and 6 register the IMS and WMS with the ServiceManager, so that if a client wants to use THE WMS, You need to go to the ServiceManager to query information, and then establish a communication link with the WMS process, the client can use THE WMS. Note 7 is used to initialize the screen display information, and note 8 is used to notify WMS that the initialization is complete and that the WindowManagerPolicy systemReady method is called internally. Since this section focuses on WMS startup, let’s go straight to comment 4 and see its invocation as follows:

//WMS.java
    public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
            WindowManagerPolicy policy) {
        /** * 1. Get the handler instance in DisplayThead, call handler's runWithScissors method, use it to handle operations that require low latency display. And * Fast operations can only be performed in real time by Windows Manager, DisplayManager and InputManager */
        DisplayThread.getHandler().runWithScissors(() ->
                /** * 2. Create a WMS instance and run it in Runnable */
                sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                        onlyCore, policy), 0);
        return sInstance;
    }
Copy the code

The code above is a Java8 feature that uses a lambda expression, takes the “android.display “Handler object in the comment, calls the runWithScissors method, It is used to perform low-latency operations that can only be performed in real time by WindowManager, DisplayManager, and InputManager. Note 2 creates WMS instances. The runWithScissors function of the Handler is unfamiliar. We will cover it later. Note that the second parameter timeout passes 0. Mobile architecture (two) Android Handler architecture analysis, and to achieve their own simple version of the Handler framework code is as follows:

//Handler.java
    public final boolean runWithScissors(final Runnable r, long timeout) {
        if (r == null) {
            throw new IllegalArgumentException("runnable must not be null");
        }
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout must be non-negative");
        }

        /** * 1. Check whether the current thread is the thread pointed to by Handler */
        if (Looper.myLooper() == mLooper) {
            r.run();
            return true;
        }
        /** * r: external Runnable */
        BlockingRunnable br = new BlockingRunnable(r);
        return br.postAndWait(this, timeout);
    }
Copy the code

The above code determines whether the Runnable passed in is null and whether the delay time is less than zero, and throws an exception if it is not. Dispaly (android.dispaly, android.dispaly, android.dispaly, android.dispaly); If not, call BlockingRunnable’s postAndWait method and pass in the current Handler and delay time. BlockingRunnable is an inner class of this Handler.

//Handler.java

    private static final class BlockingRunnable implements Runnable {
        private final Runnable mTask;
        private boolean mDone;

        public BlockingRunnable(Runnable task) {
            mTask = task;
        }

        @Override
        public void run(a) {
            try {
                / * * * 1 * /
                mTask.run();
            } finally {
                synchronized (this) {
                    mDone = true; notifyAll(); }}}public boolean postAndWait(Handler handler, long timeout) {
            /** Adds the current BlockingRunnable to the Handler's task queue. * 2. * /
            if(! handler.post(this)) {
                return false;
            }

            synchronized (this) {
                if (timeout > 0) {
                    final long expirationTime = SystemClock.uptimeMillis() + timeout;
                    while(! mDone) {long delay = expirationTime - SystemClock.uptimeMillis();
                        if (delay <= 0) {
                            return false; // timeout
                        }
                        try {

                            wait(delay);
                        } catch (InterruptedException ex) {
                        }
                    }
                } else {
                    while(! mDone) {try {
                            / * * * 3. * /
                            wait();
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            }
            return true; }}Copy the code

Add the current BlockingRunnable object to the Handler task queue in comment 2 based on the code above. The second argument to the runWithScissors method was passed 0, so timeout > 0 is not valid. If mDone is false, comment 3 is executed to put the current thread into wait state. Which thread is waiting? Looking up, we execute the Runnable run method passed in at comment 1 (running on the Android.display thread), set mDone to true, and call notifyAll to wake up the waiting thread. This will not continue to call the wait method at comment 3, so we conclude that the SystemServer thread is waiting for the Android.display thread, Until the android.display thread finishes executing SystemServer in the same thread, because the WMS creation is performed internally, and the WMS creation has a higher priority. That’s all for WMS creation. Finally, let’s look at the WMS constructor, which looks like this:

//WMS.java

    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
            WindowManagerPolicy policy) {.../** * 1. Assign the IMS passed in to mInputManager */
        mInputManager = inputManager; // Must be before createDisplayContentLocked.
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
        mDisplaySettings = newDisplaySettings(); mDisplaySettings.readSettingsLocked(); ./** * 2. Get the DisplayManager object and call its getDisplays method to get the Displays array. Each Display device has a Display instance */
        mDisplays = mDisplayManager.getDisplays();

        for (Display display : mDisplays) {
            / * * * 3. Through the Display array, will Display the object encapsulation into DiplayContent, DisplayContent used to describe a screen * /createDisplayContentLocked(display); }.../** * 4. Get the AMS instance and assign it to the WMS member variable mActivityManager */mActivityManager = ActivityManager.getService(); ./** * 5. Create a WindowAnimator object that manages animation for all Windows */
        mAnimator = new WindowAnimator(this);

        mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);



        LocalServices.addService(WindowManagerInternal.class, new LocalService());
        /** * 6. Initialize the window management policy interface class */ 
      	initPolicy();

        /** * add WMS to Watchdog by addMonitor method * It is used to Monitor the health of some key services of the system. These monitored services implement the watchdog. Monitor interface. * The Watchdog checks the monitored system service every minute. If a deadlock occurs in the monitored system service, it will kill the Watchdog process, that is, the SystemServer process */
        Watchdog.getInstance().addMonitor(this);


    }
Copy the code

The above constructor does about seven things

  1. Comment 1 is used to hold incoming IMS, which is equivalent to WMS holding IMS references
  2. Note 2 gets the Display array from the DisplayManager’s getDisplays method (each Display device has an instance of Display) and iterates over the Display array
  3. Note 3 traverse the Display array, will Display the object encapsulation into DiplayContent, DisplayContent used to describe a screen
  4. Note 4 takes the AMS proxy object and assigns it to the member variable mActivityManager in WMS, so that WMS holds a reference to AMS
  5. Note 5 creates a WindowAnimator object, which manages animation for all Windows
  6. The interface class that initializes the window management policy, WindowManagerPolicy, defines the general specification that a window policy is required to follow
  7. Add its own WMS to Watchdog through the addMonitor method, which is used to Monitor the health of some key services of the system. These monitored services implement the watchdog.monitor interface. The Watchdog checks the monitored system service every minute. If a deadlock occurs on the monitored system service, the Watchdog process, that is, the SystemServer process, is killed.

Let’s look at the implementation at comment 6:

//WMS.java

    private void initPolicy(a) {
        UiThread.getHandler().runWithScissors(new Runnable() {
            @Override
            public void run(a) {
                WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
								/ / 1.
                mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); }},0);
    }
Copy the code

Is this a little bit similar to what we did with DisplayThread? Note 1 shows the WMP init method, which is implemented in PhoneWindowManager. UI thread has a higher priority than the Android. display thread. Therefore, the Android. display thread needs to wait for the PMW init method to complete execution. The android.display thread that handles the wait state is woken up to continue executing the following code. So far we have involved three threads (SystemSerer thread (system_server, android.display, android. UI)) to make it easier to understand, I have drawn a simple diagram, see below:

As you can see from the figure above, the relationship three threads ago is implemented in three steps:

  1. The SystemServer startOtherServices method is first executed in the system_server thread. The WMS main method is called in the startOtherServices method. The main method creates the WMS, which takes precedence over the android.display thread, so the system_server thread waits until the WMS is created. The system_server thread in the wait state is woken up to continue executing the following code.
  2. The WMS constructor calls the WMS initPolicy method, which in turn calls the PWM init method. The PWM init method runs in the Android. UI thread. It has a higher priority than the Android. display thread, so the android.display thread will wait for the PWM init method to complete before the waiting Android. display thread will wake up and continue executing the following code.
  3. After the PWM init method is executed, the Android. display thread completes the creation of the WMS, and waits for the system_server thread to wake up and continue to execute the logic after the WMS main method. For example, the displayReady method of WMS is used to initialize the screen display information (SystemServer startOtherServices).

Key member of WMS

To better understand WMS, it is important to know not only how THE WMS is created, but also the important members of the WMS. Here, the important members refer to some of the WMS member variables. The following table describes their functions in detail:

Members of the type instructions
mPolicy WindowManagerPolicy The interface class for window management policies that defines the general specification that a window policy is required to follow and provides all specific UI behavior for Windows Manager. 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 It is mainly used for inter-process communication. If other application processes want to communicate with THE WMS process, they need to go through a Session, and each application process has a corresponding Session. WMS stores these sessions to record all clients that propose window management services to WMS.
mWindowMap WindowHashMap It inherits from HashMap and limits the key value of HashMap to IBinder and the value of HashMap to WindowState. WindowState is used to hold window information, and in WMS it is used to describe a window. To sum up, it is used to store the collection of various Windows in WMS.
mFinishedStarting ArrayList Its element type is AppWindowToken, which is a subclass of WindowToken. MFinishedStarting is a list of AppWindowTokens used to store application Windows (such as activities) that have been started.
mResizingWindows ArrayList A list of Windows that are being resized
mAnimator WindowAnimator Animation for managing Windows and special effects animation
mH H The code logic used to add tasks to the message queue of the main thread is executed in the main thread.
mInputManager InputManagerService It’s the manager of the input system, it handles touch events, and it looks for the most appropriate window to handle touch feedback.

WindowToken (which is the parent of the mFinishedStarting element type AppWindowToken)

  • 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 brings together Windows (WindowStates) that share a component (such as the same Activity) for easy management

Window adding process (WMS processing part)

In the last article, we talked about how operations on Windows are divided into two parts: One is WindowManager, and the other is WindowManagerService. In the last article, we learned how to handle Windows in Windows Manager. So this section introduces the process of addWindow in WMS, the source code is as follows:

//WMS.java
    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];
        /** * 1. Call the WMP checkAddPermission method to check the permission according to the property of the Window. No subsequent code logic */ is executed without permission
        int res = mPolicy.checkAddPermission(attrs, appOp);
        if(res ! = WindowManagerGlobal.ADD_OKAY) {returnres; }...synchronized(mWindowMap) {
            if(! mDisplayReady) {throw new IllegalStateException("Display has not been initialialized");
            }

            /** * 2. Use displayID to get the window to add to the DisplayContent, If DisplayContent is not found, return WindowManagerGlobal.ADD_INVALID_DISPLAY *, where DisplayContent describes a screen */
            final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
            if (displayContent == null) {
                Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
                        + displayId + ". Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            if(! displayContent.hasAccess(session.mUid) && ! mDisplayManagerInternal.isUidPresentOnDisplay(session.mUid, displayId)) { Slog.w(TAG_WM,"Attempted to add window to a display for which the application "
                        + "does not have access: " + displayId + ". Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

            if (mWindowMap.containsKey(client.asBinder())) {
                Slog.w(TAG_WM, "Window " + client + " is already added");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }

            /** * 3. Type represents the type of a window. The value is between FIRST_SUB_WINDOW and LAST_SUB_WINDOW (1000~1999)
            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                Attrs. token is an object of type IBinder. WindowForClientLocked internally obtains the parent window of the child window from mWindowMap based on attrs.token as the key value. If the * parent window is null or the type range is incorrect, this will return an error status. * /
                parentWindow = windowForClientLocked(null, attrs.token, false);
                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; }}.../** * 5. Get the WindowToken object */ with displayContent's getWindowToken
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
            
            /** * 6. If there is a parent window, assign the parent window's type value to rootType, if there is no current window's type value to rootType. If the value of rootType is equal to TYPE_INPUT_METHOD, TYPE_WALLPAPER *, ADD_BAD_APP_TOKEN indicates that the rootType value is equal to TYPE_INPUT_METHOD and TYPE_WALLPAPER value Null * /
            final int rootType = hasParent ? parentWindow.mAttrs.type : type;

            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.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_DREAM) {
                    Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
                          + attrs.token + ". Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_QS_DIALOG) {
                    Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
                          + attrs.token + ". Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
                    Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
                            + attrs.token + ". Aborting.");
                    return WindowManagerGlobal.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();
                /** * Windows Token is implicitly created after several conditional judgments, which means that when we add Windows, we can not provide WindoToken to WMS, * provided that the rootType and type values are not filtered by the previous conditional judgments. WindowToken implicit and display creation must be distinguished; passing * false in the fourth argument to the object indicates that the creation is implicit. * * /
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow);

                /** * 8. Check if the window is an application window */
            } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
                /** * 9. Convert WindowToken to AppWindowToken specifically for application Windows, and then make subsequent judgments based on the value of AppWindowToken */
                atoken = token.asAppWindowToken();
                if (atoken == null) {
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } else if (atoken.removed) {
                    returnWindowManagerGlobal.ADD_APP_EXITING; }}else if (rootType == TYPE_INPUT_METHOD) {
                if(token.windowType ! = TYPE_INPUT_METHOD) {returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}... }/** * 10. Create WindowState, which holds all the state information of the window, In WMS it represents a window * this refers to IWindow, which will call back the WMS window management operations to ViewRootImpl,token refers to WindowToken */
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            /** * 11. Determine whether the client requesting the window is dead */
            if (win.mDeathRecipient == null) {
                return WindowManagerGlobal.ADD_APP_EXITING;
            }

            /** * 12. Check whether the window DisplayContent is null */
            if (win.getDisplayContent() == null) {
                Slog.w(TAG_WM, "Adding window to Display that has been removed.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

            AdjustWindowParamsLw = adjustWindowParamsLw, adjustWindowParamsLw = adjustWindowParamsLw, adjustWindowParamsLw = adjustWindowParamsLw, adjustWindowParamsLw = adjustWindowParamsLw
            mPolicy.adjustWindowParamsLw(win.mAttrs);
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

            /** * 14. Call WMP's prepareAddWindowLw method to prepare the window to be added to the system */
            res = mPolicy.prepareAddWindowLw(win, attrs);
            if(res ! = WindowManagerGlobal.ADD_OKAY) {returnres; }... win.attach();/** * 15. Add WindowState to mWindowMap */
            mWindowMap.put(client.asBinder(), win);
            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;

            /** * 16. Add WindowState to the corresponding WindowToken (which is actually stored in WindowToken's parent class, WindowContainer). Thus the WindowToken contains the WindowState */ of the same component
            win.mToken.addWindow(win);
            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; }}...return res;
    }
Copy the code

The addWindow method of WMS has a lot of code and is relatively complicated, which is divided into 16 steps. Here, because important codes are annotated in detail, SO I will not explain too much. I will directly summarize the addWindow method here:

  • All Windows to be added are checked, and the following code logic is not executed if the window does not meet some criteria.
  • 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 require WMS to implicitly create a WindowToken.
  • Creation and related processing of WindowState, associating WindowToken with WindowState.
  • Create and configure DisplayContent in preparation for adding Windows to the system.

Window deletion procedure

In the last article, we explained how to create and update a Window. To remove a Window, you need to call the removeView method of WindowManagerImpl. The removeView method of Windows ManagerGlobal is called in the removeView method, so we’ll start here. The code is as follows:

//WindowManagerGlobal.java

    public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }

        synchronized (mLock) {
            /** * 1. Find the position in the mViews list */
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            /** * 2. Call an internal method to pass the index position in */
            removeViewLocked(index, immediate);
            if (curView == view) {
                return; }... }}private int findViewLocked(View view, boolean required) {
        final int index = mViews.indexOf(view);
        if (required && index < 0) {... }return index;
    }
Copy the code

Let’s look at the implementation of comment 2, which looks like this:

//WindowManagerGlobal.java
    private void removeViewLocked(int index, boolean immediate) {
        /** * 1. Get the ViewRootImpl */ to delete the Window
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();

        if(view ! =null) {
            /** * 2. Get InputMethodManager instance */
            InputMethodManager imm = InputMethodManager.getInstance();
            if(imm ! =null) {
                /** * 3. End the logic */ related to the input method in the deleted IMM instance windowimm.windowDismissed(mViews.get(index).getWindowToken()); }}/** * 4. Call the ViewRootImpl die method */
        boolean deferred = root.die(immediate);
        if(view ! =null) {
            view.assignParent(null);
            if(deferred) { mDyingViews.add(view); }}}Copy the code

The above code is divided into 4 steps to do some preparatory work for deletion. Then let’s look at the implementation of comment 4. The code is as follows:

//ViewRootImpl.java
    // The die method needs to be executed immediately and ViewRootImpl no longer executes performTraversals at this point
    boolean die(boolean immediate) {

        /** * 1. Immediate is true, mIsInTraversal is false */
        if(immediate && ! mIsInTraversal) {/** * 2. MIsInTraversal is set to true when performing the performTraversals method of ViewRootImpl. This is set to false */ when performTraversals is finished
            doDie();
            return false; }... mHandler.sendEmptyMessage(MSG_DIE);return true;
    }
Copy the code

We know that mIsInTraversal is set to false after performTraversals is executed, so the doDie method must be executed immediately as follows:

//ViewRootImpl.java

    void doDie(a) {
        /** * 1. Check if the thread of execution is in the thread that created the Window, if not, throw an exception */
        checkThread();
        synchronized (this) {
            /** * 2. Support return */ if it is deleted
            if (mRemoved) {
                return;
            }
            /** * 3. Set the deletion action to true to avoid the deduplication operation */
            mRemoved = true;
            /** * 4. Check whether the deleted Window has child View */
            if (mAdded) {
                / * * * 5. If there is a will call dispatchDetachedFromWindow method to destroy the View * /
                dispatchDetachedFromWindow();
            }

            /** * 6. If the deleted Window has child views and is not deleted for the first time, the following logic */ will be executed
            if(mAdded && ! mFirst) { destroyHardwareRenderer();if(mView ! =null) {
                    int viewVisibility = mView.getVisibility();
                    booleanviewVisibilityChanged = mViewVisibility ! = viewVisibility;if (mWindowAttributesChanged || viewVisibilityChanged) {
                        
                        try {
                            if ((relayoutWindow(mWindowAttributes, viewVisibility, false) & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) ! =0) { mWindowSession.finishDrawing(mWindow); }}catch (RemoteException e) {
                        }
                    }

                    mSurface.release();
                }
            }

            mAdded = false;
        }
        /** * 7. Handle the WindowManagerGlobal doRemoveView method */
        WindowManagerGlobal.getInstance().doRemoveView(this);
    }
Copy the code

If the thread does not belong to the thread that created the Window, an exception will be thrown, indicating that only creation and deletion are allowed to be processed in the same thread. Then, some logical decisions are made to delete the Window, such as whether there is repeated deletion of the Window. If there is a child View of the Window that has been deleted, then the action to delete the View will be distributed.

//WindowManagerGlobal.java
    void doRemoveView(ViewRootImpl root) {
        synchronized (mLock) {
            /** * 1. Locate the ViewRootImpl corresponding to the deleted Window in the mRoots list, */
            final int index = mRoots.indexOf(root);
            if (index >= 0) {
                mRoots.remove(index);
                mParams.remove(index);
                finalView view = mViews.remove(index); mDyingViews.remove(view); }}if(ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) { doTrimForeground(); }}Copy the code

The above code first finds the position of the ViewRootImpl corresponding to the deleted Window in the mRoots list, and then obtains the index position to delete the elements corresponding to the deleted Window from the layout parameter list and View list. We then return to the code implementation of comment 5 in the ViewRootImpl doDie method as follows:

//ViewRootImpl.java

    void dispatchDetachedFromWindow(a) {...try {
          / / 1.
            mWindowSession.remove(mWindow);
        } catch (RemoteException e) {
        }
			...
    }
Copy the code

In dispatchDetachedFromWindow approach, the core code is the code comments 1, call IWindowSession the remove method, IWindowSession implementation class in Session, in the last article has been described, do not understand the first to look at the series of WindowManager source code analysis, next we look at the Session remove method, the code is as follows:

//Session.java
    public void remove(IWindow window) {
        mService.removeWindow(this, window);
    }
Copy the code

WMS: delete Window

//WMS.java
    void removeWindow(Session session, IWindow client) {
        synchronized(mWindowMap) {
            /** * 1. Obtain the corresponding WindowState of the Window. WindowState is used to store information about the Window
            WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return;
            }
            /** * 2. Call the WindowState removeIfPossible method */win.removeIfPossible(); }}Copy the code

Let’s look at the annotation 2 implementation, the code implementation:

//WindowState.java
    @Override
    void removeIfPossible(a) {
        super.removeIfPossible();
        /** * Call the internal removeIfPossible method */
        removeIfPossible(false /*keepVisibleDeadWindow*/);
    }
Copy the code
//WindowState.java
    private void removeIfPossible(boolean keepVisibleDeadWindow) {...// Check for logic code omission

        / * * * 1 * /removeImmediately(); . }Copy the code

In comment 1, the operation of the deleted Window will be judged and filtered. As long as there is one that does not meet the requirement, the deletion will be delayed. For example, if the Window is performing an animation, it needs to wait until the animation is completed before deleting it, and finally execute the code of comment 1 as follows:

//WindowState.java

    @Override
    void removeImmediately(a) {
        super.removeImmediately();

        /** * 1. The deletion action is being performed */
        if (mRemoved) {
            ..
            return;
        }

        /** * 2. The operation used to prevent deduplication */
        mRemoved = true; ./** * 3. If the current Window is StatusBar or NavigationBar, it will be removed from the corresponding controller */
        mPolicy.removeWindowLw(this); ./** * 4. Delete the Session corresponding to the deleted Window from WMS */mSession.windowRemovedLocked(); ./ * * * 5. Call the WMS postWindowRemoveCleanupLocked method used to be deleted Windows do some cleanup. * /
        mService.postWindowRemoveCleanupLocked(this);
    }
Copy the code

Now let’s take a look at comments 3 and 4 respectively to see their specific operation implementation. The code is as follows

//PhoneWindowManager.java    
@Override
    public void removeWindowLw(WindowState win) {
        if (mStatusBar == win) {
            mStatusBar = null;
            mStatusBarController.setWindow(null);
        } else if (mNavigationBar == win) {
            mNavigationBar = null;
            mNavigationBarController.setWindow(null); }}Copy the code

NavigationBar (StatusBar, NavigationBar, StatusBar, NavigationBar);

//Session.java
    void windowRemovedLocked(a) {
        mNumWindow--;
        killSessionLocked();
    }
    private void killSessionLocked(a) {
        if (mNumWindow > 0| |! mClientDead) {return;
        }

        /** * 1. Delete Session */ from WMS
        mService.mSessions.remove(this);
        if (mSurfaceSession == null) {
            return; }...try {
            /** * 2. Release SurfaceSession resources */
            mSurfaceSession.kill();
        } catch(Exception e) { ... }... }Copy the code

SurfaceSession (SurfaceSession) is a connection to SufaceFlinger. SurfaceSession (SurfaceSession) is a connection to SufaceFlinger. From this connection, 1 ~ more surfaces can be created and rendered to the screen.

Although the deletion process of Windows is too complicated, we can make a simple summary of its deletion process as follows:

  1. Check that the deleted thread meets the requirements and throw an exception if it does not.
  2. Remove the element corresponding to the deleted Window from the ViewRootImpl, layout parameters, and View list.
  3. Determine if the deletion can be performed, and if not, postpone the operation.
  4. Perform the delete operation to clear and release resources related to the deleted Window.

conclusion

In this article, we learned the responsibilities of WMS and the operation of adding and deleting Windows. From the responsibilities of WMS, we can see that WMS is quite complex, and there are four modules associated with it: “Window management”, “Window animation”, “input system” and “Surface”. Each of them is more important and complex system, the article introduced and application development is more closely related to the window management, the rest of the system will have to rely on their own to consult the relevant source books or some related articles.

Thank you for reading, I hope to help you!

reference

  • Android Advanced Decryption