Android WMS(二)—— 窗口容器树的构建

469 阅读21分钟

窗口容器树系列文章:

  1. Android WMS(一)—— 窗口和窗口容器树
  2. Android WMS(二)—— 窗口容器树的构建

1. WMS 服务进程启动

1.1 WMS 服务进程启动,创建 RootWindowContainer 实例

SystemServer 进程首先在 startBootstrapServices 中创建初始化了 ATMS、AMS,AMS 构造时传入 ATMS 对象,AMS 持有 ATMS:


private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
    ...
    // 【ATMS启动】ActivityTaskManagerService 负责 Activity 和 Task 的管理
    ActivityTaskManagerService atm = mSystemServiceManager.startService(
            ActivityTaskManagerService.Lifecycle.class).getService();
    // 【AMS启动】ActivityManagerService 是系统的总调度器
    mActivityManagerService = ActivityManagerService.Lifecycle.startService(
            mSystemServiceManager, atm);
    ...
}

SystemServer 进程的 startOtherServices 方法中启动了 WMS 服务:


private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    ...
    // 启动 WMS 服务进程,在启动 AMS 之后
    mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
    wm = WindowManagerService.main(context, inputManager, !mFirstBoot,
            new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
            DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
    // 关注点,向 AMS 添加 WMS 实例
    mActivityManagerService.setWindowManager(wm);
    ...
}

通过静态工厂方法 main() 创建 WMS 实例。传入了 ATMS 对象, WMS 持有 ATMS:

public static WindowManagerService main(final Context context, final InputManagerService im,
        final boolean showBootMsgs, WindowManagerPolicy policy,
        ActivityTaskManagerService atm) {

    return main(context, im, showBootMsgs, policy, atm, new DisplayWindowSettingsProvider(),
            SurfaceControl.Transaction::new, SurfaceControl.Builder::new);
}

@VisibleForTesting
public static WindowManagerService main(final Context context, final InputManagerService im,
        final boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
        DisplayWindowSettingsProvider displayWindowSettingsProvider,
        Supplier<SurfaceControl.Transaction> transactionFactory,
        Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
        
    final WindowManagerService[] wms = new WindowManagerService[1];
    // 使用 DisplayThread.getHandler().runWithScissors 是为了将 WMS 的创建强制切换到专用的 DisplayThread 上同步执行,
    // 因为 WMS 依赖 VSYNC、动画、SurfaceControl 等显示子系统能力,这些逻辑必须在单一显示线程中串行运行以保证线程上下文(Looper/Choreographer/ThreadLocal)一致;
    // 同时 SystemServer 主线程作为系统调度中枢不能承载高频显示任务,否则会引发调度阻塞与锁竞争。
    // 因此通过 runWithScissors 在 DisplayThread 执行并阻塞等待结果,既保证线程归属正确,又保证初始化对系统是同步且确定的。
    DisplayThread.getHandler().runWithScissors(() ->
            wms[0] = new WindowManagerService(context, im, showBootMsgs, policy, atm,
                    displayWindowSettingsProvider, transactionFactory,
                    surfaceControlFactory), 0);
    return wms[0];
}

启动 WMS 服务进程,在 WMS 的构造方法中,创建 RootWindowContainer 实例,RootWindowContainer 是窗口管理树的根节点,并创建了 DisplayAreaPolicy.Provider 对象:

private WindowManagerService(Context context, InputManagerService inputManager,
            boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
            DisplayWindowSettingsProvider displayWindowSettingsProvider,
            Supplier<SurfaceControl.Transaction> transactionFactory,
            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
            
    mRoot = new RootWindowContainer(this);
    // 实际类型是 DisplayAreaPolicy.DefaultProvider
    mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(mContext.getResources());
            
}

DisplayAreaPolicy 定义了 DisplayArea 的层次结构和窗口分组策略。

通过 fromResources 方法从系统资源中读取配置,如果没有自定义配置则使用 DefaultProvider:

static Provider fromResources(Resources res) {
    String name = res.getString(
            com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider);
    if (TextUtils.isEmpty(name)) {
        return new DisplayAreaPolicy.DefaultProvider();
    }
    try {
        return (Provider) Class.forName(name).newInstance();
    } catch (ReflectiveOperationException | ClassCastException e) {
        throw new IllegalStateException("Couldn't instantiate class " + name
                + " for config_deviceSpecificDisplayAreaPolicyProvider:"
                + " make sure it has a public zero-argument constructor"
                + " and implements DisplayAreaPolicy.Provider", e);
    }
}

这里会去读取 config_deviceSpecificDisplayAreaPolicyProvider 这个 String 资源,在源码中没有搜索到这个 String 资源的设置,当该资源为空时,会 new 一个 DisplayAreaPolicy.DefaultProvider 对象返回。所以 mDisplayAreaPolicyProvider 的实际类型是 DisplayAreaPolicy.DefaultProvider.

本阶段几个有意思的问题:

