preface

With the development of mobile terminal and front-end technology, more and more applications use the mode of mixed development, the most typical representative is small program. Some of this will definitely involve front-end and mobile interaction.

The common interaction modes between Android and the front end are as follows:

JavaScript to Android The Android – JavaScript
addJavascriptInterface loadUrl
shouldOverrideUrlLoading evaluateJavascript
onJsPrompt/onJsConfirm/onJsAlert /

In the development of Android and front-end interaction, due to front-end performance and style and other related reasons, some controls will be displayed through the front-end input Android native display. Such as Dialog, DatePicker, Alert, Toast, etc. Which brings us to today’s question.

Front end: I found a popover control does not display, help me check the reason.

Android: generally pop-ups are the communication between JS and Android, we will intercept it in addJavascriptInterface, and then display it. You don’t need to check, it should be my problem, LET me see.

Android: check for a long time, how did not go to addJavascriptInterface, must be JS error, front-end quick connect to the pan!

Front end: Why was the previous version ok?

Android :(guilty……) You can’t be my problem. Check it out.

After investigation, it was found that this popover did not go to the front end of the communication with Android, but used a SELECT tag, then the question arises, why the use of select suddenly failed to display correctly.

Problem description

This article source analysis based on Android API Level29

The front-end code

select.html

<! DOCTYPEhtml>
<html>
<head>
  <meta charset="utf-8">
  <title></title>
</head>
<body>
  <select>
    <option value="Java">Java</option>
    <option value="PHP">PHP</option>
    <option value="Go">Go</option>
    <option value="JavaScript">JavaScript</option>
  </select>
</body>
</html>
Copy the code

The Android code

MainActivity.java
  
public class MainActivity extends AppCompatActivity {

    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewGroup container = findViewById(R.id.container);
        webView = new WebView(getApplicationContext());
        webView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
        container.addView(webView);
        webView.loadUrl("file:android_asset/select.html"); }}Copy the code

This is a simplified example of code that does not pop up when clicking the Select TAB and the console displays the following error:

E/WindowManager: BadTokenException or InvalidDisplayException, clean up.
Copy the code

When the WebView is passed in a different ApplicationContext, the popup window does not display properly. Under normal circumstances, the popup window will display as shown in the following figure:

The Layout Inspector shows the Select TAB as AlertDialog on the Android layer. So let’s simplify things a little bit and pass in an Application Context directly to an AlertDialog and it might be a problem, so let’s verify that.

The verification code is simple as follows:

findViewById(R.id.show_dialog).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new AlertDialog.Builder(getApplicationContext())
                .setTitle("Title")
                .setMessage("message") .create() .show(); }});Copy the code

If we wanted to, the program crashed. Remember Unable to add window — token null is not valid; is your activity running? This is a line of logs that we’ll see later.

The 2021-08-03 15:36:00. 112, 19594-19594 / com. Sample. Select E/WindowManager: BadTokenException or InvalidDisplayException, Sample. Select D/AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: Shutting down VM --------- beginning of Crash 2021-08-03 15:36:00.113 19594-19594/com.sample.select E/AndroidRuntime: N /A FATAL EXCEPTION: main Process: com.sample.select, PID: 19594 android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
        at android.view.ViewRootImpl.setView(ViewRootImpl.java:1133)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:409)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:111)
        at android.app.Dialog.show(Dialog.java:342)
        at com.sample.select.MainActivityThe $1.onClick(MainActivity.java:34)
        at android.view.View.performClick(View.java:7567)
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
        at android.view.View.performClickInternal(View.java:7544)
        at android.view.View.accessThe $3600(View.java:836)
        at android.view.View$PerformClick.run(View.java:28770)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:233)
        at android.app.ActivityThread.main(ActivityThread.java:7892)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
Copy the code

Dialog displays source code analysis

Since crash’s log is related to the display of Dialog, now we don’t look at the error reported above, and first follow the line showing Dialog to see the main process of Dialog calling show method.

findViewById(R.id.show_dialog).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      // Click the button to execute the following code, which is ActivityContext
        new AlertDialog.Builder(MainActivity.this)
                .setTitle("Title")
                .setMessage("message") .create() .show(); }});Copy the code

1.0 AlertDialog create

SetTitle and setMessage set some display information. Focus on what create() does.

AlertDialog.java 
  
