当前位置: 首页 > 工具软件 > Simply Toast > 使用案例 >

从问题单处理了解Toast系统窗口显示、Activity窗口创建、WindowManagerService对系统窗口组织排布(一)

柯振濂
2023-12-01

     今天又收到测试同事的一个问题单,在手机上使用多任务键的截屏功能,然后将截屏图片删除掉,在通知栏中点击当前截屏中通知的分享功能,随便选择一种分享方式,因为截屏图片已经被删除,所以此时弹出toast提示分享文件不存在,但是奇怪的是,toast被当前的分享方式对话框盖住了,只有取消掉分享方式的对话框,才能看到文件不存在的toast提示。

     因为之前有看过老罗的相关博客,其中有介绍到WindowManagerService对系统窗口Zorder的排在组织,所以一拿到此题目,目标应该是非常明确的,肯定是分享方式的窗口的Zorder顺序大于toast的值了,所以排在toast窗口之上了。OK,目标明确了,那就从问题的出发点入手进行分析。

     首先很明确,当前的分享方式的窗口对应的是系统中ChooserActivity,我们可以看一下ChooserActivity在Manifest当中的注册定义,从定义中很明确的可以知道,如果我们想调用分享,那么也可以通知<action android:name="android.intent.action.CHOOSER" />这个action来调用,这个action同时也定义的Intent类中,大家不需要自己写。

        
    
    
            
     
     
                
      
      
                
      
      
                
      
      
            
     
     
        
    
    

     那么进一步明确,我们现在要确定的就是Toast的层值和ChooserActivity的层值,各位卓友还需要明确一点,就是当前窗口的层值是什么?它是怎么来的?这些问题可以参看老罗的博客:Android窗口管理服务WindowManagerService对窗口的组织方式分析Android窗口管理服务WindowManagerService计算窗口Z轴位置的过程分析相关文章,里边有非常详细的介绍,这里我们就大致走一下这个流程。

     首先,每一个应用程序添加一个窗口在起点是从ActivityThread类的handleResumeActivity方法中开始的,此方法的代码如下:

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, int reasonFlags/*LEUI-10257*/, boolean reallyResume) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;
            //[+LEUI-10257]
            r.reasonFlags = reasonFlags;
            //[-LEUI-10257]
            if (localLOGV) Slog.v(
                TAG, "Resume " + r + " started activity: " +
                a.mStartedActivity + ", hideForNow: " + r.hideForNow
                + ", finished: " + a.mFinished);

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            //[+LEUI-10257]
            checkReasonFlags(r);
            //[-LEUI-10257]
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                if((r.intent != null) && (r.intent.getFlags() & Intent.FLAG_ACTIVITY_ON_TOP) != 0) {
                    l.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
                } else {
                    l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                }
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }/*[LEUI-15516]*/else{
                    //See {@link Activity#setVisible}
                    Slog.i(LEUI_TAG, "Resume "+r+" is NOT going to show a UI(addView)");
                }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }

            // Get rid of anything left hanging around.
            cleanUpPendingRemoveWindows(r);

            // The window is now visible if it has been added, we are not
            // simply finishing, and we are not starting another activity.
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                if (r.newConfig != null) {
                    r.tmpConfig.setTo(r.newConfig);
                    if (r.overrideConfig != null) {
                        r.tmpConfig.updateFrom(r.overrideConfig);
                    }
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
                            + r.activityInfo.name + " with newConfig " + r.tmpConfig);
                    performConfigurationChanged(r.activity, r.tmpConfig);
                    freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
                    r.newConfig = null;
                }
                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
                        + isForward);
                WindowManager.LayoutParams l = r.window.getAttributes();
                if ((l.softInputMode
                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                        != forwardBit) {
                    l.softInputMode = (l.softInputMode
                            & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                            | forwardBit;
                    if (r.activity.mVisibleFromClient) {
                        ViewManager wm = a.getWindowManager();
                        View decor = r.window.getDecorView();
                        wm.updateViewLayout(decor, l);
                    }
                }
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                }/*[LEUI-15516]*/else{
                    //See {@link Activity#setVisible}
                    Slog.i(LEUI_TAG, "Resume "+r+" is NOT going to show a UI (makeVisible)");
                }
            }

            if (!r.onlyLocalRequest) {
                r.nextIdle = mNewActivities;
                mNewActivities = r;
                if (localLOGV) Slog.v(
                    TAG, "Scheduling idle handler for " + r);
                Looper.myQueue().addIdleHandler(new Idler());
            }
            r.onlyLocalRequest = false;

            // Tell the activity manager we have resumed.
            if (reallyResume) {
                try {
                    ActivityManagerNative.getDefault().activityResumed(token);
                } catch (RemoteException ex) {
                }
            }

        } else {
            // If an exception was thrown when trying to resume, then
            // just end this activity.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(token, Activity.RESULT_CANCELED, null, false);
            } catch (RemoteException ex) {
            }
        }
    }
     其中performResumeActivity调用最终就会调用到我们当前Activity的onResume方法,willBeVisible变量的意思很明显,就是我们即将添加的Activity窗口是否将要被显示,窗口添加的实际开始就是wm.addView(decor, l)这句引起的。decor对象就是我们在自己Activity的onCreate方法当中调用setContentView构建好的当前Activity的View对象,如果想要了解此过程,可参考Android应用程序窗口(Activity)的视图对象(View)的创建过程分析。l对象就是根据当前的窗口属性构建好的一个WindowManager.LayoutParams实体,wm是Activity类的getWindowManager()方法返回的mWindowManager对象,mWindowManager对象是在Activity实例化时attach()方法中进行赋值,addView方法的定义是在ViewManager当中的,ViewManager中WindowManager的父类,此方法的实现是在WindowManagerImpl类当中,实现非常简单,就是调用mGlobal.addView(view, params, mDisplay, mParentWindow),mGlobal是一个WindowManagerGlobal实体,WindowManagerGlobal的addView方法的代码如下,此代码在我的博客中也经常用到,也是应用程序添加窗口的必经之路,大家必须要熟悉:

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 (!(params instanceof 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() {
                        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);
                    }
                }
            }

            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.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }
     此方法的调用我们就不说了,大家如果不了解,可以自行百度。这里呢调用到了ViewRootImpl的setView方法,向WinowManagerService添加我们Activity窗口的工作也是在这里执行的,详细方法我就不引用了,我们直接看调用的地方:res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel)。