Q1:为什么不使用反射创建?

  1. WMS 的实例化需要大量特定参数。而 SystemServiceManager.startService 的反射机制只支持单一 Context 参数的构造函数。
  2. WMS 的核心组件必须在 DisplayThread 中初始化。

Q2:为什么使用工厂方法创建?

  1. 隐藏了复杂的实例化逻辑和依赖项(如后面调用的重载 main 方法中注入的 provider 和 factory)。
  2. 可以在创建对象前做一些准备工作。

Q3:为什么使用数组保存 WMS 实例?

Lambda 捕获的局部变量必须是 final 或 effectively final。直接声明 WindowManagerService wms 无法在 Lambda 内部赋值。将变量包装在 final 数组中:数组引用不可变,但元素可修改,因此可通过 wms[0] 将 DisplayThread 中创建的实例传递出来。

Q4:为什么通过 DisplayThread.getHandler().runWithScissors 创建 WMS 实例?

  1. 绑定专属线程: WMS 负责核心 UI 与窗口管理,将其绑定在专用的高优先级 DisplayThread 中初始化,既能避免与其他系统服务抢夺 CPU 资源,又能保证其内部复杂数据结构的线程安全。
  2. 跨线程与同步阻塞: runWithScissors 的作用是将实例化 WMS 的任务投递给 DisplayThread 执行,同时强制挂起并阻塞当前的 SystemServer 主线程(参数 0 代表无限期死等,直到执行完毕)。
  3. 保障启动时序强依赖: Android 核心服务启动一环扣一环,后续服务(如 AMS)强依赖已完全初始化完毕的 WMS。强制阻塞主线程是为了防止后续代码跑得太快导致拿到空指针,引发系统崩溃。

Q5:fromResources 方法从系统资源中读取配置的思想?

  1. 支持设备多形态,避免 WMS 核心代码被污染。解耦默认的 DefaultProvider 只针对最基础的单屏幕手机形态。在里面,所有的应用窗口都被扔进唯一的 DefaultTaskDisplayArea 中。但现在的 Android 需要运行在折叠屏、车机屏幕、智能电视甚至双屏设备(如 Surface Duo)上。
  2. 利用 Android 的资源覆盖机制 (RRO) 实现“运行时插拔”,无侵入式定制扩展。在 Provider 的 fromResources 方法中,系统会读取一个配置:config_deviceSpecificDisplayAreaPolicyProvider。这种设计意味着:厂商不需要修改 Android 的核心 Java 源码。他们只需要在自己设备的 res/values/config.xml 文件中,将这个字符串替换为自己实现的类的全限定名(例如 com.android.car.CarDisplayAreaPolicyProvider)。

1.2 WMS 服务进程启动,AMS、ATMS、WMS、RootWindowContainer 之间建立联系

接着调用了 AMS 的 setWindowManager 方法向 AMS 添加 WMS 实例:

public void setWindowManager(WindowManagerService wm) {
    synchronized (this) {
        mWindowManager = wm;
        mWmInternal = LocalServices.getService(WindowManagerInternal.class);
        // 向 ATMS 添加 WMS 实例
        mActivityTaskManager.setWindowManager(wm);
    }
}

向 ATMS 添加 WMS 实例:

// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
public void setWindowManager(com.android.server.wm.WindowManagerService wm) {
    synchronized (mGlobalLock) {
        mWindowManager = wm;
        // ATMS 持有了 RootWindowContainer 实例
        mRootWindowContainer = wm.mRoot;
        mWindowOrganizerController.mTransitionController.setWindowManager(wm);
        mLifecycleManager.setWindowManager(wm);
        mTempConfig.setToDefaults();
        mTempConfig.setLocales(LocaleList.getDefault());
        mConfigurationSeq = mTempConfig.seq = 1;
        mRootWindowContainer.onConfigurationChanged(mTempConfig);
        mLockTaskController.setWindowManager(wm);
        mTaskSupervisor.setWindowManager(wm);
        // 从 mRootWindowContainer.setWindowManager(wm) 开始,就开始了窗口容器树的构建过程
        mRootWindowContainer.setWindowManager(wm);
        mBackNavigationController.setWindowManager(wm);
    }
}

将 WMS 持有的 RootWindowContainer 实例赋值给了 ATMS 的 mRootWindowContainer。接着就调用了 mRootWindowContainer 的 setWindowManager 方法,此时就开始了窗口容器树的构建过程。

2. RootWindowContainer 构建窗口容器树

2.1 RootWindowContainer setWindowManager 开始构建窗口容器树

一个 Android 系统中只存在一个 RootWindowContainer,它作为全局窗口树的根,统一管理所有 DisplayContent。

看一下 RootWindowContainer 的 setWindowManager 方法:

void setWindowManager(WindowManagerService wm) {
    // 将 WMS 实例传递给 RootWindowContainer
    mWindowManager = wm;
    mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
    mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
    mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);

    // 获取屏幕个数
    final Display[] displays = mDisplayManager.getDisplays();
    // 遍历每一个 Display,为每一个 Display 构建一个 DisplayContent。
    for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
        final Display display = displays[displayNdx];
        // 为每一个 Display 创建一个 DisplayContent 对象。在窗口容器树中,DisplayContent 就代表了一个屏幕。
        final DisplayContent displayContent = new DisplayContent(display, this, mDeviceStateController);
        addChild(displayContent, POSITION_BOTTOM);
        // DEFAULT_DISPLAY 0 默认的主屏
        if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
            mDefaultDisplay = displayContent;
        }
    }

    final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
    defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
    positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
            false /* includingParents */);
}

通过 DisplayService 获取到了当前设备所有屏幕,接着遍历每一个 Display(屏幕),为每一个 Display 构建一个 DisplayContent。在窗口容器树中,DisplayContent 就代表了一个屏幕。

2.2 DisplayContent 初始化

看一下 DisplayContent 初始化:

DisplayContent(Display display, RootWindowContainer root,
            @NonNull DeviceStateController deviceStateController) {
    super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
    ...
    // 创建事务 
    final Transaction pendingTransaction = getPendingTransaction();
    // 开始构建层级树 
    configureSurfaces(pendingTransaction);
    // 执行事务 
    pendingTransaction.apply();
    ...
}

看一下 DisplayContent 的 configureSurfaces 方法:

private void configureSurfaces(Transaction transaction) {
    // 构建一个 SurfaceControl,表示 SurfaceFlinger 中的一个图层节点
    final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
            .setOpaque(true)
            .setContainerLayer()
            .setCallsite("DisplayContent");
    // 设置名字后构建 (Display 0 name="XXX")Display 0 name="Built-in Screen"
    mSurfaceControl = b.setName(getName()).setContainerLayer().build();

    if (mDisplayAreaPolicy == null) {
        // Setup the policy and build the display area hierarchy.
        // Build the hierarchy only after creating the surface so it is reparented correctly
        // 关注点,构建树结构。返回一个 DisplayAreaPolicy.Provider 对象
        // 构建 DisplayArea 树结构,为不同窗口类型(如状态栏、对话框)分配层级。
        mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
                mWmService, this /* content */, this /* root */,
                mImeWindowsContainer);
    }

    final List<DisplayArea<? extends WindowContainer>> areas =
            mDisplayAreaPolicy.getDisplayAreas(FEATURE_WINDOWED_MAGNIFICATION);
    final DisplayArea<?> area = areas.size() == 1 ? areas.get(0) : null;

    if (area != null && area.getParent() == this) {
        // The windowed magnification area should contain all non-overlay windows, so just use
        // it as the windowing layer.
        mWindowingLayer = area.mSurfaceControl;
        transaction.reparent(mWindowingLayer, mSurfaceControl);
    } else {
        // Need an additional layer for screen level animation, so move the layer containing
        // the windows to the new root.
        mWindowingLayer = mSurfaceControl;
        mSurfaceControl = b.setName("RootWrapper").build();
        transaction.reparent(mWindowingLayer, mSurfaceControl)
                .show(mWindowingLayer);
    }

    if (mOverlayLayer == null) {
        mOverlayLayer = b.setName("Display Overlays").setParent(mSurfaceControl).build();
    } else {
        transaction.reparent(mOverlayLayer, mSurfaceControl);
    }

    if (mInputOverlayLayer == null) {
        mInputOverlayLayer = b.setName("Input Overlays").setParent(mSurfaceControl).build();
    } else {
        transaction.reparent(mInputOverlayLayer, mSurfaceControl);
    }

    if (mA11yOverlayLayer == null) {
        mA11yOverlayLayer =
                b.setName("Accessibility Overlays").setParent(mSurfaceControl).build();
    } else {
        transaction.reparent(mA11yOverlayLayer, mSurfaceControl);
    }

    // 事务相关设置
    transaction
            .setLayer(mSurfaceControl, 0)
            .setLayerStack(mSurfaceControl, mDisplayId)
            .show(mSurfaceControl)
            .setLayer(mOverlayLayer, Integer.MAX_VALUE)
            .show(mOverlayLayer)
            .setLayer(mInputOverlayLayer, Integer.MAX_VALUE - 1)
            .show(mInputOverlayLayer)
            .setLayer(mA11yOverlayLayer, Integer.MAX_VALUE - 2)
            .show(mA11yOverlayLayer);
}

首先创建 DisplayContent 的根 SurfaceControl。SurfaceControl 是系统合成器 (SurfaceFlinger) 管理的屏幕上 Surface 的句柄。SurfaceControl 用于创建、配置和管理 Surface 的显示属性(如大小、位置、透明度、层级等)。

DisplayAreaPolicy 类负责建立和管理 DisplayArea 的层级结构,定义显示区域的划分策略(如系统区、应用区)。

2.3 构建 HierarchyBuilder,配置 Feature

这里直接调用 DefaultProvider 的 instantiate 进行构建 DisplayArea 树结构,为不同窗口类型(如状态栏、对话框)分配层级:

// 默认显式策略
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
                                     DisplayContent content, RootDisplayArea root,
                                     DisplayArea.Tokens imeContainer) {
    // 创建一个名为 "DefaultTaskDisplayArea" 的对象作为应用窗口的默认容器,应用窗口容器
    final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
            "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
    final List<com.android.server.wm.TaskDisplayArea> tdaList = new ArrayList<>();
    tdaList.add(defaultTaskDisplayArea);

    // Define the features that will be supported under the root of the whole logical
    // display. The policy will build the DisplayArea hierarchy based on this.
    // 传递 RootDisplayArea(DisplayContent)构建出一个层级树的数据结构,用于后续窗口容器树的构建。
    final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);

    // Set the essential containers (even if the display doesn't support IME).
    // HierarchyBuilder 设置输入法容器和应用窗口容器
    rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
    if (content.isTrusted()) {
        // Only trusted display can have system decorations.
        // 配置层级的支持的 Feature
        configureTrustedHierarchyBuilder(rootHierarchy, wmService, content);
    }

    // Instantiate the policy with the hierarchy defined above. This will create and attach
    // all the necessary DisplayAreas to the root.
    // 开始构建窗口容器树
    return new com.android.server.wm.DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);
}

创建一个名为 DefaultTaskDisplayArea 的对象作为应用窗口的默认容器。TaskDisplayArea 代表了屏幕上一块专门用来存放 App 窗口的区域。使用列表而非单个对象,是为了支持多 TDA 场景(如分屏模式下的多个 TDA)。

构建出 DisplayContent 的层级树的数据结构 HierarchyBuilder,HierarchyBuilder 是层级树的"蓝图",用于后续窗口容器树的构建。

先设置 IME 容器,再设置 TDA 列表。

接着通过 configureTrustedHierarchyBuilder 配置层级的支持的 Feature:

/**
 * Feature.mName	                     Feature.mID	                        Feature.mWindowLayers 数组为 true 的区间
 * WindowedMagnification	    FEATURE_WINDOWED_MAGNIFICATION	                            [0,31]
 * HideDisplayCutout	        FEATURE_HIDE_DISPLAY_CUTOUT	                                [0,14], 16, [18,23], [26,35]
 * OneHanded	                FEATURE_ONE_HANDED	                                        [0,23], [26,32],[34,35]
 * FullscreenMagnification  	FEATURE_FULLSCREEN_MAGNIFICATION	                        [0,12], [15,23], [26,27], [29,31], [33,35]
 * ImePlaceholder           	FEATURE_IME_PLACEHOLDER	                                    [13,14]
 */
private void configureTrustedHierarchyBuilder(HierarchyBuilder rootHierarchy,
        WindowManagerService wmService, DisplayContent content) {
    // WindowedMagnification should be on the top so that there is only one surface
    // to be magnified.
    // 通过 Feature.Builder 构造一个 Feature 对象
    // 再调用 HierarchyBuilder 对象的 addFeature 方法,将构造好的 Feature 对象保存到 HierarchyBuilder 对象内部的
    // ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures 成员中
    // 构造好了名为 WindowedMagnification 的 Feature 对象
    rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",
            FEATURE_WINDOWED_MAGNIFICATION)
            .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)  // 0-32 true
            .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)// 32 false
            .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)// new 一个 DisplayArea.Dimmable 保存在 Builder 的 mNewDisplayAreaSupplier 成员中
            .build()); // 最后调用 build 方法,new 一个 Feature 返回,前面准备好的数据都作为参数传入构造方法,然后保存到 Feature 中
    if (content.isDefaultDisplay) {
        // Only default display can have cutout.
        // See LocalDisplayAdapter.LocalDisplayDevice#getDisplayDeviceInfoLocked.
        rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "HideDisplayCutout",
                FEATURE_HIDE_DISPLAY_CUTOUT)
                .all()
                .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR,
                        TYPE_NOTIFICATION_SHADE)
                .build())
                .addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
                        FEATURE_ONE_HANDED)
                        .all()
                        .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL,
                                TYPE_SECURE_SYSTEM_OVERLAY)
                        .build());
    }
    rootHierarchy
            .addFeature(new Feature.Builder(wmService.mPolicy, "FullscreenMagnification",
                    FEATURE_FULLSCREEN_MAGNIFICATION)
                    .all()
                    .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, TYPE_INPUT_METHOD,
                            TYPE_INPUT_METHOD_DIALOG, TYPE_MAGNIFICATION_OVERLAY,
                            TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
                    .build())
            .addFeature(new Feature.Builder(wmService.mPolicy, "ImePlaceholder",
                    FEATURE_IME_PLACEHOLDER)
                    .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
                    .build());
}

DisplayArea 节点会有自己的 Feature,来指定节点所包含的特殊功能。配置层级的支持的 Feature。一共有 5 个 Feature :

  • WindowedMagnification
  • HideDisplayCutout
  • OneHanded
  • FullscreenMagnification
  • ImePlaceholder

setRootHierarchy() 将 HierarchyBuilder 保存到 DisplayAreaPolicyBuilder。

2.4 构建 PendingArea 树

继续调用 DisplayAreaPolicyBuilder 的 build 方法:

// 构建 PendingArea 树
// 根据 PendingArea 树构建最终的窗口容器树
Result build(WindowManagerService wmService) {
    validate();
    mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
    List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
            mDisplayAreaGroupHierarchyBuilders.size());
    // mDisplayAreaGroupHierarchyBuilders 是一个空列表
    for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
        HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
        hierarchyBuilder.build();
        displayAreaGroupRoots.add(hierarchyBuilder.mRoot);
    }
    // Use the default function if it is not specified otherwise.
    if (mSelectRootForWindowFunc == null) {
        mSelectRootForWindowFunc = new DefaultSelectRootForWindowFunction(
                mRootHierarchyBuilder.mRoot, displayAreaGroupRoots);
    }
    return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
            mSelectRootForWindowFunc, mSelectTaskDisplayAreaFunc);
}

继续看一下 HierarchyBuilder 的 build 方法,这里主要通过两个循环构建了 PendingArea 树,完成之后再使用 PendingArea 树构建出目标的窗口容器树。build 方法较长,分几个阶段来看。首先准备构建所需的数据结构:

private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
    // 初始化局部变量
    final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;
    // 定义最大层级数  37
    final int maxWindowLayerCount = policy.getMaxWindowLayer() + 1;
    // 存储每个窗口层级对应的 DisplayArea.Tokens 37 个
    final DisplayArea.Tokens[] displayAreaForLayer =
            new DisplayArea.Tokens[maxWindowLayerCount];
    // 存储每个 Feature 对应的 DisplayArea 列表
    final Map<Feature, List<DisplayArea<WindowContainer>>> featureAreas =
            new ArrayMap<>(mFeatures.size());
    for (int i = 0; i < mFeatures.size(); i++) {
        featureAreas.put(mFeatures.get(i), new ArrayList<>());
    }

    // 初始化 PendingArea 数组,用于临时存储每个窗口层级对应的 PendingArea,一共 37 个
    PendingArea[] areaForLayer = new PendingArea[maxWindowLayerCount];
    final PendingArea root = new PendingArea(null, 0, null);
    // areaForLayer 数组全部填充为 root
    Arrays.fill(areaForLayer, root);
    ...
}

displayAreaForLayer 保存每一个 Window Layer 对应的 DisplayArea.Tokens 对象,避免后续创建 WindowToken 时,每次都重新遍历 DisplayArea 层级树去寻找对应的 DisplayArea.Tokens。

featureAreas 存储每个 Feature 对应的 DisplayArea 列表。

areaForLayer 记录的不是最终的 DisplayArea,也不是窗口。它记录的是:当前 layer 在 PendingArea 构建阶段,应该挂载到哪个“最近的父 PendingArea 节点”。

接着创建 PendingArea 树的根节点 root,不关联任何 Feature。初始将所有层级都指向根节点,表示初始时所有窗口都附加到根节点。

随后的 Feature 遍历会将这些连接“打断”并插入新的中间节点,从而形成树状结构。所有定义的 Feature 创建对应的 DisplayArea:

private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
    ...
    final int size = mFeatures.size();
    for (int i = 0; i < size; i++) {
        final Feature feature = mFeatures.get(i);
        PendingArea featureArea = null;
        // 【层级遍历】遍历所有窗口层级(0-36),检查当前 Feature 是否适用于每个层级。
        for (int layer = 0; layer < maxWindowLayerCount; layer++) {
            if (feature.mWindowLayers[layer]) { 
                if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
                    // 参数:1.关联Feature; 2.最小层级; 3.父PendingArea
                    featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
                    // 将新创建的 PendingArea 添加到父节点的子节点列表中
                    areaForLayer[layer].mChildren.add(featureArea);
                }

                // 更新当前 layer 的插入位置
                // 下一个 Feature 遍历时,就会基于这个新的 featureArea 继续构建子节点。
                areaForLayer[layer] = featureArea;
            } else {
                // 【Feature 不适用】当前 Feature 不适用于此层级
                // 如果下一层级需要应用此 Feature,将需要创建新的 DisplayArea
                // 重置 featureArea 为 null,确保下次创建时不会错误重用
                featureArea = null;
            }
        }
    }
    ...
}

mFeatures 按定义顺序遍历,越早定义的 Feature 越靠近根节点,后定义的 Feature 作为子节点插入。Feature 顺序不是排列问题,而是决定变换执行层级。

featureArea 变量起到了“缓存”作用,用于判断当前层级是否可以延续上一个层级的容器。重用条件:

  1. 现有 DisplayArea 是为当前 Feature 在前一层级创建的。
  2. 应用于前一层级的最后一个 Feature 与应用于当前层级的 Feature 相同。

以下情况必须重新创建:

  1. featureArea 为空(说明是该 Feature 的第一个适用层级)。
  2. 或者 featureArea 的父节点 != 当前层级原本指向的节点(说明父容器变了,不能单纯延续)。

新建之后需要将新创建的 PendingArea 添加到父节点的子节点列表中。

