Android WindowManagerService

906 阅读4分钟

大家都知道WMS是很重要的一个服务,它主要的功能有:

  • 窗口管理:负责添加、启动和删除窗口,窗口的大小,层级也是WMS管理
  • 窗口动画:窗口切换动画管理,管理类为WindowAnimator
  • 输入系统的中转站:InputManagerService对触摸事件进行处理,WMS作为中转站,寻找合适的窗口来处理反馈信息。
  • Surface管理:每个窗口有一个Surface,Surface来绘制。

1630414161717.jpg

WMS的创建

WMS是在SystemServer进程中被创建的,SystemServer的main方法中。

在SystemServer的main方法中,系统分别调用了startBootstrapServices、startCoreServices、startOtherServices三个方法来分别启动引导服务、核心服务、其他服务。

WMS就是在startOtherServices方法中启动的。

在startOtherServices方法中会先获取Watchdog实例,Watchdog是用来监控关键服务的运行状况。然后会创建IMS(InputManagerService)。接着通过WindowManagerService的main方法获取到WMS的实例,该方法会把IMS传入。然后将IMS,WMS都注册到ServiceManager中。最后调用WMS的displayReady方法来初始化屏幕显示信息,调用WMS的systemReady方法来通知WMS系统的初始化工作已经完成了。

下面看看WMS的构造方法:

private WindowManagerService(Context context, InputManagerService inputManager,
        boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
        WindowManagerPolicy policy) {
    ...
    mInputManager = inputManager; // Must be before createDisplayContentLocked.
    ...
    mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
    mDisplays = mDisplayManager.getDisplays();
    for (Display display : mDisplays) {
        createDisplayContentLocked(display);
    }
    ...
    mActivityManager = ActivityManager.getService();
    ...
    mAnimator = new WindowAnimator(this);

    mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
            com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);

    LocalServices.addService(WindowManagerInternal.class, new LocalService());
    initPolicy();

    // Add ourself to the Watchdog monitors.
    Watchdog.getInstance().addMonitor(this);
    ...
}

代码经过了简化,我们可以看到,在WMS的构造方法中,先对inputManager进行了赋值。

然后获取到了DisplayManager,通过getDisplays方法获取Display数组,按理来说每个显示设备都会有一个Display实例。

通过遍历将display通过createDisplayContentLocked方法封装成DisplayContent,并设置在WMS中,DisplayContent用来描述一块屏幕。

然后会获取AMS实例,创建WindowAnimator来管理窗口动画。

接着初始化窗口管理策略的接口类WindowManagerPolicy,这个用来定义一个窗口策略要遵循的规范。然后通过addMonitor将WMS添加到Watchdog里面。Watchdog每分钟会对系统服务进行检查,如果出现死锁,就会杀死Watchdog所在的进程,就是SystemServer进程。

WMS的重要成员

了解WMS首先需要链接WMS中重要的成员变量:

//WindowManagerPolicy是窗口管理策略的接口类,用来定义一个窗口策略的通用规范,
//并提供WindowManager所有的特定UI行为。具体实现类是PhoneWindowManager,
//这个实现类在WMS创建时被创建。
final WindowManagerPolicy mPolicy;

final IActivityManager mActivityManager;
final ActivityManagerInternal mAmInternal;

final AppOpsManager mAppOps;

final DisplaySettings mDisplaySettings;

//Session前面提到过,用于进程间通信,其他应用程序想要和WMS通信就需要通过Session,
//每个应用程序进程都会有一个Session。WMS保存Session用来记录所有向WMS提出窗口管理服务的客户端
final ArraySet<Session> mSessions = new ArraySet<>();

//WindowHashMap的value限定了是WindowState,WindowState保存的是窗口的信息,
//是用来描述一个窗口的,所以mWindowMap中保存了所有窗口的信息
final WindowHashMap mWindowMap = new WindowHashMap();

//WindowToken有两个作用,一个是当作窗口令牌,当应用程序想要像WMS申请创建一个窗口,
//需要向WMS出示有效的WindowToken。AppWindowTOken是WindowToken的子类,用来描述应用程序的
//WindowToken结构,应用程序中每个Activity都对应一个AppWindowToken。
//WindowToken还会将同一个Acticity的窗口合集在一起,方便管理。
//mFinishedStarting就是用来储存已经启动完成的Acticity的AppWindowToken。
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();

 //用来储存已经绘制完成,但是不需要展示任何保存的surface的界面的AppWindowToken
final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>();

//保存正在调整大小的窗口的信息。
final ArrayList<WindowState> mResizingWindows = new ArrayList<>();

//用于管理窗口动画
final WindowAnimator mAnimator;

//Handler类,用户将任务加入到主线程消息队列
final H mH = new H();

//输入系统的管理者,会对触摸事件进行管理。
final InputManagerService mInputManager;

Window的添加过程