mWindowSession是一个IWindowSession对象,它其实就是WindowManagerService当中调用openSession方法,直接new构造的一个Session对象,而在应用程序当中我们拿到的是一个binder代理,实现过程这里就追究了,大家请自行了解。那么它的addToDisplay自然也就是调用了Session类的addToDisplay方法,其中实现非常简单,就是转而调用mService.addWindow(),mService就是WindowManagerService对象了,是在Session类的构造函数中传进来的。那么我们就进入WindowManagerService的addWindow方法看一下究竟。方法代码如下:

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

        boolean reportNewConfig = false;
        WindowState attachedWindow = null;
        long origId;
        final int type = attrs.type;

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

            final DisplayContent displayContent = getDisplayContentLocked(displayId);
            if (displayContent == null) {
                Slog.w(TAG, "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, "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, "Window " + client + " is already added");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }

            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                attachedWindow = windowForClientLocked(null, attrs.token, false);
                if (attachedWindow == null) {
                    Slog.w(TAG, "Attempted to add window with token that is not a window: "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                    Slog.w(TAG, "Attempted to add window with token that is a sub-window: "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
            }

            if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
                Slog.w(TAG, "Attempted to add private presentation window to a non-private display.  Aborting.");
                return WindowManagerGlobal.ADD_PERMISSION_DENIED;
            }

            boolean addToken = false;
            WindowToken token = mTokenMap.get(attrs.token);
            if (token == null) {
                if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                    Slog.w(TAG, "Attempted to add application window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (type == TYPE_INPUT_METHOD) {
                    Slog.w(TAG, "Attempted to add input method window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (type == TYPE_VOICE_INTERACTION) {
                    Slog.w(TAG, "Attempted to add voice interaction window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (type == TYPE_WALLPAPER) {
                    Slog.w(TAG, "Attempted to add wallpaper window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (type == TYPE_KEYGUARD_WALLPAPER) {
                    Slog.w(TAG, "Attempted to add keyguard wallpaper window with unknown token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (type == TYPE_DREAM) {
                    Slog.w(TAG, "Attempted to add Dream window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (type == TYPE_ACCESSIBILITY_OVERLAY) {
                    Slog.w(TAG, "Attempted to add Accessibility overlay window with unknown token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                token = new WindowToken(this, attrs.token, -1, false);
                addToken = true;
            } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                AppWindowToken atoken = token.appWindowToken;
                if (atoken == null) {
                    Slog.w(TAG, "Attempted to add window with non-application token "
                          + token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } else if (atoken.removed) {
                    Slog.w(TAG, "Attempted to add window with exiting application token "
                          + token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_APP_EXITING;
                }
                if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
                    // No need for this guy!
                    if (localLOGV) Slog.v(
                            TAG, "**** NO NEED TO START: " + attrs.getTitle());
                    return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;
                }
            } else if (type == TYPE_INPUT_METHOD) {
                if (token.windowType != TYPE_INPUT_METHOD) {
                    Slog.w(TAG, "Attempted to add input method window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_VOICE_INTERACTION) {
                if (token.windowType != TYPE_VOICE_INTERACTION) {
                    Slog.w(TAG, "Attempted to add voice interaction window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_WALLPAPER) {
                if (token.windowType != TYPE_WALLPAPER) {
                    Slog.w(TAG, "Attempted to add wallpaper window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_KEYGUARD_WALLPAPER) {
                if (token.windowType != TYPE_KEYGUARD_WALLPAPER) {
                    Slog.w(TAG, "Attempted to add keyguard wallpaper window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_DREAM) {
                if (token.windowType != TYPE_DREAM) {
                    Slog.w(TAG, "Attempted to add Dream window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_ACCESSIBILITY_OVERLAY) {
                if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
                    Slog.w(TAG, "Attempted to add Accessibility overlay window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (token.appWindowToken != null) {
                Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type);
                // 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, null, -1, false);
                addToken = true;
            }

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

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

            mPolicy.adjustWindowParamsLw(win.mAttrs);
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

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

            if (outInputChannel != null && (attrs.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                String name = win.makeInputChannelName();
                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
                win.setInputChannel(inputChannels[0]);
                inputChannels[1].transferTo(outInputChannel);

                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
            }

            // From now on, no exceptions or errors allowed!

            res = WindowManagerGlobal.ADD_OKAY;

            origId = Binder.clearCallingIdentity();

            if (addToken) {
                mTokenMap.put(attrs.token, token);
            }
            win.attach();
            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);
                }
            }

            if (type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) {
                token.appWindowToken.startingWindow = win;
                if (DEBUG_EUI_STARTING_WINDOW || DEBUG_STARTING_WINDOW) Slog.v (TAG, "addWindow: " + token.appWindowToken
                        + " startingWindow=" + win);
            }

            boolean imMayMove = true;

            if (type == TYPE_INPUT_METHOD) {
                win.mGivenInsetsPending = true;
                mInputMethodWindow = win;
                addInputMethodWindowToListLocked(win);
                imMayMove = false;
            } else if (type == TYPE_INPUT_METHOD_DIALOG) {
                mInputMethodDialogs.add(win);
                addWindowToListInOrderLocked(win, true);
                moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
                imMayMove = false;
            } else {
                addWindowToListInOrderLocked(win, true);
                if (type == TYPE_WALLPAPER || type == TYPE_KEYGUARD_WALLPAPER) {
                    getMatchedWallpaperOffsetParams(type).mLastWallpaperTimeoutTime = 0;
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if (requestWallpaper(win) != 0) {
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if (mWallpaperTarget != null
                        && mWallpaperTarget.mLayer >= win.mBaseLayer) {
                    // If there is currently a wallpaper being shown, and
                    // the base layer of the new window is below the current
                    // layer of the target window, then adjust the wallpaper.
                    // This is to avoid a new window being placed between the
                    // wallpaper and its target.
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                }
            }

            final WindowStateAnimator winAnimator = win.mWinAnimator;
            winAnimator.mEnterAnimationPending = true;
            winAnimator.mEnteringAnimation = true;

            if (displayContent.isDefaultDisplay) {
                mPolicy.getInsetHintLw(win.mAttrs, mRotation, outContentInsets, outStableInsets,
                        outOutsets);
            } else {
                outContentInsets.setEmpty();
                outStableInsets.setEmpty();
            }

            if (mInTouchMode) {
                res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
            }
            if (win.mAppToken == null || !win.mAppToken.clientHidden) {
                res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
            }

            mInputMonitor.setUpdateInputWindowsNeededLw();
            //[+LEUI-15516]
            //win.canReceiveKeys return FALSE here when win.mAppToken != null,
            //because viewVisibility == View.INVISIBLE that set by ActivityThread.handleResumeActivity
            //[-LEUI-15516]
            boolean focusChanged = false;
            if (win.canReceiveKeys()) {
                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                        false /*updateInputWindows*/);
                if (focusChanged) {
                    imMayMove = false;
                }
            }

            if (imMayMove) {
                moveInputMethodWindowsIfNeededLocked(false);
            }

            assignLayersLocked(displayContent.getWindowList());
            // Don't do layout here, the window must call
            // relayout to be displayed, so we'll do it there.

            if (focusChanged) {
                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
            }
            mInputMonitor.updateInputWindowsLw(false /*force*/);

            if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG, "addWindow: New client "
                    + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
            else{//[+LEUI-15784]
                Slog.i(TAG,"addWindow: window="+win);
            }

            if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
                reportNewConfig = true;
            }
        }

        if (reportNewConfig) {
            sendNewConfiguration();
        }

        Binder.restoreCallingIdentity(origId);

        return res;
    }
     这个方法会作的工作有几点:1、检查当前应用的添加窗口的权限;2、根据我们传进来的窗口布局参数的type类型,判断要添加的窗口类型,同时构建一个WindowToken对象;3、构建WindowState对象,此对象非常重要,它是我们当前Activity在WindowManagerService窗口管理中的代表;4、将ViewRootImpl传进来的一对InputChannel进行实例化并进行注册,因为ViewRootImpl中构建的InputChannel只是空壳,在native层没有对应的描述符,所以需要实例化;5、将构建好的WindowState对象添加到WindowManagerService的类变量mWindowMap中,它是管理所有WindowState对象的;6、调用assignLayersLocked对所有窗口进行重新排布。到这里,我们的Activity的窗口就添加到系统当中了。
     好了,回到主题,添加窗口的过程我们大概走了一下,那么窗口层值是什么呢?
     在WindowState类中有一个mBaseLayer和一个mSubLayer变量,mBaseLayer表示当前窗口的基本层值,mSubLayer表示当前窗口在子窗口中的层值,关于这两个变量的实际意义,大家如果还不明白,请自行百度。我们来看一下这两个变量的值是怎么定的呢?这两个变量的值都是在WindowState对象的构造方法中确定的,意思也就是说,当系统调用WindowManagerService的addWindow方法为我们的Activity添加窗口的时候,此时目标窗口的层值就可以确定了,层值也会在构造WindowState对象时确定好,而且不会再改变。mSubLayer比较简单,它只在当前窗口有子窗口时才有意义,这种情况下就调用mPolicy.subWindowTypeToLayerLw(a.type)对它进行赋值,否则就赋值为0;mBaseLayer有当前窗口是否有子窗口的情况下都是调用mPolicy.windowTypeToLayerLw()方法进行赋值的,不过两个调用传入的参数不一样。mPolicy是一个WindowManagerPolicy对象,它是在WindowState构造函数中赋值的,也就是WindowManagerService的mPolicy对象,实际是一个PhoneWindowManager,我们来看一下它的windowTypeToLayerLw方法:
    @Override
    public int windowTypeToLayerLw(int type) {
        if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
            return 2;
        }
        switch (type) {
        case TYPE_PRIVATE_PRESENTATION:
            return 2;
        case TYPE_WALLPAPER:
        case TYPE_KEYGUARD_WALLPAPER:
            // wallpaper is at the bottom, though the window manager may move it.
            return 2;
        case TYPE_PHONE:
            return 3;
        case TYPE_SEARCH_BAR:
        case TYPE_VOICE_INTERACTION_STARTING:
            return 4;
        case TYPE_VOICE_INTERACTION:
            // voice interaction layer is almost immediately above apps.
            return 5;
        case TYPE_INPUT_CONSUMER:
            return 6;
        case TYPE_SYSTEM_DIALOG:
            return 7;
        case TYPE_TOAST:
            // toasts and the plugged-in battery thing
            return 8;
        case TYPE_PRIORITY_PHONE:
            // SIM errors and unlock.  Not sure if this really should be in a high layer.
            return 9;
        case TYPE_DREAM:
            // used for Dreams (screensavers with TYPE_DREAM windows)
            return 10;
        case TYPE_SYSTEM_ALERT:
            // like the ANR / app crashed dialogs
            return 11;
        case TYPE_INPUT_METHOD:
            // on-screen keyboards and other such input method user interfaces go here.
            return 12;
        case TYPE_INPUT_METHOD_DIALOG:
            // on-screen keyboards and other such input method user interfaces go here.
            return 13;
        case TYPE_KEYGUARD_SCRIM:
            // the safety window that shows behind keyguard while keyguard is starting
            return 14;
        case TYPE_STATUS_BAR_SUB_PANEL:
            return 15;
        case TYPE_STATUS_BAR:
            return 16;
        case TYPE_CONTROL_CENTER_PANEL:
            return 17;
        case TYPE_STATUS_BAR_PANEL:
            return 17;
        case TYPE_KEYGUARD_DIALOG:
            return 18;
        case TYPE_VOLUME_OVERLAY:
            // the on-screen volume indicator and controller shown when the user
            // changes the device volume
            return 19;
        case TYPE_SYSTEM_OVERLAY:
            // the on-screen volume indicator and controller shown when the user
            // changes the device volume
            return 20;
        case TYPE_NAVIGATION_BAR:
            // the navigation bar, if available, shows atop most things
            return 21;
        case TYPE_NAVIGATION_BAR_PANEL:
            // some panels (e.g. search) need to show on top of the navigation bar
            return 22;
        case TYPE_SYSTEM_ERROR:
            // system-level error dialogs
            return 23;
        case TYPE_MAGNIFICATION_OVERLAY:
            // used to highlight the magnified portion of a display
            return 24;
        case TYPE_DISPLAY_OVERLAY:
            // used to simulate secondary display devices
            return 25;
        case TYPE_DRAG:
            // the drag layer: input for drag-and-drop is associated with this window,
            // which sits above all other focusable windows
            return 26;
        case TYPE_ACCESSIBILITY_OVERLAY:
            // overlay put by accessibility services to intercept user interaction
            return 27;
        case TYPE_SECURE_SYSTEM_OVERLAY:
            return 28;
        case TYPE_BOOT_PROGRESS:
            return 29;
        case TYPE_POINTER:
            // the (mouse) pointer layer
            return 30;
        }
        Log.e(TAG, "Unknown window type: " + type);
        return 2;
    }

    /**
     * @hide
     */
    @Override
    public int windowTypeToLayerLw(WindowManager.LayoutParams attr) {
        int type = attr.type;
        if (type == WindowManager.LayoutParams.TYPE_TOAST
                && (attr.leuiFlags & WindowManager.LayoutParams.LEUI_FLAG_TOAST_SHOW_WHEN_LOCKED) != 0)
            type = WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM;
        return windowTypeToLayerLw(type);
    }
     很明显可以看到两个方法的区别,参数为WindowManager.LayoutParams的方法先把type进行了一下换算,然后将换算后的值作为参数,调用了参数为int的方法。从这里我们也可以看出来android系统中的窗口类型,TYPE_WALLPAPER表示壁纸窗口,TYPE_KEYGUARD_WALLPAPER表示锁屏的壁纸,TYPE_SYSTEM_DIALOG表示系统弹窗,比如ANR、异常终止等,TYPE_TOAST表示Toast窗口。大家可以看到在方法开始,直接对比type值如果大于等于FIRST_APPLICATION_WINDOW并且小于等于LAST_APPLICATION_WINDOW,则直接返回2,这里也就是我们最常用的应用程序的窗口Application。FIRST_APPLICATION_WINDOW和LAST_APPLICATION_WINDOW的值定义在WindowManager.LayoutParams中,值分别为1和99。那么我们的目标就非常明确了,应该就是创建ChooserActivity时的mBaseLayer的值大于Toast窗口的mBaseLayer值,才导致Toast窗口被覆盖了,好,有了推断的结论,来验证一下,在源码中加上相应的日志,执行mm,然后push替换,果然,Toast窗口层值为81000,而ChooserActivity的层值为171000,远远大于Toast的窗口层值,那么我们现在要作的就是要找到为什么ChooserActivity的窗口层值这么大了!
     从这里往上分析,日志中还可以看到ChooserActivity对象的type值为2014,这个值是怎么来的呢?type值是取的attrs.type,attrs是在ViewRootImpl中调用addToDisplay方法时传进来的,而addToDisplay中的参数又是其类变量mWindowAttributes,它是在setView为调用mWindowAttributes.copyFrom(attrs)从方法参数中拷贝过来的一份,再往上,方法参数attrs是在WindowManagerGlobal的addView方法中传进来的,addView方法整个调用过程只是使用此参数,并未对其值进行修改,那么再往前,此处的attrs就是WindowManagerImpl中传进来的,而WindowManagerImpl中的参数又是在ActivityThread的wm.addView(decor, l)调用时传入的,也就是我们前面所说的 l,好了,l 就是这个调用的根源了,我们从代码可以看出,如果进入if分支,则 l 的type属性值则被赋值为WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,它的定义在WindowManager.LayoutParams当中,值正好是2014:public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14,和我们的日志完全对应,也证明我们的分析是正确的。那么结论就出来了,就是因为多任务键截屏后,点击分享时,启动ChoosetActivity的intent不为空并且FLAG_ACTIVITY_ON_TOP标志不等于0,才导致ChoosetActivity的启动布局参数被放大,最终导致其覆盖在Toast系统窗口之上。
     好了,这一篇博客就写到这里了,我们要分析的还有Toast的显示过程,因博客篇幅过长,所以Toast显示过程见下一篇博客,静请期待,谢谢大家。
 类似资料: