preface

In Android development, Window is the carrier of all views, such as the view of Activity, Dialog and Toast. If we want to add or delete a Window, we need to operate it through WindowManager. WindowManager communicates with WindowManagerService across processes through Binder, handing over the implementation to WindowManagerService (referred to as WMS). Here are some of them to clarify their basic context.

This article is based on Android8.0, the relevant source location is as follows: Frameworks/base/core/Java/android/view / *. Java (* on behalf of the Window, WindowManager, ViewManager WindowManagerImpl, WindowManagerGlobal, ViewRootImpl) frameworks/base/core/Java/com/android/internal/policy/PhoneWindow Java frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java frameworks/base/services/core/java/com/android/server/wm/Session.javaCopy the code

Window

1. What is Window

Window is the concept of a Window in Android development. It is an abstract class. We open Window as follows:

public abstract class Window {
    public static final int FEATURE_NO_TITLE = 1;
    public static final int FEATURE_CONTENT_TRANSITIONS = 12;
    / /...
     public abstract View getDecorView(a);
     public abstract void setContentView(@LayoutRes int layoutResID);
     public abstract void setContentView(View view);
     public abstract void setContentView(View view, ViewGroup.LayoutParams params);
     public <T extends View> T findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
    }
    / /...
}
Copy the code

The PhoneWindow implementation class is PhoneWindow. In this case, there is a top-level View– DecorView, inherited from FrameLayout. We can get this with getDecorView(). When we call the Activity’s setContentView, we actually end up calling the Window’s setContentView. When we call the Activity’s findViewById, The findViewById of the Window is actually called, which indirectly indicates that the Window is the View’s direct manager. But Window does not really exist, it represents more of an abstract set of functions. View is the presentation form of View in Android. What is drawn on the screen is a View, not Window, but a View cannot exist alone, it must be attached to the abstract concept of Window. Android relies on Windows to provide views for activities, Dialog, Toast, PopupWindow, StatusBarWindow (system status bar), input Window, etc. A view like a Dialog corresponds to a Window.

Window type (application Window, child Window, system Window) and hierarchy

The Window type type is defined in the static inner class LayoutParams in WindowManager as follows:

public interface WindowManager extends ViewManager {
    / /...
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
        // Application window type value
        public static final int FIRST_APPLICATION_WINDOW = 1;// represents the starting value of the application window
        public static final int TYPE_BASE_APPLICATION   = 1;// The base value of the window. The type value of other Windows is greater than this value
        public static final int TYPE_APPLICATION        = 2;// For normal application Windows, the token must be set to the Activity's token to specify who the window belongs to
        public static final int TYPE_APPLICATION_STARTING = 3;
        public static final int TYPE_DRAWN_APPLICATION = 4;
        public static final int LAST_APPLICATION_WINDOW = 99;// Represents the end value of the application window
        
        // Subwindow type value
        public static final int FIRST_SUB_WINDOW = 1000;// represents the starting value of the child window
        public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
        public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
        public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;
        public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
        public static final int LAST_SUB_WINDOW = 1999;// Represents the end value of the child window
        
        // System window type value
        public static final int FIRST_SYSTEM_WINDOW     = 2000;// represents the starting value of the system window
        public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;// System status bar
        public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;// Search bar window
        public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;// Call window
        / /...
        public static final int LAST_SYSTEM_WINDOW      = 2999;// Indicates the end value of the system window}}Copy the code

There are many values in LayoutParams that start with TYPE, but generally they fall into three categories:

  • Application window: The value of type ranges from 1 to 99. An Activity is a typical application window. The value of type is TYPE_BASE_APPLICATION, and WindowManager LayoutParams default type is TYPE_APPLICATION.
  • Child window: the value of type ranges from 1000 to 1999. PupupWindow is a typical child window whose type value is TYPE_APPLICATION_PANEL. Child Windows cannot exist independently and must be attached to the parent window
  • System window: The value of type ranges from 2000 to 2999. There are many types of system Windows, which are not all listed above. The system status bar is a typical system window, and its type value is TYPE_STATUS_BAR.

In general, the higher the type value is, the more advanced the Window will be displayed. Among the three types of Windows, the hierarchy of application Windows ranges from 1 to 99. The level range of the sub-window is 1000~1999, and the level range of the system Window is 2000~2999. The level range corresponds to the type value. If you want the Window to be on all Windows, use a larger level, such as the system level.

3. Window properties

The Window type flag is also defined in the static inner class LayoutParams in WindowManager as follows:

public interface WindowManager extends ViewManager {
    / /...
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
        public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;
        public static final int FLAG_DIM_BEHIND        = 0x00000002;
        public static final int FLAG_BLUR_BEHIND        = 0x00000004;
        public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;
        public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;
        public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;
        public static final int FLAG_KEEP_SCREEN_ON     = 0x00000080;
		/ /...}}Copy the code