public AlertDialog create(a) {
    // Context has already been wrapped with the appropriate theme.
  	// Create an AlertDialog object
    final AlertDialog dialog = new AlertDialog(P.mContext, 0.false);
    P.apply(dialog.mAlert);
    dialog.setCancelable(P.mCancelable);
    if (P.mCancelable) {
        dialog.setCanceledOnTouchOutside(true);
    }
    dialog.setOnCancelListener(P.mOnCancelListener);
    dialog.setOnDismissListener(P.mOnDismissListener);
    if(P.mOnKeyListener ! =null) {
        dialog.setOnKeyListener(P.mOnKeyListener);
    }
    return dialog;
}
Copy the code

1.1 Invoke the AlertDialog constructor

Final AlertDialog dialog = new AlertDialog(P.mContext, 0, false) will eventually be called to the dialog.

Dialog.java
  
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    if (createContextThemeWrapper) {
        if (themeResId == Resources.ID_NULL) {
            final TypedValue outValue = new TypedValue();
            context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
            themeResId = outValue.resourceId;
        }
        mContext = new ContextThemeWrapper(context, themeResId);
    } else {
        mContext = context;
    }

  	// Get WindowManager from context
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

    / / create PhoneWindow
    final Window w = new PhoneWindow(mContext);
    mWindow = w;
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setOnWindowSwipeDismissedCallback(() -> {
        if(mCancelable) { cancel(); }});// Set the WindowManager for Windows
    w.setWindowManager(mWindowManager, null.null);
    w.setGravity(Gravity.CENTER);

    mListenersHandler = new ListenersHandler(this);
}
Copy the code

We can see that there are three main things that are done in the Dialog constructor

  • Get the WindowManager from the context.
  • Create PhoneWindow and assign it to mWindow. Window is an abstract class and PhoneWindow is its only implementation class
  • Set the WindowManager for the previous Window

1.2 according to the show

Create completes and goes to the show method on the Dialog

Dialog.java
  
public void show(a) {
   // ...
    if(! mCreated) { dispatchOnCreate(null);
    } else {
        // Fill the DecorView in on any configuration changes that
        // may have occured while it was removed from the WindowManager.
        final Configuration config = mContext.getResources().getConfiguration();
        mWindow.getDecorView().dispatchConfigurationChanged(config);
    }
  
    onStart();
    mDecor = mWindow.getDecorView();

   // ...
  
    WindowManager.LayoutParams l = mWindow.getAttributes();
    boolean restoreSoftInputMode = false;
    if ((l.softInputMode
            & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
        l.softInputMode |=
                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
        restoreSoftInputMode = true;
    }

    // Notice the mWindowManager, where the error is reported
    mWindowManager.addView(mDecor, l);
    if (restoreSoftInputMode) {
        l.softInputMode &=
                ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
    }

    mShowing = true;

    sendShowMessage();
}
Copy the code

The main points we care about in this step are:

  • Get the DecorView through mWindow
  • Add a View from mWindowManager: mWindowManager.addView(mDecor, L)

So mDecor is obtained through mWindow, how is it set? Let’s take another look.

1.2.1 dispatchOnCreate

DispatchOnCreate is called in the show method as follows:

Dialog.java
  
	void dispatchOnCreate(Bundle savedInstanceState) {
        if(! mCreated) { onCreate(savedInstanceState); mCreated =true; }}Copy the code

1.2.2 onCreate

AlertDialog.java
  
		@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAlert.installContent();
    }
Copy the code

1.2.3 installContent

AlertController.java

		@UnsupportedAppUsage
    public void installContent(a) {
        int contentView = selectContentView();// Default layout file
        mWindow.setContentView(contentView);
        setupView();
    }
Copy the code

1. The setContentView

PhoneWindow.java
  
		@Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
          	/ / the key
            installDecor();
        } else if(! hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); }if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if(cb ! =null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
Copy the code

1.2.5 installDecor

PhoneWindow.java
  
		private void installDecor(a) {
        mForceDecorInstall = false;
        if (mDecor == null) {
          	// Assign mDecor
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if(! mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures ! =0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); }}else {
            mDecor.setWindow(this);
        }
      
        // ...
    }
Copy the code

1.2.6 generateDecor

 PhoneWindow.java
   
		protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                if(mTheme ! = -1) { context.setTheme(mTheme); }}}else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }
Copy the code

At this point the Window’s DecorView is assigned. With the subline done, we return to the main line.

1.3 WindowManagerImpl addView

