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