An_droid:一个_机器人

Android WindowManagerService之Win

2019-08-02  本文已影响0人  android_coder

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或是正在销毁中或是不可见

上一篇下一篇

猜你喜欢

热点阅读