You can see that mWindowManager.addView(mDecor, L) is called in show(), which is done by WindowManager to add the View as a window to the WMS.

In the process of obtaining mWindowManager we can see that the implementation class of mWindowManager is WindowManagerImpl, next to WindowManagerImpl to see the implementation of addView method.

WindowManagerImpl.java
  
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();  
  
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
Copy the code

1.4 WindowManagerGlobal addView

WindowManagerImpl calls mglobal.addView again. Mglobal.addview is a singleton with only one instance object in the current process.

WindowManagerGlobal.java

// get the WindowManagerGlobal singleton object
@UnsupportedAppUsage
public static WindowManagerGlobal getInstance(a) {
    synchronized (WindowManagerGlobal.class) {
        if (sDefaultWindowManager == null) {
            sDefaultWindowManager = new WindowManagerGlobal();
        }
        returnsDefaultWindowManager; }}/ /...
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (display == null) {
        throw new IllegalArgumentException("display must not be null");
    }
    if(! (paramsinstanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    if(parentWindow ! =null) {
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
        // If there's no parent, then hardware acceleration for this view is
        // set from the application's hardware acceleration setting.
        final Context context = view.getContext();
        if(context ! =null&& (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) ! =0) {
            wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
        }
    }

    ViewRootImpl root;
    View panelParentView = null;

    synchronized (mLock) {
        // Start watching for system property changes.
        if (mSystemPropertyUpdater == null) {
            mSystemPropertyUpdater = new Runnable() {
                @Override public void run(a) {
                    synchronized (mLock) {
                        for (int i = mRoots.size() - 1; i >= 0; --i) { mRoots.get(i).loadSystemProperties(); }}}}; SystemProperties.addChangeCallback(mSystemPropertyUpdater); }int index = findViewLocked(view, false);
        if (index >= 0) {
            if (mDyingViews.contains(view)) {
                // Don't wait for MSG_DIE to make it's way through root's queue.
                mRoots.get(index).doDie();
            } else {
                throw new IllegalStateException("View " + view
                        + " has already been added to the window manager.");
            }
            // The previous removeView() had not completed executing. Now it has.
        }

        // If this is a panel window, then find the window it is being
        // attached to for future reference.
        if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
            final int count = mViews.size();
            for (int i = 0; i < count; i++) {
                if(mRoots.get(i).mWindow.asBinder() == wparams.token) { panelParentView = mViews.get(i); }}}/ / create ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        // do this last because it fires off messages to start doing things
        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throwe; }}}Copy the code

The view wrootimpl is created in the addView method of Windows ManagerGlobal. Let’s look at what root.setView(View, wparams, panelParentView) does.

1.5 ViewRootImpl setView