无论是Acticity还是系统窗口,他们添加过程都会调用WMS的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];
    
    //这里最终调用的是PhoneWindowManager的checkAddPermission方法,
    //根据传入的attrs中的type来判断是否可以添加窗口,如果不行,直接返回ADD_INVALID_DISPLAY状态
    int res = mPolicy.checkAddPermission(attrs, appOp);
    if (res != WindowManagerGlobal.ADD_OKAY) {
        return res;
    }
    ...
    synchronized(mWindowMap) {
        if (!mDisplayReady) {
            throw new IllegalStateException("Display has not been initialialized");
        }

        //根据displayId获取窗口要添加到哪个DisplayContent,
        //如果没有找到返回ADD_INVALID_DISPLAY状态。
        //DisplayContent用来描述一块屏幕
        final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
        if (displayContent == null) {
            Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
                    + displayId + ".  Aborting.");
            return WindowManagerGlobal.ADD_INVALID_DISPLAY;
        }
        ...
        
        //这里判断type是否是大于1000小于1999,如果是,说明是一个子窗口,类似PopupWindow这种
        if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
        
            //通过windowForClientLocked方法,传入token,token是IBinder类对象,
            //方法内部会把token作为key,从mWindowMap中查找该子窗口的父窗口
            parentWindow = windowForClientLocked(null, attrs.token, false);
            //对父窗口进行判断,如果父窗口为null或者父窗口type不正确,都会返回错误的状态。
            if (parentWindow == null) {
                Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
                      + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
            }
            if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                    && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
                        + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
            }
        }
        ...
        AppWindowToken atoken = null;
        final boolean hasParent = parentWindow != null;
        
        //通过getWindowToken方法获取WindoToken
        WindowToken token = displayContent.getWindowToken(
                hasParent ? parentWindow.mAttrs.token : attrs.token);
                
        //根据是否有父窗口,获取正确的type
        final int rootType = hasParent ? parentWindow.mAttrs.type : type;

        boolean addToastWindowRequiresToken = false;
        
        //如果token为null,进行其他判断
        if (token == null) {
        
            //如果添加的type为应用程序窗口,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();
            
            //进过一系列的判断,会在这里创建WindowToken,这里传入的第四个参数为false,代表这里是
            //隐式创建的
            token = new WindowToken(this, binder, type, false, displayContent,
                    session.mCanAddInternalSystemWindow);
        } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
        
            //如果token不为null,创建的是应用程序窗口,直接获取AppWindowToken
            atoken = token.asAppWindowToken();
            //根据atoken是否为null返回不同的返回值。
            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 (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);
        }
        
        //如果上面的的判断没有return,会在这里创建一个WindowState,this为当前WMS,
        //session是用来和WMS通信的,client是IWindow对象,
        //IWindow会将WMS操作回调给ViewRootImpl,token是WindowToken。
        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;
        }
        
        //判断窗口的DisplayContent是否还存在
        if (win.getDisplayContent() == null) {
            Slog.w(TAG_WM, "Adding window to Display that has been removed.");
            return WindowManagerGlobal.ADD_INVALID_DISPLAY;
        }
        
        //调用PhoneWindowManager的adjustWindowParamsLw,
        //在方法中会根据窗口的type对LayoutParamas进行一些修改
        //这里传入的win.mAttrs其实就是WindowManager.LayoutParams
        mPolicy.adjustWindowParamsLw(win.mAttrs);
        
        win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
        
        //调用prepareAddWindowLw准备将窗口添加到系统中。如果添加的是应用程序窗口
        //这里不会做额外操作,会回直接返回ADD_OKAY状态
        res = mPolicy.prepareAddWindowLw(win, attrs);
        ...
        win.attach();
        //将WindowState添加到WindowHashMap中,key就是将IWindow转换成Binder
        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);
            }
        }

        final AppWindowToken aToken = token.asAppWindowToken();
        if (type == TYPE_APPLICATION_STARTING && aToken != null) {
            aToken.startingWindow = win;
            if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
                    + " startingWindow=" + win);
        }

        boolean imMayMove = true;
        //将WindowState添加到对应的WindowToken中
        win.mToken.addWindow(win);
        ...
        boolean focusChanged = false;
        if (win.canReceiveKeys()) {
            //更新焦点窗口
            focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                    false /*updateInputWindows*/);
            if (focusChanged) {
                imMayMove = false;
            }
        }
        ...
        //window已经展示,如果方向有变动,更新相应参数
        if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false, displayId)) {
            reportNewConfig = true;
        }

    return res;
}

以上就是添加窗口的过程,东西很多,大家慢慢看。

Window删除过程

window的删除首先会调用WindowManagerGlobal的removeView方法:

public void removeView(View view, boolean immediate) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }

    synchronized (mLock) {
        int index = findViewLocked(view, true);
        View curView = mRoots.get(index).getView();
        removeViewLocked(index, immediate);
        if (curView == view) {
            return;
        }

        throw new IllegalStateException("Calling with view " + view
                + " but the ViewAncestor is attached to " + curView);
    }
}

这里会对view进行一些判断,然后调用removeViewLocked方法:

private void removeViewLocked(int index, boolean immediate) {
    ViewRootImpl root = mRoots.get(index);
    View view = root.getView();

    if (view != null) {
        InputMethodManager imm = InputMethodManager.getInstance();
        if (imm != null) {
            imm.windowDismissed(mViews.get(index).getWindowToken());
        }
    }
    boolean deferred = root.die(immediate);
    if (view != null) {
        view.assignParent(null);
        if (deferred) {
            mDyingViews.add(view);
        }
    }
}