至此,所有的 Feature 节点(中间节点)都已创建完毕,此时窗口容器树如下: image.png

接下来需要挂载真正的“叶子”容器,即 DisplayArea.Tokens,用于存放 WindowToken:

private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
    ...
    PendingArea leafArea = null;
    int leafType = LEAF_TYPE_TOKENS; // 默认当前叶子类型为 TOKENS

    for (int layer = 0; layer < maxWindowLayerCount; layer++) {
        // 【层级类型判断】根据窗口层级确定叶子节点类型
        // LEAF_TYPE_TOKENS(0): 默认类型,用于大多数窗口层级
        // LEAF_TYPE_TASK_CONTAINERS(1): 应用层级,用于承载 Task 和 Activity
        // LEAF_TYPE_IME_CONTAINERS(2): 输入法层级,用于承载输入法窗口
        int type = typeOfLayer(policy, layer);

        // 叶子节点重用检查:若当前层级与上一层级类型相同且父节点一致(同一 Feature 组),
        // 则复用已有 LeafArea,避免为每个层级都创建新的 DisplayArea 对象。
        // 以下三种情况需要重新创建:
        //   1. leafArea == null:首个层级,无历史节点可复用
        //   2. leafArea.mParent != areaForLayer[layer]:父节点不同,分属不同 Feature 组
        //   3. type != leafType:层级类型不同(如从系统窗口层切换到应用窗口层)
        if (leafArea == null || leafArea.mParent != areaForLayer[layer]
                || type != leafType) {
            // Create a new Tokens for this layer.
            // 参数:1.叶子节点Feature为null; 2.最小层级; 3.父PendingArea
            leafArea = new PendingArea(null /* feature */, layer, areaForLayer[layer]);
            // 将新创建的 PendingArea 添加到父节点的子节点列表中
            areaForLayer[layer].mChildren.add(leafArea);
            leafType = type;// 更新
            if (leafType == LEAF_TYPE_TASK_CONTAINERS) {// 如果叶子节点类型是 LEAF_TYPE_TASK_CONTAINERS,也就是 layer 等于 2,则做以下处理:
                // We use the passed in TaskDisplayAreas for task container type of layer.
                // Skip creating Tokens even if there is no TDA.
                // 【深度解析:为什么 TaskDisplayArea 要新建额外的 PendingArea,导致 layer=2 出现多个叶子节点?】
                // 1. Android 的设计中,Layer 2 (APPLICATION_LAYER) 是一个非常特殊的层级。
                //    普通的 layer (比如 layer 15 的状态栏) 只需要一个单一的 `DisplayArea.Tokens` 容器来装窗口。
                //    但是 Layer 2 承担了所有的 App 窗口,它必须支持【分屏模式】和【多窗口模式】。
                // 2. 为了支持分屏,WMS 允许开发者在这个层级创建多个 `TaskDisplayArea`(比如左边一个 TDA,右边一个 TDA)。
                // 3. 因此,Layer 2 的初始 leafArea (普通的 DisplayArea.Tokens) 被 `mSkipTokens = true` 废弃掉。
                //    随后调用 `addTaskDisplayAreasToApplicationLayer`,根据 `mTaskDisplayAreas` 列表的长度(可能是1个主屏,也可能是多个分屏),
                //    为每一个 TDA 创建专属的子 `PendingArea` 并挂载。
                // 这就是为什么 Layer 2 看起来会新建额外的叶子节点——因为它可以是一对多的关系。
                addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);
                // 【功能】添加 DisplayAreaGroup 到应用层(通常为空列表)
                // displayAreaGroupHierarchyBuilders是一个空的列表,这个方法就不用管了
                addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],
                        displayAreaGroupHierarchyBuilders);
                // 【优化策略】跳过 Tokens 创建,因为已有专门的 TaskDisplayArea
                // 接着将叶子节点 leafArea 的 mSkipTokens 设置为 true,那么后续在根据PendingArea 树生成 DisplayArea 层级结构的时候,就不会为这个 PendingArea 对象生成一个 DisplayArea 对象了。
                // 【系统架构】应用层(Layer 2) 是特殊的,它不直接使用 DisplayArea.Tokens,
                // 而是使用功能更强大的 TaskDisplayArea。因此这里标记 skipTokens。
                leafArea.mSkipTokens = true;
            } else if (leafType == LEAF_TYPE_IME_CONTAINERS) {// 如果叶子节点类型是 LEAF_TYPE_IME_CONTAINERS,也就是 layer 等于 13 或者 14
                // We use the passed in ImeContainer for ime container type of layer.
                // Skip creating Tokens even if there is no ime container.
                // 【深度解析:为什么 ImeContainer 不新建额外的叶子节点,而是直接复用当前的 leafArea?】
                // 1. 唯一性:不管是在什么复杂的场景下(甚至分屏),一个 Display (屏幕) 上永远【只会有一个】输入法容器 (ImeContainer)。
                // 2. 因为它是一对一的关系,所以不需要像 TDA 那样遍历列表去新建多个子 PendingArea。
                // 3. 因此,代码直接将提前准备好的 `mImeContainer` 塞进了当前这个 `leafArea` 的 `mExisting` 属性中。
                //    并在实例化阶段(instantiateChildren)发现 `mExisting` 有值时,直接返回这个预先创建好的输入法容器,
                //    避免了再去 new 一次普通的 `DisplayArea.Tokens`。
                leafArea.mExisting = mImeContainer;
                leafArea.mSkipTokens = true;
            }
        }
        // 【层级管理】设置叶子节点的最大层级
        // 【底层原理】确保每个 DisplayArea 知道自己管理的窗口层级范围
        leafArea.mMaxLayer = layer;
    }
    ...
}