ViewRootImpl.java

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
           
         		/ /...
            int res; /* = WindowManagerImpl.ADD_OKAY; * /

            requestLayout();
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                mInputChannel = newInputChannel(); } mForceDecorViewVisibility = (mWindowAttributes.privateFlags & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) ! =0;
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
              	// Here we assign a value to res
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                        mTempInsets);
                setFrame(mTmpFrame);
            } catch (RemoteException e) {
                mAdded = false;
                mView = null;
                mAttachInfo.mRootView = null;
                mInputChannel = null;
                mFallbackEventHandler.setView(null);
                unscheduleTraversals();
                setAccessibilityFocus(null.null);
                throw new RuntimeException("Adding window failed", e);
            } finally {
                if(restore) { attrs.restore(); }}/ /...
            if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
            if (res < WindowManagerGlobal.ADD_OKAY) {
                mAttachInfo.mRootView = null;
                mAdded = false;
                mFallbackEventHandler.setView(null);
                unscheduleTraversals();
                setAccessibilityFocus(null.null);
                switch (res) {
                    case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
                    case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
                    		// The crash was caused by the code going into this branch
                        throw new WindowManager.BadTokenException(
                                "Unable to add window -- token " + attrs.token
                                + " is not valid; is your activity running?");
                    case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
                        throw new WindowManager.BadTokenException(
                                "Unable to add window -- token " + attrs.token
                                + " is not for an application");
                    case WindowManagerGlobal.ADD_APP_EXITING:
                        throw new WindowManager.BadTokenException(
                                "Unable to add window -- app for token " + attrs.token
                                + " is exiting");
                    case WindowManagerGlobal.ADD_DUPLICATE_ADD:
                        throw new WindowManager.BadTokenException(
                                "Unable to add window -- window " + mWindow
                                + " has already been added");
                    case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
                        // Silently ignore -- we would have just removed it
                        // right away, anyway.
                        return;
                    case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
                        throw new WindowManager.BadTokenException("Unable to add window "
                                + mWindow + " -- another window of type "
                                + mWindowAttributes.type + " already exists");
                    case WindowManagerGlobal.ADD_PERMISSION_DENIED:
                        throw new WindowManager.BadTokenException("Unable to add window "
                                + mWindow + " -- permission denied for window type "
                                + mWindowAttributes.type);
                    case WindowManagerGlobal.ADD_INVALID_DISPLAY:
                        throw new WindowManager.InvalidDisplayException("Unable to add window "
                                + mWindow + " -- the specified display can not be found");
                    case WindowManagerGlobal.ADD_INVALID_TYPE:
                        throw new WindowManager.InvalidDisplayException("Unable to add window "
                                + mWindow + " -- the specified window type "
                                + mWindowAttributes.type + " is not valid");
                }
                throw new RuntimeException(
                        "Unable to add window -- unknown error code " + res);
            }

           / /...}}}Copy the code

At this point, if res < WindowManagerGlobal.ADD_OKAY you can see that we have an error above

Unable to add window -- token null is not valid; is your activity running?

Here if we pass an Activity the return value is greater than zero (ADD_OKAY). The Application res value passed in is -1.

Error analysis

With the basis of the above, then we analyze how res to above, res is mWindowSession addToDisplay return values, what’s the mWindowSession?

It is clear from the following figure and code that mWindowSession is a Binder.

ViewRootImpl.java

@UnsupportedAppUsage
final IWindowSession mWindowSession;  

/ /...
  
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
        getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
        mTempInsets)
Copy the code
IWindowSession.aidl

interface IWindowSession {
    int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
            in int viewVisibility, in int layerStackId, out Rect outFrame,
            out Rect outContentInsets, out Rect outStableInsets,
            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
            out InsetsState insetsState, out InsetsSourceControl[] activeControls);
    int addToDisplayAsUser(IWindow window, int seq, in WindowManager.LayoutParams attrs,
                in int viewVisibility, in int layerStackId, in int userId,
                out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets,
                out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
                out InsetsState insetsState, out InsetsSourceControl[] activeControls);
    int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
            in int viewVisibility, in int layerStackId, out Rect outContentInsets,
            out Rect outStableInsets, out InsetsState insetsState);
    @UnsupportedAppUsage
    void remove(IWindow window);
    int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility,
            int flags, long frameNumber, out Rect outFrame,
            out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
            out Rect outBackdropFrame,
            out DisplayCutout.ParcelableWrapper displayCutout,
            out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
            out InsetsState insetsState, out InsetsSourceControl[] activeControls,
            out Point outSurfaceSize, out SurfaceControl outBlastSurfaceControl);
}
Copy the code

MWindowSession is a Binder that runs in the application process and is initialized when ViewRootImpl is created.

ViewRootImpl.java

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

In ViewRootImpl constructor invokes the WindowManagerGlobal. GetWindowSession ()

WindowManagerGlobal.java

public static IWindowSession getWindowSession(a) {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                // Emulate the legacy behavior. The global instance of InputMethodManager
                // was instantiated here.
                // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
                InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                IWindowManager windowManager = getWindowManagerService();
                s  WindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {
                            @Override
                            public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); }}); }catch (RemoteException e) {
                throwe.rethrowFromSystemServer(); }}returnsWindowSession; }}Copy the code

IWindowSession corresponds to a Session in the system_server process.

Session.java

class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    final WindowManagerService mService;
    final IWindowSessionCallback mCallback;
    final int mUid;
    final int mPid;
    private final String mStringName;
    SurfaceSession mSurfaceSession;
    private int mNumWindow = 0;
    // Set of visible application overlay window surfaces connected to this session.
    private final Set<WindowSurfaceController> mAppOverlaySurfaces = new HashSet<>();
    // Set of visible alert window surfaces connected to this session.
    private final Set<WindowSurfaceController> mAlertWindowSurfaces = new HashSet<>();
    private final DragDropController mDragDropController;
    final boolean mCanAddInternalSystemWindow;
    final boolean mCanHideNonSystemOverlayWindows;
    final boolean mCanAcquireSleepToken;
    private AlertWindowNotification mAlertWindowNotification;
    private boolean mShowingAlertWindowNotificationAllowed;
    private boolean mClientDead = false;
    private float mLastReportedAnimatorScale;
    private String mPackageName;
    private String mRelayoutTag;

    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        try {
            return super.onTransact(code, data, reply, flags);
        } catch (RuntimeException e) {
            if(! (einstanceof SecurityException)) {
                Slog.wtf(TAG_WM, "Window Session Crash", e);
            }
            throwe; }}@Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
            Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
                outInsetsState);
    }
	/ /...
}
Copy the code

Session addToDisplay calls mservice.addWindow (), which is the familiar WindowManagerService.

WindowManagerService.java

public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState) {
    int[] appOp = new int[1];
    int res = mPolicy.checkAddPermission(attrs, appOp);
    if(res ! = WindowManagerGlobal.ADD_OKAY) {return res;
    }

    boolean reportNewConfig = false;
    WindowState parentWindow = null;
    long origId;
    final int callingUid = Binder.getCallingUid();
  	// Assign type here
    final int type = attrs.type;

    synchronized (mGlobalLock) {
        if(! mDisplayReady) {throw new IllegalStateException("Display has not been initialialized");
        }

        final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);

        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)) { 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;
        }

        if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
          	// Assign parentWindow here
            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; }}if(type == TYPE_PRIVATE_PRESENTATION && ! displayContent.isPrivate()) { Slog.w(TAG_WM,"Attempted to add private presentation window to a non-private display. Aborting.");
            return WindowManagerGlobal.ADD_PERMISSION_DENIED;
        }

        AppWindowToken atoken = null;
        final booleanhasParent = parentWindow ! =null;
        // Use existing parent window token for child windows since they go in the same token
        // as there parent window so we can apply the same policy on them.
        WindowToken token = displayContent.getWindowToken(
                hasParent ? parentWindow.mAttrs.token : attrs.token);
        // If this is a child window, we want to apply the same type checking rules as the
        // parent window type.
        final int rootType = hasParent ? parentWindow.mAttrs.type : type;

        boolean addToastWindowRequiresToken = false;

      	// Token == null returns WindowManagerGlobal.ADD_BAD_APP_TOKEN
        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();
            final booleanisRoundedCornerOverlay = (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) ! =0;
            token = new WindowToken(this, binder, type, false, displayContent,
                    session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
        } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
            atoken = token.asAppWindowToken();
            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.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            } else if(type == TYPE_APPLICATION_STARTING && atoken.startingWindow ! =null) {
                Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"
                        + " starting window");
                returnWindowManagerGlobal.ADD_DUPLICATE_ADD; }}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; }}else if (rootType == TYPE_VOICE_INTERACTION) {
            if(token.windowType ! = TYPE_VOICE_INTERACTION) { Slog.w(TAG_WM,"Attempted to add voice interaction window with bad token "
                        + attrs.token + ". Aborting.");
                  returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if (rootType == TYPE_WALLPAPER) {
            if(token.windowType ! = TYPE_WALLPAPER) { Slog.w(TAG_WM,"Attempted to add wallpaper window with bad token "
                        + attrs.token + ". Aborting.");
                  returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if (rootType == TYPE_DREAM) {
            if(token.windowType ! = TYPE_DREAM) { Slog.w(TAG_WM,"Attempted to add Dream window with bad token "
                        + attrs.token + ". Aborting.");
                  returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
            if(token.windowType ! = TYPE_ACCESSIBILITY_OVERLAY) { Slog.w(TAG_WM,"Attempted to add Accessibility overlay window with bad token "
                        + attrs.token + ". Aborting.");
                returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if (type == TYPE_TOAST) {
            // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
            addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
                    callingUid, parentWindow);
            if(addToastWindowRequiresToken && token.windowType ! = TYPE_TOAST) { Slog.w(TAG_WM,"Attempted to add a toast window with bad token "
                        + attrs.token + ". Aborting.");
                returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if (type == TYPE_QS_DIALOG) {
            if(token.windowType ! = TYPE_QS_DIALOG) { Slog.w(TAG_WM,"Attempted to add QS dialog window with bad token "
                        + attrs.token + ". Aborting.");
                returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if(token.asAppWindowToken() ! =null) {
            Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
            // It is not valid to use an app token with other system types; we will
            // instead make a new token for it (as if null had been passed in for the token).
            attrs.token = null;
            token = new WindowToken(this, client.asBinder(), type, false, displayContent,
                    session.mCanAddInternalSystemWindow);
        }

        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], seq, attrs, viewVisibility, session.mUid,
                session.mCanAddInternalSystemWindow);
        if (win.mDeathRecipient == null) {
            // 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) {
            Slog.w(TAG_WM, "Adding window to Display that has been removed.");
            return WindowManagerGlobal.ADD_INVALID_DISPLAY;
        }

        final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
        displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
                Binder.getCallingUid());
        win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

        res = displayPolicy.prepareAddWindowLw(win, attrs);
        if(res ! = WindowManagerGlobal.ADD_OKAY) {return res;
        }

        final booleanopenInputChannels = (outInputChannel ! =null
                && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
        if  (openInputChannels) {
            win.openInputChannel(outInputChannel);
        }

      / /...
    if (reportNewConfig) {
        sendNewConfiguration(displayId);
    }

    Binder.restoreCallingIdentity(origId);

    return res;
}
Copy the code

The code is very long and the focus is on the error we reported. One is the type and the other is the token. Let’s look at the type first, because the token value is related to the type.

final inttype = attrs.type; Attrs is attrs of type LayoutParamsCopy the code

Why 2? Look at the code:

Window.java
private final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();

WindowManager.java
public LayoutParams(a) {
    super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    type = TYPE_APPLICATION;
    format = PixelFormat.OPAQUE;
}
Copy the code

Type = TYPE_APPLICATION, TYPE_APPLICATION=2. When type =2 parentWindow=null, the token is derived from attrs.token.

WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);
Copy the code

Attrs is LayoutParams again. Let’s look at attrs and see where the token comes from. We need to go back to the Windows ManagerGlobal addView method. After many times of debugging, we found the place where the token was assigned.

Performed parentWindow. AdjustLayoutParamsForSubWindow (wparams) after the token is assigned.

Activity parentWindow is valid if passed, Application parentWindow is null

Window.java

void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
    CharSequence curTitle = wp.getTitle();
    if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
            wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
        if (wp.token == null) {
            View decor = peekDecorView();
            if(decor ! =null) { wp.token = decor.getWindowToken(); }}if (curTitle == null || curTitle.length() == 0) {
            final StringBuilder title = new StringBuilder(32);
            if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA) {
                title.append("Media");
            } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY) {
                title.append("MediaOvr");
            } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
                title.append("Panel");
            } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) {
                title.append("SubPanel");
            } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL) {
                title.append("AboveSubPanel");
            } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) {
                title.append("AtchDlg");
            } else {
                title.append(wp.type);
            }
            if(mAppName ! =null) {
                title.append(":").append(mAppName); } wp.setTitle(title); }}else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
            wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
        // We don't set the app token to this system window because the life cycles should be
        // independent. If an app creates a system window and then the app goes to the stopped
        // state, the system window should not be affected (can still show and receive input
        // events).
        if (curTitle == null || curTitle.length() == 0) {
            final StringBuilder title = new StringBuilder(32);
            title.append("Sys").append(wp.type);
            if(mAppName ! =null) {
                title.append(":").append(mAppName); } wp.setTitle(title); }}else {
        if (wp.token == null) {
            wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
        }
        if ((curTitle == null || curTitle.length() == 0) && mAppName ! =null) { wp.setTitle(mAppName); }}if (wp.packageName == null) {
        wp.packageName = mContext.getPackageName();
    }
    if(mHardwareAccelerated || (mWindowAttributes.flags & FLAG_HARDWARE_ACCELERATED) ! =0) { wp.flags |= FLAG_HARDWARE_ACCELERATED; }}Copy the code

The type of the Window

The type of Window is determined by the Type property of the LayoutParams class in WindowManager. We can see that There are three main types of Windows.

  • APPLICATION_WINDOW: The value ranges from 1 to 99

  • SUB_WINDOW: The value ranges from 1000 to 1999

  • SYSTEM_WINDOW: The value ranges from 2000 to 2999

public int type;

public static final int FIRST_APPLICATION_WINDOW = 1;
public static final int TYPE_BASE_APPLICATION   = 1;
public static final int TYPE_APPLICATION        = 2;
public static final int TYPE_APPLICATION_STARTING = 3;
public static final int TYPE_DRAWN_APPLICATION = 4;
public static final int LAST_APPLICATION_WINDOW = 99;


public static final int FIRST_SUB_WINDOW = 1000;
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;
@UnsupportedAppUsage
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;


public static final int FIRST_SYSTEM_WINDOW     = 2000;
public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;
@Deprecated
public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
@Deprecated
public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
@Deprecated
public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
@Deprecated
public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;
@Deprecated
public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;
public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;
public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;
@Deprecated
public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;
public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;
public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;
@UnsupportedAppUsage
public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
public static final int TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16;
public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;
public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;
public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
@UnsupportedAppUsage
public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;
public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;
public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;
public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;
public static final int TYPE_PRESENTATION = FIRST_SYSTEM_WINDOW + 37;
public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
public static final int LAST_SYSTEM_WINDOW      = 2999;

public static final int INVALID_WINDOW_TYPE = -1;
Copy the code

The type value of AlertDialog is 2, and the type of PopupWindow is 1000

ParentWindow. AdjustLayoutParamsForSubWindow (wparams) parentWindow is actually the mParentWindow WindowManagerImpl object, This is related to The WindowManager, and the WindowManager is retrieved from the context, so there is a different performance, and we will finally analyze why there is a difference. Again, let’s review the WindowManager fetch

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    if (createContextThemeWrapper) {
        if (themeResId == Resources.ID_NULL) {
            final TypedValue outValue = new TypedValue();
            context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
            themeResId = outValue.resourceId;
        }
        mContext = new ContextThemeWrapper(context, themeResId);
    } else {
        mContext = context;
    }

    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

    final Window w = new PhoneWindow(mContext);
    mWindow = w;
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setOnWindowSwipeDismissedCallback(() -> {
        if(mCancelable) { cancel(); }}); w.setWindowManager(mWindowManager,null.null);
    w.setGravity(Gravity.CENTER);

    mListenersHandler = new ListenersHandler(this);
}
Copy the code

From the above code we can see that the mWindowManager is retrieved from the context and set to the PhoneWindow. How do we get WindowManager separately

MWindowManager is the WindowManager of the Activity or Application. AddView (mDecor, L);Copy the code
  • Activity Context
Activity.java

		@Override
    public Object getSystemService(@ServiceName @NonNull String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }

        if (WINDOW_SERVICE.equals(name)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }
Copy the code

Now, how does mWindowManager assign

Activity.java
  
		@UnsupportedAppUsage
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if(info.softInputMode ! = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); }if(info.uiOptions ! =0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mAssistToken = assistToken;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if(voiceInteractor ! =null) {
            if(lastNonConfigurationInstances ! =null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this.this, Looper.myLooper()); }}// The mToken has a valuemWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! =0);
        if(mParent ! =null) {
            mWindow.setContainer(mParent.getWindow());
        }
      	// Assign to mWindowManager
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);

        setAutofillOptions(application.getAutofillOptions());
        setContentCaptureOptions(application.getContentCaptureOptions());
    }
Copy the code
Window.java
  
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated;
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
  return new WindowManagerImpl(mContext, parentWindow);
}

// Note that parentWindow is not null and token is not null
private WindowManagerImpl(Context context, Window parentWindow) {
	mContext = context;
	mParentWindow = parentWindow;
}
Copy the code

ActivityContext getSystemService gained the windowManager, windowManager mParentWindow is the Activity of PhoneWindow so isn’t empty.

WindowManagerImpl.java

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (display == null) {
        throw new IllegalArgumentException("display must not be null");
    }
    if(! (paramsinstanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    if(parentWindow ! =null) {
      	// If parentWindow is not empty, the token is assigned a value
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
        // If there's no parent, then hardware acceleration for this view is
        // set from the application's hardware acceleration setting.
        final Context context = view.getContext();
        if(context ! =null&& (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) ! =0) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; }}// ...
}
Copy the code

So now that we can basically analyze it, let’s look at Application’s

  • Application Context

Application gets the WindowManager call getSystemService (ContextImpl)

Application.java -> ContextWrapper.java -> ContextImpl.java

@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}
Copy the code

ContextImpl’s call stack is shown below:

Here WindowManager is created, where mParentWindow is null

ApplicationContext. GetSystemService gained the windowManager windowManager mParentWindow is null

conclusion

So far the whole process has been analyzed, and we have solved the case. Each type of Context has its own mission, so be careful when you use it.