根据传入的index获取ViewRootImpl,获取到对应的View。然后获取到InputMethodManager,并调用windowDismissed结束触摸相关的逻辑。然后调用ViewRootImpl的die方法。

下面来看看die方法:

boolean die(boolean immediate) {
    // Make sure we do execute immediately if we are in the middle of a traversal or the damage
    // done by dispatchDetachedFromWindow will cause havoc on return.
    if (immediate && !mIsInTraversal) {
        doDie();
        return false;
    }

    if (!mIsDrawing) {
        destroyHardwareRenderer();
    } else {
        Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
                "  window=" + this + ", title=" + mWindowAttributes.getTitle());
    }
    mHandler.sendEmptyMessage(MSG_DIE);
    return true;
}

这里我们可以看到,如果immediate为true,并且mIsInTraversal为false,就会马上执行doDie(),并且不会执行下面的代码。mIsInTraversal在执行ViewRootImpl的performTraversals方法的时候设置为true,方法执行完成后设置为false。意思就是如果此时没有执行performTraversals,就可以立即执行doDie方法。

下面来看看doDie方法:

void doDie() {

    //先检查当前线程是否为主线程
    checkThread();
    if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
    synchronized (this) {
    
        //如果已经调用过了该方法,这里会返回,如果没有调用,会将mRemoved设置为true。
        if (mRemoved) {
            return;
        }
        mRemoved = true;
        
        //如果有子View,回调用dispatchDetachedFromWindow方法来销毁View
        if (mAdded) {
            dispatchDetachedFromWindow();
        }

        ...

        mAdded = false;
    }
    //最后调用doRemove方法
    WindowManagerGlobal.getInstance().doRemoveView(this);
}

下面看看doRemoveView方法的代码:

void doRemoveView(ViewRootImpl root) {
    synchronized (mLock) {
        final int index = mRoots.indexOf(root);
        if (index >= 0) {
            mRoots.remove(index);
            mParams.remove(index);
            final View view = mViews.remove(index);
            mDyingViews.remove(view);
        }
    }
    if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
        doTrimForeground();
    }
}

删除View相关的信息

下面我们再来看看doDie方法中的dispatchDetachedFromWindow方法:

void dispatchDetachedFromWindow() {
    ...
    try {
        mWindowSession.remove(mWindow);
    } catch (RemoteException e) {
    }
    ...
}

我们可以看到这里调用了mWindowSession的remove方法。mWindowSession是IWindowSession,IWindowSession在服务端是Session实现的,下面看看Session的remove方法:

mService.removeWindow(this,window);

这里的mService是WMS,我们看看WMS中的removeWindow方法:

void removeWindow(Session session, IWindow client) {
    synchronized(mWindowMap) {
        WindowState win = windowForClientLocked(session, client, false);
        if (win == null) {
            return;
        }
        win.removeIfPossible();
    }
}

现获取WindowState,WindowState用来保存窗口信息,然后调用WindowState的removeIfPossible方法

@Override
void removeIfPossible() {
    super.removeIfPossible();
    removeIfPossible(false /*keepVisibleDeadWindow*/);
}

private void removeIfPossible(boolean keepVisibleDeadWindow) {
    ...
    removeImmediately();
    // Removing a visible window will effect the computed orientation
    // So just update orientation if needed.
    if (wasVisible && mService.updateOrientationFromAppTokensLocked(false, displayId)) {
        mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
    }
    mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
    Binder.restoreCallingIdentity(origId);
}

先进性一些列判断,如果为true就会先延迟删除View。如果没有,这里会执行removeImmediately方法。

@Override
void removeImmediately() {
    super.removeImmediately();
    
    //防止重复调用
    if (mRemoved) {
        // Nothing to do.
        if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                "WS.removeImmediately: " + this + " Already removed...");
        return;
    }

    mRemoved = true;

    mWillReplaceWindow = false;
    if (mReplacementWindow != null) {
        mReplacementWindow.mSkipEnterAnimationForSeamlessReplacement = false;
    }

    final DisplayContent dc = getDisplayContent();
    if (mService.mInputMethodTarget == this) {
        dc.computeImeTarget(true /* updateImeTarget */);
    }

    final int type = mAttrs.type;
    if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
        dc.mTapExcludedWindows.remove(this);
    }
    mPolicy.removeWindowLw(this);

    disposeInputChannel();

    mWinAnimator.destroyDeferredSurfaceLocked();
    mWinAnimator.destroySurfaceLocked();
    mSession.windowRemovedLocked();
    try {
        mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
    } catch (RuntimeException e) {
        // Ignore if it has already been removed (usually because
        // we are doing this as part of processing a death note.)
    }

    mService.postWindowRemoveCleanupLocked(this);
}

该方法是对View相关的一些信息进行处理,最后调用postWindowRemoveCleanupLocked也是方便View清理。到这里大致的删除Window流程就完成了。