美文网首页An_droid:一个_机器人
Android WindowManagerService之Win

Android WindowManagerService之Win

作者: android_coder | 来源:发表于2019-08-02 17:49 被阅读0次

    WindowState是WindowManagerService端的窗口对象,是在addWindow的时候创建的,当前系统里面所有的窗口windowState被保存在一个WindowHashMap的对象中,这里主要是记录下WindowState相关的内容

    1:WindowState的创建时机

    WindowManagerService::addWindow

    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) {----------------------->displayContent就是一块显示屏幕
                    Slog.w(TAG_WM, "Adding window to Display that has been removed.");
                    return WindowManagerGlobal.ADD_INVALID_DISPLAY;
                }
                final boolean hasStatusBarServicePermission =
                        mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
                                == PackageManager.PERMISSION_GRANTED;
                mPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission);
                win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
               //准备添加窗口,主要是对statusbar和navgationbar做一些处理
                res = mPolicy.prepareAddWindowLw(win, attrs);
                if (res != WindowManagerGlobal.ADD_OKAY) {
                    return res;
                }
                final boolean openInputChannels = (outInputChannel != null
                        && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
                if  (openInputChannels) {
                    win.openInputChannel(outInputChannel);
                }
                // If adding a toast requires a token for this app we always schedule hiding
                // toast windows to make sure they don't stick around longer then necessary.
                // We hide instead of remove such windows as apps aren't prepared to handle
                // windows being removed under them.
                //
                // If the app is older it can add toasts without a token and hence overlay
                // other apps. To be maximally compatible with these apps we will hide the
                // window after the toast timeout only if the focused window is from another
                // UID, otherwise we allow unlimited duration. When a UID looses focus we
                // schedule hiding all of its toast windows.
                if (type == TYPE_TOAST) {
                    if (!getDefaultDisplayContentLocked().canAddToastWindowForUid(callingUid)) {
                        Slog.w(TAG_WM, "Adding more than one toast window for UID at a time.");
                        return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                    }
                    // Make sure this happens before we moved focus as one can make the
                    // toast focusable to force it not being hidden after the timeout.
                    // Focusable toasts are always timed out to prevent a focused app to
                    // show a focusable toasts while it has focus which will be kept on
                    // the screen after the activity goes away.
                    if (addToastWindowRequiresToken
                            || (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
                            || mCurrentFocus == null
                            || mCurrentFocus.mOwnerUid != callingUid) {
                        mH.sendMessageDelayed(
                                mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
                                win.mAttrs.hideTimeoutMilliseconds);
                    }
                }
                // From now on, no exceptions or errors allowed!
                res = WindowManagerGlobal.ADD_OKAY;
                if (mCurrentFocus == null) {
                    mWinAddedSinceNullFocus.add(win);
                }
                if (excludeWindowTypeFromTapOutTask(type)) {
                    displayContent.mTapExcludedWindows.add(win);
                }
                origId = Binder.clearCallingIdentity();
                win.attach();---->主要是请求surfacefligner为其分配surface
                mWindowMap.put(client.asBinder(), win);-------->将其存储到windowMap中
    

    2:WindowState的成员变量和方法

    我们一般分析窗口的时候都是dumpsys下其他相关的信息,然后我们根据里面的一些信息做一些分析

     Window #6 Window{d2bf214 u0 com.mflashlight.android/com.mflashlight.android.app.MainActivity}:
        mDisplayId=0 stackId=10 mSession=Session{7d12165 12000:u0a10059} mClient=android.os.BinderProxy@e7a8467
        mOwnerUid=10059 mShowToOwnerOnly=true package=com.mflashlight.android appop=NONE
        mAttrs={(0,0)(fillxfill) sim={adjust=pan forwardNavigation} ty=BASE_APPLICATION wanim=0x10302f8
          fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS
          pfl=FORCE_DRAW_STATUS_BAR_BACKGROUND
          vsysui=LAYOUT_STABLE LAYOUT_FULLSCREEN}
        Requested w=720 h=1520 mLayoutSeq=1059
        mBaseLayer=21000 mSubLayer=0 mAnimLayer=0+=0 mLastLayer=0
        mToken=AppWindowToken{23a389e token=Token{d9706d9 ActivityRecord{d278d20 u0 com.mflashlight.android/.app.MainActivity t13}}}
        mAppToken=AppWindowToken{23a389e token=Token{d9706d9 ActivityRecord{d278d20 u0 com.mflashlight.android/.app.MainActivity t13}}}
         isAnimatingWithSavedSurface()= mAppDied=false    drawnStateEvaluated=true    mightAffectAllDrawn=true
        mViewVisibility=0x0 mHaveFrame=true mObscured=false
        mSeq=0 mSystemUiVisibility=0x500
        mGivenContentInsets=[0,0][0,0] mGivenVisibleInsets=[0,0][0,0]
        mFullConfiguration={0theme1.0 ?mcc?mnc [zh_CN_#Hans] ldltr sw360dp w360dp h682dp 320dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 720, 1520) mAppBounds=Rect(0, 60 - 720, 1424) mWindowingMode=fullscreen mActivityType=standard} s.6}
        mLastReportedConfiguration={0theme1.0 ?mcc?mnc [zh_CN_#Hans] ldltr sw360dp w360dp h682dp 320dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 720, 1520) mAppBounds=Rect(0, 60 - 720, 1424) mWindowingMode=fullscreen mActivityType=standard} s.6}
        mHasSurface=true isReadyForDisplay()=true canReceiveKeys()=true mWindowRemovalAllowed=false
        mFrame=[0,0][720,1520] last=[0,0][720,1520]
        Frames: containing=[0,0][720,1520] parent=[0,0][720,1520]
            display=[0,0][720,1520] overscan=[0,0][720,1520]
            content=[0,60][720,1424] visible=[0,60][720,1424]
            decor=[0,0][720,1520]
            outset=[0,0][0,0]
        Cur insets: overscan=[0,0][0,0] content=[0,60][0,96] visible=[0,60][0,96] stable=[0,60][0,96] surface=[0,0][0,0] outsets=[0,0][0,0] cutout=DisplayCutout{insets=Rect(0, 60 - 0, 0) boundingRect=Rect(240, 0 - 480, 60)}
        Lst insets: overscan=[0,0][0,0] content=[0,60][0,96] visible=[0,60][0,96] stable=[0,60][0,96] physical=[0,0][0,0] outset=[0,0][0,0] cutout=com.android.server.wm.utils.WmDisplayCutout@135aa601
        WindowStateAnimator{11dff06 com.mflashlight.android/com.mflashlight.android.app.MainActivity}:
          mSurface=Surface(name=com.mflashlight.android/com.mflashlight.android.app.MainActivity)/@0xe6fdf1d
          Surface: shown=true layer=0 alpha=1.0 rect=(0.0,0.0) 720 x 1520 transform=(1.0, 0.0, 1.0, 0.0)
          mDrawState=HAS_DRAWN       mLastHidden=false
          mSystemDecorRect=[0,0][720,1520] mLastClipRect=[0,0][720,1520]
        isOnScreen=true
        isVisible=true
    

    2.1:每个成员变量的含义

    2.1.1:DisplayContent对应的id

    mDisplayId=0:显示设备,有的设备是支持多屏显示的,0代表的是默认

    2.1.2:StatckId:

    stackId = 10:代表的是在哪个栈中,WMS是以栈的方式管理Window

    2.1.3:Session对象mSession

    每一个ui进程对应的session是相同的,不同的ui进程session对象是不同的,session是应用程序和wms通信的桥梁,其在ViewRootImpl里面创建的,Session继承了IWindowSession.Stub,可以知道WindowState保存的是本地的对象,ViewRootImpl持有的是代理对象

    2.1.4:mClient

    Window类对象mClient,mClient是一个代理对象,本地对象保存在ViewRootImpl中的mWindow中。WMS利用mClient来通知ViewRootImpl一些状态的改变等。mClient代表的是UI进程侧的一个窗口。在应用程序端真正的窗口对象是final W mWindow

    2.1.5:mOwnerUid

    该变量保存的是UID,UID在Linux中是为多用户设计的,而在Android中赋予了新的使命--数据共享,android为每个应用几乎都分配了不同的UID,如果要实现两个程序的互访,可以定义相同的android:sharedUserId,并且签名相同便可互访。uid是在应用安装的时候被赋予的,后面都不会改变直到应用被卸载重新安装

    2.1.6mShowToOwnerOnly

    android是支持多用户的,该变量控制的是是否支持仅在当面用户下才显示
    win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));从这个判断可以看出是跟窗口的属性有关

    2.1.7appop

    我们知道添加一个窗口是需要权限的,特别是系统窗口,appop保存的是这个特殊的权限,很多地方权限管理AppOpsManager会检查这个窗口是否能申请到对应的资源

    2.1.8mBaseLayer
        mBaseLayer = mPolicy.getWindowLayerLw(this)
                * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
    

    android中全部的WindowState都是根据窗体Z轴值来排列的。高度越高,越可见。这个Z轴高度的计算是基于mBaseLayer、可见性

    2.1.9mSubLayer
     mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
     default int getSubWindowLayerFromTypeLw(int type) {
            switch (type) {
                case TYPE_APPLICATION_PANEL:
                case TYPE_APPLICATION_ATTACHED_DIALOG:
                    return APPLICATION_PANEL_SUBLAYER;
                case TYPE_APPLICATION_MEDIA:
                    return APPLICATION_MEDIA_SUBLAYER;
                case TYPE_APPLICATION_MEDIA_OVERLAY:
                    return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
                case TYPE_APPLICATION_SUB_PANEL:
                    return APPLICATION_SUB_PANEL_SUBLAYER;
                case TYPE_APPLICATION_ABOVE_SUB_PANEL:
                    return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;
            }
            Slog.e("WindowManager", "Unknown sub-window type: " + type);
            return 0;
        }
    
    2.1.10WindowToken类对象mToken

    WindowToken是窗口令牌,是窗口分组的依据,相同的windowtoken放在一组,每一个窗口都是通过一个WindowState对象来描述的。例如,一个Activity组件窗口可能有一个启动窗口(Starting Window),还有若干个子窗口,那么这些窗口就会组成一组,并且都是以Activity组件在Window管理服务WindowManagerService中所对应的AppWindowToken对象为令牌的。从抽象的角度来看,就是在Window管理服务WindowManagerService中,每一个令牌(AppWindowToken或者WindowToken)都是用来描述一组窗口(WindowState)的,并且每一个窗口的子窗口也是与它同属于一个组,即都有着相同的令牌。

     AppWindowToken atoken = null;
                final boolean hasParent = 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;
    
                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.");
                            return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                        }
                    }
                    final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                    final boolean isRoundedCornerOverlay =
                            (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");
                        return WindowManagerGlobal.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.");
                          return WindowManagerGlobal.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.");
                          return WindowManagerGlobal.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.");
                          return WindowManagerGlobal.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.");
                          return WindowManagerGlobal.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.");
                        return WindowManagerGlobal.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.");
                        return WindowManagerGlobal.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.");
                        return WindowManagerGlobal.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);
                }
    
    2.1.11AppWindowToken

    该类用来描述一个Activity窗口令牌,对于输入法,壁纸,自己addView的窗口,及PopupWindow等都是用WindowToken来描述窗口令牌的。我们都知道AMS和WMS之间的数据是关联在一起的,ActivityRecord是AMS端的activity的描述,WindowState是WMS端的窗window描述,appwindowtoken分别保存在三个地方:应用程序段(app端),ams端和wms端,AppWindowToken是WindowToken的子类,AppwindowToken是在AppWindowContainerController中创建的

    2.1.12mHasSurface

    该变量指示是否为该窗口new 一个SurfaceControl对象,即调用WindowState.mWinAnimator.createSurfaceLocked()来创建。SurfaceControl是WMS与SurfaceFlinger通信的接口之一,每一个WindowState都对应有一个SurfaceControl,还对应在SurfaceFlinger中一个Layer对象。

    2.1.13isReadyForDisplay
        boolean isReadyForDisplay() {
            if (mToken.waitingToShow && mService.mAppTransition.isTransitionSet()) {
                return false;
            }
            return mHasSurface && mPolicyVisibility && !mDestroying
                    && ((!isParentWindowHidden() && mViewVisibility == View.VISIBLE && !mToken.isHidden())
                            || mWinAnimator.isAnimationSet());
        }
    

    可以看出只有当一个窗口是显示状态的时候该值才为true

    2.1.14canReceiveKeys
        boolean canReceiveKeys() {
            return isVisibleOrAdding()
                    && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
                    && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
                    && (mAppToken == null || mAppToken.windowsAreFocusable())
                    && !canReceiveTouchInput();
        }
    

    可以看出一定是需要可见的,并且没有处于移除状态,窗口没有带flag:FLAG_NOT_FOCUSABLE

    2.1.15WindowStateAnimator

    窗口动画

    2.1.16Frames
       Frames: containing=[0,0][720,1520] parent=[0,0][720,1520]
            display=[0,0][720,1520] overscan=[0,0][720,1520]
            content=[0,60][720,1424] visible=[0,60][720,1424]
            decor=[0,0][720,1520]
            outset=[0,0][0,0]
    

    containing--->mContainingFrame,在全屏显示的时候该值和parentFrame是一样的,parent--->mParentFrame,指的是父窗口的大小,display---->mDisplayFrame,默认情况下是指整个显示屏幕,也就是屏幕的大小,overscan----->mOverscanFrame,设备的屏幕大小,content----->mContentFrame,窗口的内容区域大小,visible--->mVisibleFrame,可见区域大小,decor---->mDecorFrame,装饰区域大小,除去状态栏和导航栏

    2.1.17mLayoutAttached
            if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
                // The multiplier here is to reserve space for multiple
                // windows in the same type layer.
                mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
                        * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
                mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
                mIsChildWindow = true;
                if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + parentWindow);
                parentWindow.addChild(this, sWindowSubLayerComparator);
                mLayoutAttached = mAttrs.type !=
                        WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
                mIsImWindow = parentWindow.mAttrs.type == TYPE_INPUT_METHOD
                        || parentWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
                mIsWallpaper = parentWindow.mAttrs.type == TYPE_WALLPAPER;
            } 
    

    可以看出当其为子窗口的时候才为true

    2.1.18mWallpaperVisible

    表明的是壁纸窗口是否可见

    2.1.19isVisible

    表示的当前窗口是否可见,也就是当前可以看到的窗口,可能是应用程序的窗口,状态栏等

    2.1.20isOnScreen
        boolean isOnScreen() {
            if (!mHasSurface || mDestroying || !mPolicyVisibility) {
                return false;
            }
            final AppWindowToken atoken = mAppToken;
            if (atoken != null) {
                return ((!isParentWindowHidden() && !atoken.hiddenRequested)----->父窗口没有被隐藏并且没有请求隐藏
                        || mWinAnimator.isAnimationSet());------>已经设置了窗口动画
            }
            return !isParentWindowHidden() || mWinAnimator.isAnimationSet();
        }
    

    如果当前窗口没有surface或是正在销毁中或是不可见

    相关文章

      网友评论

        本文标题:Android WindowManagerService之Win

        本文链接:https://www.haomeiwen.com/subject/wqlrrctx.html