首先通过typeOfLayer获取叶子节点类型:

private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
    // 层级类型映射,将数字层级 (int layer) 映射到 语义类型 (Type)
    // 方便系统对 Application、IME 和 System 窗口进行不同的策略处理
    if (layer == APPLICATION_LAYER) {// layer 等于 2,叶子节点类型是 LEAF_TYPE_TASK_CONTAINERS,用于表示应用窗口容器
        return LEAF_TYPE_TASK_CONTAINERS;
    } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
            || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {// layer 等于 13 14,叶子节点类型是 LEAF_TYPE_IME_CONTAINERS,用于表示输入法窗口
        return LEAF_TYPE_IME_CONTAINERS;
    } else {// layer 不等于 2,13,14,叶子节点类型是 LEAF_TYPE_TOKENS,用于表示系统窗口
        return LEAF_TYPE_TOKENS;
    }
}

以下三种情况需要重新创建 leafArea:

  1. leafArea == null:首个层级,无历史节点可复用
  2. leafArea.mParent != areaForLayer[layer]:父节点不同,分属不同 Feature 组
  3. type != leafType:层级类型不同(如从系统窗口层切换到应用窗口层)

同样,创建之后将新创建的 PendingArea 添加到父节点的子节点列表中。

当叶子节点类型对应应用容器时,通过addTaskDisplayAreasToApplicationLayer 新建额外的 PendingArea:

private void addTaskDisplayAreasToApplicationLayer(PendingArea parentPendingArea) {
    // 将所有 TaskDisplayArea (TDA) 挂载到应用层节点下
    // 此时默认的 mTaskDisplayAreas 中只有一个元素,即名为 DefaultTaskDisplayArea 的 TaskDisplayArea 对象,这里是为该对象创建了一个对应的 PendingArea 对象
    final int count = mTaskDisplayAreas.size();
    // 并且将创建的 PendingArea 添加到 areaForLayer[2] 节点之下,然后将 PendingArea.mExisting 设置为 DefaultTaskDisplayArea
    for (int i = 0; i < count; i++) {
        // 【创建应用节点】APPLICATION_LAYER = 2
        // 这里创建的 PendingArea 是为了容纳 TDA
        PendingArea leafArea =
                new PendingArea(null /* feature */, APPLICATION_LAYER, parentPendingArea);
        // 【复用现有对象】关键点:这里直接使用了已经存在的 mTaskDisplayAreas 对象
        // 而不是让系统再去 new 一个新的 DisplayArea。
        // 后续根据 PendingArea 生成 DisplayArea.Tokens 的时候,如果 mExisting 不为空,那么直接用 mExisting,而不会再重新创建一个 DisplayArea.Tokens 对象。
        leafArea.mExisting = mTaskDisplayAreas.get(i);
        leafArea.mMaxLayer = APPLICATION_LAYER;
        parentPendingArea.mChildren.add(leafArea);
    }
}

将原有的叶子节点 leafArea 的 mSkipTokens 设置为 true,那么后续在根据 PendingArea 树生成 DisplayArea 层级结构的时候,就不会为这个 PendingArea 对象生成一个 DisplayArea 对象了。

当叶子节点类型对应 IME 容器时,将 leafArea.mExisting 直接设置为 mImeContainer。

此时,叶子节点挂载完毕,窗口容器树如下:

image.png
private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
    ...
    // 计算非叶子节点的 mMaxLayer
    // 通过递归计算确保每个 DisplayArea 知道自己管理的层级范围。
    root.computeMaxLayer();

    // We built a tree of PendingAreas above with all the necessary info to represent the
    // hierarchy, now create and attach real DisplayAreas to the root.
    // 【第四阶段:实例化 DisplayArea 树】基于 PendingArea 树生成真正的 DisplayArea 对象
    // 【底层原理】将临时的 PendingArea 结构转换为实际的窗口容器层级
    // 【面试高频考点】问:PendingArea 和 DisplayArea 的区别是什么?为什么需要两阶段构建?
    // 答:PendingArea 是构建时的逻辑中间节点,DisplayArea 是运行时的物理节点。
    // 两阶段构建解耦了层级计算逻辑和对象创建逻辑,使得复杂的层级重用和 Feature 插入算法更容易实现。
    // 逻辑树推演好以后,就会调用下面的方法,基于 PendingArea 树生成窗口容器树 DisplayAreas
    root.instantiateChildren(mRoot, displayAreaForLayer, 0, featureAreas);

    // Notify the root that we have finished attaching all the DisplayAreas. Cache all the
    // feature related collections there for fast access.
    // 【完成构建】通知根节点完成所有 DisplayArea 的附加,缓存特性相关集合以便快速访问
    // 【底层原理】构建完成后需要建立快速查找索引,提高运行时性能
    // 系统需要频繁查询 Feature 对应的 DisplayArea(例如开启单手模式时),缓存这些映射至关重要。
    mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);
    ...
}