There are also many flag attributes defined in LayoutParams. Here are some common ones:

  • FLAG_ALLOW_LOCK_WHILE_SCREEN_ON: Allows screen locking while the screen is on, as long as the window is visible to the user.
  • FLAG_KEEP_SCREEN_ON: The screen stays on as long as the window is visible to the user.
  • FLAG_SHOW_WHEN_LOCKED: Windows can be displayed on the lock screen interface.
  • FLAG_NOT_FOCUSABLE: The window can’t get focus and can’t accept any input events. This flag also enables FLAG_NOT_TOUCH_MODAL, and the event is passed directly to the lower window that has focus.
  • FLAG_NOT_TOUCH_MODAL: Touch events outside the current Window area are transmitted to the underlying Window, and touch events inside the current Window area are handled by themselves. Generally, enable this flag, otherwise other Windows cannot receive stand-alone events.
  • FLAG_NOT_TOUCHABLE: The window does not receive any touch events

You can see that the Type and flag in LayoutParams are very important to control the display characteristics of the Window. With information about Windows, you can better understand Windows Manager.

WindowManager

WindowManager is an interface, there are commonly used methods: add View, update View and delete View, WindowManager inherits from ViewManager, these three methods are defined in ViewManager, as follows:

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

Copy the code

You can see that these methods pass in a View, not a Window, which means that The Windows Manager is managing the View in the Window, and we’re manipulating the View in the Window through The Windows Manager. WindowManager concrete implementation class is WindowManagerImp, we look at the implementation of the corresponding method, as follows:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;
    / /...
    
    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }
    
      @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        / /...
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        / /...
        mGlobal.updateViewLayout(view, params);
    }
    
     @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false); }}Copy the code

As you can see, WindowManagerImp does nothing. It delegates all three methods to the WindowManagerGlobal singleton class. We can also see the mParentWindow field, which is of type Window and is passed in from the construct. So WindowManager will hold a reference to the Window so that WindowManager can operate on it. For example, mglobal.addView can be interpreted as adding a View to a Window, in Windows ManagerGlobal, as follows:

public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow){
    / /...
    ViewRootImpl root;
    root = new ViewRootImpl(view.getContext(), display);/ / comment 1
    / /...
    root.setView(view, wparams, panelParentView);
}
Copy the code

It will end up in viewrolMP’s setView as follows:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  	/ /...
    // This is where the View is drawn
    requestLayout();
     / /...
    // Establish communication with WMS through session
     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
    / /...
}
Copy the code

In ViewRootlmp setView, requestLayout() initiates the View drawing process, and then in mWindowSession addToDisplay, Binder communicates with WMS across the process to request the View on the display window. At this point, the View will be displayed on the screen. This mWindowSession is an IWindowSession.AIDL interface type used for inter-process communication. Within WMS, a separate Session is reserved for each application request, also implementing the IWindowSession interface. The application communicates with the WMS through this Session. So when does this mWindowSession get assigned? Just in comment 1 above, we open the ViewRootlmp constructor as follows:

public ViewRootImpl(Context context, Display display) {
    mWindowSession = WindowManagerGlobal.getWindowSession();
    / /...
}
Copy the code

You can see that mWindowSession is obtained from the WindowManagerGlobal singleton class getWindowSession(). We open the WindowManagerGlobal getWindowSession() as follows:

 public static IWindowSession getWindowSession(a) {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    // get the local proxy of WMS first
                    IWindowManager windowManager = getWindowManagerService();
                    // get Session from openSession of WMS
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throwe.rethrowFromSystemServer(); }}returnsWindowSession; }}Copy the code

GetWindowManagerService () getWindowManagerService()

public static IWindowManager getWindowManagerService(a) {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                // Get the local proxy object for WMS
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
              	/ /...
                } catch (RemoteException e) {
                    throwe.rethrowFromSystemServer(); }}returnsWindowManagerService; }}Copy the code

As you can see, the ServiceManager. GetService (” window “) is to obtain the WMS, then through IWindowManager. The Stub. AsInterface () into WMS local agent in the application process, GetWindowManagerService () is the proxy that returns the WMS application process locally. (Binder knowledge is involved here)

WMS: openSession (WMS) : openSession (WMS);

  @Override
    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client, IInputContext inputContext) {
        / /...
        // Create a Session for each window request and return it
        Session session = new Session(this, callback, client, inputContext);
        return session;
    }

Copy the code

Thus the communication bridge with WMS has been established. The WindowManager then indirectly requests the WINDOW view to the WMS through the Session, and the WMS returns information to the application about how it interacts with the window. Mglobal.updateviewlayout and McLobal.removeview are similar processes that can be studied on your own.

WindowManagerService

WindowManagerService is a system-level service started by SystemService and implements the iWindowManager. AIDL interface. Its main functions are divided into the following two aspects:

1. Window management

It is responsible for starting, adding, and deleting Windows. It is also responsible for the display of the window hierarchy (z-Orderes) and maintaining the state of the window. We continue the above mGlobal addView, when it comes to the method above is a display window view on WMS’s request, will eventually go to mWindowSession. AddToDisplay () method, we can find this function in the Session, as follows:

 @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        // Return the result returned by addWindow in WMS
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
Copy the code

As you can see, the addToDisplay method finally returns the result returned by addWindow in WMS. The addWindow request is handled by WMS. The addWindow implementation is in WMS, and the code is quite long, which I won’t go into here (I’ll leave it for an example in the next article). The main thing addWindow does is to check the window permission first, because the system window needs to declare the permission, and then proofread the window according to the relevant Display information and window information, and then obtain the corresponding WindowToken, and then check the validity of the window according to different window types. If all the above steps are followed, a WindowState object is created for the window to maintain the WindowState and adjust the WindowState as appropriate, and finally communicate with SurfaceFlinger via the WindowState attach method. So SurfaceFlinger can use these Window information to synthesize surfaces and render output to the display device.

2. Relay station for input events

When we touch the screen, an input event is generated. In Android, the input responsible for managing the event is InputManagerService. When IMS is started, the NativeInputManager is created in the native layer. In the construction of NativeInputManager, InputManager and Eventhub are created (listening for all event inputs in the /dev/inpu/device node), InputDispatcher, InputReader, InputReaderThread, and InputDispatcherThread are created accordingly in the InputManager construction.

InputReader runs in InputReaderThread, which cycles through the raw input events from EventHub (calling EventHub’s getEvent() method), The InputReader processes these raw input events to the InputDispatcher running in the InputDispatcherThread, and the InputDispatcher looks for the most appropriate window to process the input events. The WMS is the window manager. WMS updates all Window information to the InputDispatcher so that the InputDispatcher can send the input event to the appropriate Window, which will pass the input event to the top-level View and then involve the familiar event distribution mechanism.

Let’s look at the ViewRootImp setView invokes the mWindowSession. AddToDisplay method when the incoming parameters:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  	/ /...
   	mInputChannel = new InputChannel();
    / /...
    // Establish communication with WMS via session and receive input event callbacks via InputChannel
     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
    / /...
     if(mInputChannel ! =null) {
         / /...
         // Handle input event callbacks
         mInputEventReceiver = newWindowInputEventReceiver(mInputChannel, Looper.myLooper()); }}Copy the code

Note that the mInputChannel parameter passed in is of type InputChannel, which implements the Parcelable interface to accept input events returned by WMS. In WMS, two instances of InputChannel are created. One will be passed back through the mInputChannel parameter, one will be put in the WindowState of the WMS, and the InputChannel in the WindowState will be given to the InputDispatcher, In this way, the application side and the InputDispatcher side can receive and send events through the two InputChannels.

The class diagram relationship between them is as follows:

conclusion

From the above brief introduction, we know that Window is the carrier of View. If we want to delete, add or update Windows, we have to go through WindowManager. WindowManager communicates with WMS through Session, and the specific implementation is handled by WMS. The WMS creates a WindowState for each Window and manages them, leaving rendering to the SurfaceFinger. The WMS system structure discussed in this paper is as follows:

References:

Android Source Code Design Patterns