您的位置:首页 > 其它

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

2016-09-28 12:19 459 查看
     今天又收到测试同事的一个问题单,在手机上使用多任务键的截屏功能,然后将截屏图片删除掉,在通知栏中点击当前截屏中通知的分享功能,随便选择一种分享方式,因为截屏图片已经被删除,所以此时弹出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显示过程见下一篇博客,静请期待,谢谢大家。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