在此之前,非叶子节点的 mMaxLayer 都是 0,这里调用 computerMaxLayer 方法计算非叶子节点的 mMaxLayer:

int computeMaxLayer() {
    for (int i = 0; i < mChildren.size(); i++) {
        mMaxLayer = Math.max(mMaxLayer, mChildren.get(i).computeMaxLayer());
    }
    return mMaxLayer;
}

计算之后,窗口容器树如下:

image.png

接着通过 instantiateChildren,基于 PendingArea 树生成真正的 DisplayArea 树。

2.5 基于 PendingArea 树,生成窗口容器树

void instantiateChildren(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer,
        int level, Map<Feature, List<DisplayArea<WindowContainer>>> areas) {
    // 1. 子区域按照它们的最小层级进行升序排列
    mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
    // 2. 遍历孩子将 PendingArea 转换成 DisplayArea
    for (int i = 0; i < mChildren.size(); i++) {
        final PendingArea child = mChildren.get(i);
        // 将该节点转成对应的 DisplayArea
        final DisplayArea area = child.createArea(parent, areaForLayer);
        if (area == null) {
            // TaskDisplayArea and ImeContainer can be set at different hierarchy, so it can
            // be null.
            continue;
        }
        // 将返回的 area 设置为孩子,第一次执行的时候 root 就是 DisplayContent
        parent.addChild(area, WindowContainer.POSITION_TOP);
        if (child.mFeature != null) {
            // 让 Feature 对应的容器里添加创建的 DisplayArea
            areas.get(child.mFeature).add(area);
        }
        // 开始迭代构建
        child.instantiateChildren(area, areaForLayer, level + 1, areas);
    }
}

遍历每一个孩子节点,调用其 PendingArea 的 createArea 方法,创建相应的 DisplayArea 对象:

private DisplayArea createArea(DisplayArea<DisplayArea> parent,
        DisplayArea.Tokens[] areaForLayer) {
    if (mExisting != null) {
        // asTokens,方法定义在 DisplayArea 中默认返回 null,只有 DisplayArea.Tokens 返回本身。 而 ImeContainer 是继承 DisplayArea.Tokens 的,所以有返回值。
        if (mExisting.asTokens() != null) {
            // Store the WindowToken container for layers
            // 只有输入法满足
            fillAreaForLayers(mExisting.asTokens(), areaForLayer);
        }
        return mExisting;
    }
    // mSkipTokens 为 true,直接 return,mSkipTokens 是和 mExisting 是同一个逻辑下设置的。
    if (mSkipTokens) {
        return null;
    }
    // 定义 DisplayArea 的类型
    DisplayArea.Type type;
    if (mMinLayer > APPLICATION_LAYER) {
        type = DisplayArea.Type.ABOVE_TASKS;
    } else if (mMaxLayer < APPLICATION_LAYER) {
        type = DisplayArea.Type.BELOW_TASKS;
    } else {
        type = DisplayArea.Type.ANY;
    }
    // DisplayArea 的类型
    if (mFeature == null) {// leaf 的 mFeature null
        // 构建返回的 Leaf
        final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type,
                "Leaf:" + mMinLayer + ":" + mMaxLayer);
        fillAreaForLayers(leaf, areaForLayer);// 给对应覆盖的层级都需要赋值
        return leaf;
    } else {// 创建 DisplayArea 对象。如果 mFeature不为 null,那么创建一个 DisplayArea 对象,注意这里构造函数 name 参数传入的是 mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer,包含了 Feature 名,纵向层级范围信息。
        return mFeature.mNewDisplayAreaSupplier.create(parent.mWmService, type,
                mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer, mFeature.mId);
    }
}

当 mExisting 不为 null,前面分析的 TaskDisplayArea 和 ImeContainer 的情况,此时不需要再创建 DisplayArea 对象,直接用 mExisting。

根据窗口层级范围确定 DisplayArea 的类型:

  1. ABOVE_TASKS: 层级 > APPLICATION_LAYER(2),包含系统窗口(状态栏、导航栏、输入法等)这些窗口在 Z 轴上位于应用窗口之上。
  2. BELOW_TASKS: 层级 < APPLICATION_LAYER(2),包含壁纸等底层窗口这些窗口在 Z 轴上位于应用窗口之下。
  3. ANY: 跨越 APPLICATION_LAYER(2),可以包含任意类型的窗口Feature 节点通常属于此类型,因为它们覆盖多个层级范围。

最终生成的窗口容器树如下:

image.png